/* the ultimate(?) socat resolver function node: the address to be resolved; supported forms: 1.2.3.4 (IPv4 address) [::2] (IPv6 address) hostname (hostname resolving to IPv4 or IPv6 address) hostname.domain (fq hostname resolving to IPv4 or IPv6 address) service: the port specification; may be numeric or symbolic family: PF_INET, PF_INET6, or PF_UNSPEC permitting both socktype: SOCK_STREAM, SOCK_DGRAM protocol: IPPROTO_UDP, IPPROTO_TCP sau: an uninitialized storage for the resulting socket address returns: STAT_OK, STAT_RETRYLATER */ int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, union sockaddr_union *sau, socklen_t *socklen, unsigned long res_opts0, unsigned long res_opts1) { int port = -1; /* port number in network byte order */ char *numnode = NULL; size_t nodelen; unsigned long save_res_opts = 0; #if HAVE_GETADDRINFO struct addrinfo hints = {0}; struct addrinfo *res = NULL; #else /* HAVE_PROTOTYPE_LIB_getipnodebyname || nothing */ struct hostent *host; #endif int error_num; #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { if (!(_res.options & RES_INIT)) { Res_init(); /*!!! returns -1 on error */ } save_res_opts = _res.options; _res.options &= ~res_opts0; _res.options |= res_opts1; Debug2("changed _res.options from 0x%lx to 0x%lx", save_res_opts, _res.options); } #endif /* HAVE_RESOLV_H */ memset(sau, 0, *socklen); sau->soa.sa_family = family; /* if service is numeric we don't want to have a lookup (might take long with NIS), so we handle this specially */ if (service && isdigit(service[0]&0xff)) { char *extra; port = htons(strtoul(service, &extra, 0)); if (*extra != '\0') { Warn2("xiogetaddrinfo(, \"%s\", ...): extra trailing data \"%s\"", service, extra); } service = NULL; } /* the resolver functions might handle numeric forms of node names by reverse lookup, that's not what we want. So we detect these and handle them specially */ if (node && isdigit(node[0]&0xff)) { #if HAVE_GETADDRINFO hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ if (family == PF_UNSPEC) { family = PF_INET; #if HAVE_GETADDRINFO } else if (family == PF_INET6) { /* map "explicitely" into IPv6 address space; getipnodebyname() does this with AI_V4MAPPED, but not getaddrinfo() */ if ((numnode = Malloc(strlen(node)+7+1)) == NULL) { #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_NORETRY; } sprintf(numnode, "::ffff:%s", node); node = numnode; hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ } #if WITH_IP6 } else if (node && node[0] == '[' && node[(nodelen=strlen(node))-1]==']') { if ((numnode = Malloc(nodelen-1)) == NULL) { #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_NORETRY; } strncpy(numnode, node+1, nodelen-2); /* ok */ numnode[nodelen-2] = '\0'; node = numnode; #if HAVE_GETADDRINFO hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ if (family == PF_UNSPEC) family = PF_INET6; #endif /* WITH_IP6 */ } #if HAVE_GETADDRINFO if (node != NULL || service != NULL) { struct addrinfo *record; if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) { /* actual socket type value is not supported - fallback to a good one */ socktype = SOCK_DGRAM; } if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) { /* actual protocol value is not supported - fallback to a good one */ if (socktype == SOCK_DGRAM) { protocol = IPPROTO_UDP; } else { protocol = IPPROTO_TCP; } } hints.ai_flags |= AI_PASSIVE; hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_protocol = protocol; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; if ((error_num = Getaddrinfo(node, service, &hints, &res)) != 0) { Error7("getaddrinfo(\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s", node, service, hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, (error_num == EAI_SYSTEM)? strerror(errno):gai_strerror(error_num)); if (res != NULL) freeaddrinfo(res); if (numnode) free(numnode); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_RETRYLATER; } service = NULL; /* do not resolve later again */ record = res; if (family == PF_UNSPEC && xioopts.preferred_ip == '0') { /* we just take the first result */ family = res[0].ai_addr->sa_family; } if (family == PF_UNSPEC) { int trypf; trypf = (xioopts.preferred_ip=='6'?PF_INET6:PF_INET); /* we must look for a matching entry */ while (record != NULL) { if (record->ai_family == trypf) { family = trypf; break; /* family and record set accordingly */ } record = record->ai_next; } if (record == NULL) { /* we did not find a "preferred" entry, take the first */ record = res; family = res[0].ai_addr->sa_family; } } switch (family) { #if WITH_IP4 case PF_INET: if (*socklen > record->ai_addrlen) { *socklen = record->ai_addrlen; } memcpy(&sau->ip4, record->ai_addr, *socklen); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: #if _AIX /* older AIX versions pass wrong length, so we correct it */ record->ai_addr->sa_len = sizeof(struct sockaddr_in6); #endif if (*socklen > record->ai_addrlen) { *socklen = record->ai_addrlen; } memcpy(&sau->ip6, record->ai_addr, *socklen); break; #endif /* WITH_IP6 */ default: Error1("address resolved to unknown protocol family %d", record->ai_addr->sa_family); break; } freeaddrinfo(res); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); break; #endif /* WITH_IP6 */ } } #elif HAVE_PROTOTYPE_LIB_getipnodebyname /* !HAVE_GETADDRINFO */ if (node != NULL) { /* first fallback is getipnodebyname() */ if (family == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 family = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 family = PF_INET6; #else family = PF_INET; #endif } host = Getipnodebyname(node, family, AI_V4MAPPED, &error_num); if (host == NULL) { const static char ai_host_not_found[] = "Host not found"; const static char ai_no_address[] = "No address"; const static char ai_no_recovery[] = "No recovery"; const static char ai_try_again[] = "Try again"; const char *error_msg = "Unknown error"; switch (error_num) { case HOST_NOT_FOUND: error_msg = ai_host_not_found; break; case NO_ADDRESS: error_msg = ai_no_address; case NO_RECOVERY: error_msg = ai_no_recovery; case TRY_AGAIN: error_msg = ai_try_again; } Error2("getipnodebyname(\"%s\", ...): %s", node, error_msg); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif } } freehostent(host); } #else /* !HAVE_PROTOTYPE_LIB_getipnodebyname */ if (node != NULL) { /* this is not a typical IP6 resolver function - but Linux "man gethostbyname" says that the only supported address type with this function is AF_INET _at present_, so maybe this fallback will be useful somewhere sometimesin a future even for IP6 */ if (family == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 family = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 family = PF_INET6; #else family = PF_INET; #endif } /*!!! try gethostbyname2 for IP6 */ if ((host = Gethostbyname(node)) == NULL) { Error2("gethostbyname(\"%s\"): %s", node, h_errno == NETDB_INTERNAL ? strerror(errno) : hstrerror(h_errno)); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_RETRYLATER; } if (host->h_addrtype != family) { Error2("xioaddrinfo(): \"%s\" does not resolve to %s", node, family==PF_INET?"IP4":"IP6"); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif /* WITH_IP6 */ } } } #endif #if WITH_TCP || WITH_UDP if (service) { port = parseport(service, protocol); } if (port >= 0) { switch (family) { #if WITH_IP4 case PF_INET: sau->ip4.sin_port = port; break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: sau->ip6.sin6_port = port; break; #endif /* WITH_IP6 */ } } #endif /* WITH_TCP || WITH_UDP */ if (numnode) free(numnode); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif /* HAVE_RESOLV_H */ return STAT_OK; }
int main(int argc, const char *argv[]) { const char **arg1, *a; char *mainwaitstring; char buff[10]; double rto; int i, argc0, result; struct utsname ubuf; int lockrc; if (mainwaitstring = getenv("SOCAT_MAIN_WAIT")) { sleep(atoi(mainwaitstring)); } diag_set('p', strchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]); /* we must init before applying options because env settings have lower priority and are to be overridden by options */ if (xioinitialize(XIO_MAYALL) != 0) { Exit(1); } xiosetopt('p', "%"); xiosetopt('o', ":"); argc0 = argc; /* save for later use */ arg1 = argv+1; --argc; while (arg1[0] && (arg1[0][0] == '-')) { switch (arg1[0][1]) { case 'V': socat_version(stdout); Exit(0); #if WITH_HELP case '?': case 'h': socat_usage(stdout); xioopenhelp(stdout, (arg1[0][2]=='?'||arg1[0][2]=='h') ? (arg1[0][3]=='?'||arg1[0][3]=='h') ? 2 : 1 : 0); Exit(0); #endif /* WITH_HELP */ case 'd': diag_set('d', NULL); break; #if WITH_FILAN case 'D': xioparams->debug = true; break; #endif case 'l': switch (arg1[0][2]) { case 'm': /* mixed mode: stderr, then switch to syslog; + facility */ diag_set('s', NULL); xiosetopt('l', "m"); xioparams->logopt = arg1[0][2]; xiosetopt('y', &arg1[0][3]); break; case 'y': /* syslog + facility */ diag_set(arg1[0][2], &arg1[0][3]); break; case 'f': /* to file, +filename */ case 'p': /* artificial program name */ if (arg1[0][3]) { diag_set(arg1[0][2], &arg1[0][3]); } else if (arg1[1]) { diag_set(arg1[0][2], arg1[1]); ++arg1, --argc; } else { Error1("option -l%c requires an argument; use option \"-h\" for help", arg1[0][2]); } break; case 's': /* stderr */ diag_set(arg1[0][2], NULL); break; case 'u': diag_set('u', NULL); break; case 'h': diag_set_int('h', true); break; default: Error1("unknown log option \"%s\"; use option \"-h\" for help", arg1[0]); break; } break; case 'v': xioparams->verbose = true; break; case 'x': xioparams->verbhex = true; break; case 'b': if (arg1[0][2]) { a = *arg1+2; } else { ++arg1, --argc; if ((a = *arg1) == NULL) { Error("option -b requires an argument; use option \"-h\" for help"); Exit(1); } } xioparams->bufsiz = strtoul(a, (char **)&a, 0); break; case 's': diag_set_int('e', E_FATAL); break; case 't': if (arg1[0][2]) { a = *arg1+2; } else { ++arg1, --argc; if ((a = *arg1) == NULL) { Error("option -t requires an argument; use option \"-h\" for help"); Exit(1); } } rto = strtod(a, (char **)&a); xioparams->closwait.tv_sec = rto; xioparams->closwait.tv_usec = (rto-xioparams->closwait.tv_sec) * 1000000; break; case 'T': if (arg1[0][2]) { a = *arg1+2; } else { ++arg1, --argc; if ((a = *arg1) == NULL) { Error("option -T requires an argument; use option \"-h\" for help"); Exit(1); } } rto = strtod(a, (char **)&a); xioparams->total_timeout.tv_sec = rto; xioparams->total_timeout.tv_usec = (rto-xioparams->total_timeout.tv_sec) * 1000000; break; case 'u': xioparams->lefttoright = true; break; case 'U': xioparams->righttoleft = true; break; case 'g': xioopts_ignoregroups = true; break; case 'L': if (socat_opts.lock.lockfile) Error("only one -L and -W option allowed"); if (arg1[0][2]) { socat_opts.lock.lockfile = *arg1+2; } else { ++arg1, --argc; if ((socat_opts.lock.lockfile = *arg1) == NULL) { Error("option -L requires an argument; use option \"-h\" for help"); Exit(1); } } break; case 'W': if (socat_opts.lock.lockfile) Error("only one -L and -W option allowed"); if (arg1[0][2]) { socat_opts.lock.lockfile = *arg1+2; } else { ++arg1, --argc; if ((socat_opts.lock.lockfile = *arg1) == NULL) { Error("option -W requires an argument; use option \"-h\" for help"); Exit(1); } } socat_opts.lock.waitlock = true; socat_opts.lock.intervall.tv_sec = 1; socat_opts.lock.intervall.tv_nsec = 0; break; #if WITH_IP4 || WITH_IP6 #if WITH_IP4 case '4': #endif #if WITH_IP6 case '6': #endif xioopts.default_ip = arg1[0][1]; xioopts.preferred_ip = arg1[0][1]; break; #endif /* WITH_IP4 || WITH_IP6 */ case 'c': switch (arg1[0][2]) { case 'S': xioparams->pipetype = XIOCOMM_SOCKETPAIRS; break; case 'P': case 'p': xioparams->pipetype = XIOCOMM_PIPES; break; case 's': xioparams->pipetype = XIOCOMM_SOCKETPAIR; break; case 'Y': xioparams->pipetype = XIOCOMM_PTYS; break; case 'y': xioparams->pipetype = XIOCOMM_PTY; break; case 't': xioparams->pipetype = XIOCOMM_TCP; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': xioparams->pipetype = atoi(&arg1[0][2]); break; default: Error1("bad chain communication type \"%s\"", &arg1[0][2]); } break; case '\0': case '-': /*! this is hardcoded "--" */ case ',': case ':': break; /* this "-" is a variation of STDIO or -- */ default: xioinqopt('p', buff, sizeof(buff)); if (arg1[0][1] == buff[0]) { break; } Error1("unknown option \"%s\"; use option \"-h\" for help", arg1[0]); Exit(1); } /* the leading "-" might be a form of the first address */ xioinqopt('p', buff, sizeof(buff)); if (arg1[0][0] == '-' && (arg1[0][1] == '\0' || arg1[0][1] == ':' || arg1[0][1] == ',' || arg1[0][1] == '-'/*!*/ || arg1[0][1] == buff[0])) break; ++arg1; --argc; } #if 0 Info1("%d address arguments", argc); #else if (argc != 2) { Error1("exactly 2 addresses required (there are %d); use option \"-h\" for help", argc); Exit(1); } #endif if (xioparams->lefttoright && xioparams->righttoleft) { Error("-U and -u must not be combined"); } xioinitialize2(); Info(copyright_socat); #if WITH_OPENSSL Info(copyright_openssl); Info(copyright_ssleay); #endif Debug2("socat version %s on %s", socatversion, timestamp); xiosetenv("VERSION", socatversion, 1); /* SOCAT_VERSION */ uname(&ubuf); /* ! here we circumvent internal tracing (Uname) */ Debug4("running on %s version %s, release %s, machine %s\n", ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine); #if WITH_MSGLEVEL <= E_DEBUG for (i = 0; i < argc0; ++i) { Debug2("argv[%d]: \"%s\"", i, argv[i]); } #endif /* WITH_MSGLEVEL <= E_DEBUG */ /* not sure what signal should print a message */ Signal(SIGHUP, socat_signal); Signal(SIGINT, socat_signal); Signal(SIGQUIT, socat_signal); Signal(SIGILL, socat_signal); /* SIGABRT for assert; catching caused endless recursion on assert in libc (tzfile.c:498: __tzfile_compute: Assertion 'num_types == 1' failed.) */ /*Signal(SIGABRT, socat_signal);*/ Signal(SIGBUS, socat_signal); Signal(SIGFPE, socat_signal); Signal(SIGSEGV, socat_signal); Signal(SIGTERM, socat_signal); #if HAVE_SIGACTION { struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_flags = SA_NOCLDSTOP|SA_RESTART|SA_SIGINFO #ifdef SA_NOMASK |SA_NOMASK #endif ; act.sa_sigaction = xiosigaction_subaddr_ok; if (Sigaction(SIGUSR1, &act, NULL) < 0) { /*! Linux man does not explicitely say that errno is defined */ Warn1("sigaction(SIGUSR1, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno)); } } #else /* !HAVE_SIGACTION */ if (Signal(SIGUSR1, xiosigaction_subaddr_ok) == SIG_ERR) { Warn1("signal(SIGCHLD, xiosigaction_subaddr_ok): %s", strerror(errno)); } #endif /* !HAVE_SIGACTION */ /* set xio hooks */ xiohook_newchild = &socat_newchild; if (lockrc = socat_lock()) { /* =0: goon; >0: locked; <0: error, printed in sub */ if (lockrc > 0) Error1("could not obtain lock \"%s\"", socat_opts.lock.lockfile); Exit(1); } Atexit(socat_unlock); result = socat(argc, arg1[0], arg1[1]); Notice1("exiting with status %d", result); Exit(result); return 0; /* not reached, just for gcc -Wall */ }
void AdviceManager::HandleAdviceResponse( Client *advisor, csString sAdvisee, csString message) { if ( !advisor->GetAdvisor() ) { psserver->SendSystemInfo(advisor->GetClientNum(),"You need to be an advisor to use this command."); return; } csString buf; // Source Client Name, Target Client Name, Message buf.Format("%s, %s, \"%s\"", advisor->GetName(), sAdvisee.GetData() , message.GetData()); psserver->GetLogCSV()->Write(CSV_ADVICE, buf); // Find Advisee Client by name if (sAdvisee.Length()) { sAdvisee = NormalizeCharacterName(sAdvisee); } Client *advisee = psserver->GetConnections()->Find(sAdvisee); if (!advisee) { // Create a new message to report TELL error and send // back to original person. csString sMsg("No player named "); sMsg += sAdvisee; sMsg += " is logged on to the system currently."; psserver->SendSystemError(advisor->GetClientNum(), sMsg); return; } // Can't allow you to advise yourself if ( advisee == advisor ) { psserver->SendSystemError(advisor->GetClientNum(), "You are not allowed to advise yourself. Please wait for another advisor."); return; } // find existing Advicee in the List AdviceSession key; key.AdviseeClientNum = advisee->GetClientNum(); AdviceSession *activeSession = AdviseeList.Get(advisee->GetClientNum(), NULL); if (!activeSession || (activeSession && ( !activeSession->requestEvent ) && ( activeSession->GetAdvisor() == NULL ) ) ) { psserver->SendSystemError(advisor->GetClientNum(), "%s has not requested help.", advisee->GetName()); return; } if (activeSession && ( activeSession->AdviseeClientNum != advisee->GetClientNum() ) ) { Debug2( LOG_ANY, advisee->GetClientNum(), "Grabbed wrong advisor session: %d", activeSession->AdviseeClientNum ); } if ( ( activeSession->GetAdvisor() != NULL ) && ( activeSession->AdvisorClientNum != advisor->GetClientNum() ) ) { psserver->SendSystemError(advisor->GetClientNum(), "%s is being advised already, thank you.", advisee->GetName()); return; } if ( message.Length() == 0 && activeSession->status == SESSION_STATUS_UNKNOWN ) // advisor is claiming a session { // check to make sure advisor has only one claimed session. AdviceSession *loopSession; csHash< AdviceSession* >::GlobalIterator loop( AdviseeList.GetIterator() ); while(loop.HasNext()) { loopSession = loop.Next(); if (activeSession->status == SESSION_STATUS_CLAIMED && loopSession->GetAdvisor() == advisor ) { psserver->SendSystemInfo(advisor->GetClientNum(), "You cannot have two messengers waiting for you at the same time, please answer %s's request first." , loopSession->adviseeName.GetData() ); return; } } activeSession->SetAdvisor( advisor ); psserver->SendSystemInfo(advisee->GetClientNum(), "An advisor is preparing an answer to your question, please be patient."); psserver->SendSystemInfo(advisor->GetClientNum(), "You have claimed the session with %s. Please provide an answer." , advisee->GetName() ); for (size_t i = 0; i < advisors.GetSize(); i++) { if ( advisors[i].id != activeSession->AdvisorClientNum ) { psserver->SendSystemInfo(advisors[i].id, "%s has proclaimed they know the answer to %s's question.", advisor->GetName(), advisee->GetName() ); } } activeSession->status = SESSION_STATUS_CLAIMED; } else { if (message.IsEmpty()) { psserver->SendSystemInfo(advisor->GetClientNum(), "Please enter the advice you wish to give."); return; } psChatMessage msgChat(activeSession->AdviseeClientNum, 0, advisor->GetName(), advisee->GetName(), message ,CHAT_ADVISOR,false); if ( activeSession->GetAdvisor() == NULL || activeSession->status != SESSION_STATUS_OWNED ) { // Check to make sure the advice is 'good' // if ( message.Length() < 20 ) // { // psserver->SendSystemInfo(advisor->GetClientNum(), "Please be more specific when answering questions. Your advice has been ignored."); // return; // } //activeSession->AdvisorClientNum = me->clientnum; activeSession->SetAdvisor( advisor ); advisor->IncrementAdvisorPoints(activeSession->advisorPoints); psserver->CharacterLoader.SaveCharacterData( advisor->GetCharacterData(), advisor->GetActor(), true ); // Send Confirmation to advisor psserver->SendSystemInfo( advisor->GetClientNum(), "You are now advising %s.",advisee->GetName()); // Send Confirmation to all other advisors so they know they lost this one. for (size_t i = 0; i < advisors.GetSize(); i++) { if ( advisors[i].id != activeSession->AdvisorClientNum ) { psserver->SendSystemInfo( advisors[i].id, "%s has been assigned to %s.", advisee->GetName(), advisor->GetName() ); if ( advisors[i].GM ) continue; msgChat.msg->clientnum = advisors[i].id; msgChat.SendMessage(); } } activeSession->status = SESSION_STATUS_OWNED; } if ( activeSession->requestEvent ) activeSession->requestEvent->valid = false; // This keeps the cancellation timeout from firing. activeSession->answered = true; // Send Message to Advisee msgChat.msg->clientnum = activeSession->AdviseeClientNum; msgChat.SendMessage(); // Send Message to advisor msgChat.msg->clientnum = activeSession->AdvisorClientNum; msgChat.SendMessage(); // Send message to GM chars as well for (size_t i = 0; i < advisors.GetSize(); i++) { if (!advisors[i].GM || advisors[i].id == activeSession->AdvisorClientNum) continue; msgChat.msg->clientnum = advisors[i].id; msgChat.SendMessage(); } } // Add timeout for Advisor-Advisee relationship // this will allow later questions by the same client to go to a different advisor // spreading the wealth and burden amongst all ;) if ( activeSession->timeoutEvent ) { activeSession->timeoutEvent->valid = false; } psAdviceSessionTimeoutGameEvent *ev = new psAdviceSessionTimeoutGameEvent( this, activeSession->answered?ADVICE_SESSION_TIMEOUT:ADVICE_SESSION_TIMEOUT/2, advisee->GetActor(), activeSession ); activeSession->timeoutEvent = ev; psserver->GetEventManager()->Push(ev); }
void CombatManager::HandleCombatEvent(psCombatGameEvent *event) { psCharacter *attacker_data,*target_data; int attack_result; bool skipThisRound = false; if (!event->GetAttacker() || !event->GetTarget()) // disconnected and deleted { #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "Combat stopped as one participant logged of."); #endif return; } gemActor *gemAttacker = dynamic_cast<gemActor*> ((gemObject *) event->attacker); gemActor *gemTarget = dynamic_cast<gemActor*> ((gemObject *) event->target); attacker_data=event->GetAttackerData(); target_data=event->GetTargetData(); // If the attacker is no longer in attack mode abort. if (gemAttacker->GetMode() != PSCHARACTER_MODE_COMBAT) { #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "Combat stopped as you left combat mode."); #endif return; } // If target is dead, abort. if (!gemTarget->IsAlive() ) { #ifdef COMBAT_DEBUG psserver->SendSystemResult(event->AttackerCID, "Combat stopped as one participant logged of."); #endif return; } // If the slot is no longer attackable, abort if (!attacker_data->Inventory().CanItemAttack(event->GetWeaponSlot())) { #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "Combat stopped as you have no longer an attackable item equipped."); #endif return; } if (attacker_data->Inventory().GetEquipmentObject(event->GetWeaponSlot()).eventId != event->id) { #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "Ignored combat event as newer is in."); #endif return; } psItem* weapon = attacker_data->Inventory().GetEffectiveWeaponInSlot(event->GetWeaponSlot()); // weapon became unwieldable csString response; if(weapon!=NULL && !weapon->CheckRequirements(attacker_data,response)) { Debug2(LOG_COMBAT, gemAttacker->GetClientID(),"%s has lost use of weapon", gemAttacker->GetName() ); psserver->SendSystemError(event->AttackerCID, "You can't use your %s any more.", weapon->GetName() ); return; } // If the weapon in the slot has been changed, skip a turn (latency for this slot may also have changed) if (event->WeaponID != weapon->GetUID()) { Debug2(LOG_COMBAT, gemAttacker->GetClientID(),"%s has changed weapons mid battle", gemAttacker->GetName() ); #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "Weapon changed. Skipping"); #endif skipThisRound = true; } Client * attacker_client = psserver->GetNetManager()->GetClient(event->AttackerCID); if (attacker_client) { // Input the stamina data MathEnvironment env; env.Define("Actor", event->GetAttacker()); env.Define("Weapon", weapon); staminacombat->Evaluate(&env); MathVar *PhyDrain = env.Lookup("PhyDrain"); MathVar *MntDrain = env.Lookup("MntDrain"); if ( (attacker_client->GetCharacterData()->GetStamina(true) < PhyDrain->GetValue()) || (attacker_client->GetCharacterData()->GetStamina(false) < MntDrain->GetValue()) ) { StopAttack(attacker_data->GetActor()); psserver->SendSystemError(event->AttackerCID, "You are too tired to attack."); return; } // If the target has become impervious, abort and give up attacking if (!attacker_client->IsAllowedToAttack(gemTarget)) { StopAttack(attacker_data->GetActor()); return; } // If the target has changed, abort (assume another combat event has started since we are still in attack mode) if (gemTarget != attacker_client->GetTargetObject()) { #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "Target changed."); #endif return; } } else { // Check if the npc's target has changed (if it has, then assume another combat event has started.) gemNPC* npcAttacker = dynamic_cast<gemNPC*>(gemAttacker); if (npcAttacker && npcAttacker->GetTarget() != gemTarget) { #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "NPC's target changed."); #endif return; } } if (gemAttacker->IsSpellCasting()) { psserver->SendSystemInfo(event->AttackerCID, "You can't attack while casting spells."); skipThisRound = true; } if (!skipThisRound) { if (weapon->GetIsRangeWeapon() && weapon->GetUsesAmmo()) { INVENTORY_SLOT_NUMBER otherHand = event->GetWeaponSlot() == PSCHARACTER_SLOT_RIGHTHAND ? PSCHARACTER_SLOT_LEFTHAND: PSCHARACTER_SLOT_RIGHTHAND; attack_result = ATTACK_NOTCALCULATED; psItem* otherItem = attacker_data->Inventory().GetInventoryItem(otherHand); if (otherItem == NULL) { attack_result = ATTACK_OUTOFAMMO; } else if (otherItem->GetIsContainer()) // Is it a quiver? { // Pick the first ammo we can shoot from the container // And set it as the active ammo bool bFound = false; for (size_t i=1; i<attacker_data->Inventory().GetInventoryIndexCount() && !bFound; i++) { psItem* currItem = attacker_data->Inventory().GetInventoryIndexItem(i); if (currItem && currItem->GetContainerID() == otherItem->GetUID() && weapon->GetAmmoTypeID().In(currItem->GetBaseStats()->GetUID())) { otherItem = currItem; bFound = true; } } if (!bFound) attack_result = ATTACK_OUTOFAMMO; } else if (!weapon->GetAmmoTypeID().In(otherItem->GetBaseStats()->GetUID())) { attack_result = ATTACK_OUTOFAMMO; } if (attack_result != ATTACK_OUTOFAMMO) { psItem* usedAmmo = attacker_data->Inventory().RemoveItemID(otherItem->GetUID(), 1); if (usedAmmo) { attack_result=CalculateAttack(event, usedAmmo); usedAmmo->Destroy(); psserver->GetCharManager()->UpdateItemViews(attacker_client->GetClientNum()); } else attack_result=CalculateAttack(event); } } else { attack_result=CalculateAttack(event); } event->AttackResult=attack_result; ApplyCombatEvent(event, attack_result); } // Queue next event to continue combat if this is an auto attack slot if (attacker_data->Inventory().IsItemAutoAttack(event->GetWeaponSlot())) { // CPrintf(CON_DEBUG, "Queueing Slot %d for %s's next combat event.\n",event->GetWeaponSlot(), event->attacker->GetName() ); QueueNextEvent(event); } else { #ifdef COMBAT_DEBUG psserver->SendSystemError(event->AttackerCID, "Item %s is not a auto attack item.",attacker_data->Inventory().GetEffectiveWeaponInSlot(event->GetWeaponSlot())->GetName()); #endif } // else // CPrintf(CON_DEBUG, "Slot %d for %s not an auto-attack slot.\n",event->GetWeaponSlot(), event->attacker->GetName() ); }
void AdviceManager::HandleAdviceRequest( Client *advisee, csString message ) { // psserver->SendSystemInfo( advisee->GetClientNum(), "Sorry for the inconvenience, you cannot request advice right now. The advice system is under maintenaince and will be available after a server restart soon."); // return; if ( advisee->GetAdvisor() ) { psserver->SendSystemInfo( advisee->GetClientNum(), "You cannot request advice while you are an advisor. Lay down your advisor role using \"/advisor off\", then request advice."); return; } if ( advisors.GetSize() == 0 ) //No advisors are online { //Send back an message that there is no advisor online psserver->SendSystemInfo( advisee->GetClientNum(),"The world has no advisors at the moment. You may ask for further help on IRC or on our forums. You may also try to ask players around you for help with \"/tell <player name> <message>\"."); return; } // find existing Advicee in the List AdviceSession *activeSession = AdviseeList.Get(advisee->GetClientNum(), NULL); // Create an adviceSession if one doesn't exist and the message is valid if ( !activeSession ) { WordArray words(message); // Check to make sure the request is 'good' if ( words.GetCount() < 2 ) { psserver->SendSystemInfo( advisee->GetClientNum(), "An Advisor will need more information than this to help you, please include as much as you can in your request."); return; } Debug2( LOG_ANY, advisee->GetClientNum(), "Creating AdviceSession for %d", advisee->GetClientNum() ); activeSession = new AdviceSession( this, NULL, advisee, message ); AdviseeList.Put(activeSession->AdviseeClientNum, activeSession); // Advice is no longer pending. } else { // One unadvised unanswered question at a time if (!activeSession->GetAdvisor() && !activeSession->answered) { psserver->SendSystemError(advisee->GetClientNum(), "Please wait for an advisor to respond to your previous inquiry before asking another question."); return; } } // Create psAdviceRequestTimeoutGameEvent to timeout question. if ( activeSession && activeSession->timeoutEvent == NULL && activeSession->requestEvent == NULL) { psAdviceRequestTimeoutGameEvent *ev = new psAdviceRequestTimeoutGameEvent( this, ADVICE_QUESTION_TIMEOUT, advisee->GetActor(), activeSession ); activeSession->requestEvent = ev; psserver->GetEventManager()->Push(ev); activeSession->answered = false; } else if ( activeSession ) { WordArray words(message); // Check to make sure the request is 'good' if ( words.GetCount() < 2 ) { psserver->SendSystemInfo(advisee->GetClientNum(), "An Advisor will need more information than this to help you, please include as much as you can in your request."); activeSession->answered = true; return; } if ( activeSession->timeoutEvent ) { activeSession->timeoutEvent->valid = false; } psAdviceSessionTimeoutGameEvent *ev = new psAdviceSessionTimeoutGameEvent( this, activeSession->answered?ADVICE_SESSION_TIMEOUT:ADVICE_SESSION_TIMEOUT/2, advisee->GetActor(), activeSession ); activeSession->timeoutEvent = ev; psserver->GetEventManager()->Push(ev); activeSession->answered = false; } activeSession->lastRequest = message; csString buf; if ( activeSession->AdvisorClientNum != (uint32_t)-1 ) { // Source Client Name, Target Client Name, Message buf.Format("%s, %s, \"%s\"", advisee->GetName(), activeSession->GetAdvisor()->GetActor()->GetName(), message.GetData() ); psserver->GetLogCSV()->Write(CSV_ADVICE, buf); psChatMessage msgAdvisor( activeSession->AdvisorClientNum, 0, advisee->GetName(), 0, message , CHAT_ADVICE, false ); msgAdvisor.SendMessage(); // Send message to GM chars as well for (size_t i = 0; i < advisors.GetSize(); i++) { if (!advisors[i].GM || advisors[i].id == activeSession->AdvisorClientNum) continue; psChatMessage msgAdvisor( advisors[i].id, 0, advisee->GetName(), 0, message, CHAT_ADVICE, false); msgAdvisor.SendMessage(); } } else { // Source Client Name, Target Client Name, Message buf.Format("%s, %s, \"%s\"", advisee->GetName(), "All Advisors", message.GetData() ); psserver->GetLogCSV()->Write(CSV_ADVICE, buf); for (size_t i = 0; i < advisors.GetSize(); i++) { psChatMessage msgAdvisor( advisors[i].id, 0, advisee->GetName(), 0, message, CHAT_ADVICE, false); msgAdvisor.SendMessage(); } } psChatMessage msgChat( advisee->GetClientNum() , 0, advisee->GetName(), 0, message, CHAT_ADVICE, false); msgChat.SendMessage(); }
/*************************************************** * scalerVS_arm() * Make scaler ready to count. If ARM output is connected * to ARM input, and GATE permits, the scaler will * actually start counting. ****************************************************/ STATIC long scalerVS_arm(scalerRecord *psr, int val) { devScalerPvt *dpvt = psr->dpvt; int card = dpvt->card; volatile char *addr; volatile uint16 u16; int i, j, retry, read_again, numBad, numGood; Debug2(1, "scalerVS_arm: card %d, val %d\n", card, val); if (card >= scalerVS_total_cards) return(ERROR); addr = scalerVS_state[card]->localAddr; callbackGetUser(psr, scalerVS_state[card]->pcallback); /* disable end-of-gate interrupt */ u16 = readReg16(addr,IRQ_SETUP_OFFSET); writeReg16(addr,IRQ_SETUP_OFFSET, u16 & 0x07ff); if (val) { /*** start counting ***/ /* reset counters, overflows, and overflow-IRQ source */ writeReg16(addr,RESET_ALL_COUNTERS_OFFSET, (uint16)1); /* clear other IRQ's */ writeReg16(addr,CLEAR_INTERRUPT_2_3_OFFSET, (uint16)3); /** Set up and enable interrupts **/ /* Write interrupt vector to hardware */ writeReg16(addr,IRQ_3_GATE_VECTOR_OFFSET, (uint16)(vs_InterruptVector + card)); Debug(10,"scalerVS_arm: irq vector=%d\n", (int)readReg16(addr,IRQ_3_GATE_VECTOR_OFFSET) & 0x00ff); /* set end-of-gate interrupt level, and enable the interrupt */ u16 = readReg16(addr, IRQ_SETUP_OFFSET); u16 = (u16 & 0x0ff) | (vs_InterruptLevel << 8) | 0x800; writeReg16(addr, IRQ_SETUP_OFFSET, u16); /* * How do I make sure the internal-gate counter is zero, and that it * won't start counting as soon as ARM goes TRUE? */ /* clear hardware-done flag */ scalerVS_state[card]->done = 0; /* enable all channels */ writeReg16(addr, COUNT_ENABLE_OFFSET, 1); /* any write enables */ /* arm scaler */ writeReg16(addr, ARM_OFFSET, 1); /* any write sets ARM */ /* Make sure trigger mode is set for internal gate. */ /* (This was already done in write_preset(). It's ok to do it again.) */ u16 = readReg16(addr, CLOCK_TRIG_MODE_OFFSET); writeReg16(addr, CLOCK_TRIG_MODE_OFFSET, u16 | 0x0010); /* trigger gate */ if (devScaler_VS_check_trig) { for (i=0, retry=1; retry && i<devScaler_VS_trig_retries; i++) { writeReg16(addr, TRIG_GATE_OFFSET, 1); /* any write triggers gate */ /* * Check status register bit 9 to make sure internal gate is open. * Repeat reading until we get the same value devScaler_VS_trig_reads * times in a row. */ for (read_again = 1; read_again; ) { for (j=0, numBad=numGood=0; j<devScaler_VS_trig_reads; j++) { u16 = readReg16(addr, STATUS_OFFSET); if (u16 & 0x0200) numGood++; else numBad++; } if (numBad == devScaler_VS_trig_reads) { /* we believe the gate did NOT get triggered */ retry = 1; read_again = 0; } else if (numGood == devScaler_VS_trig_reads) { /* we believe the gate got triggered */ retry = 0; read_again = 0; } } } if (retry) { printf("scalerVS_arm: %d trigger attempts apparently failed\n", i); } else if (i >= devScaler_VS_trig_retry_report) { Debug(1,"scalerVS_arm: trigger succeeded after %d retries.\n", i); } } else { writeReg16(addr, TRIG_GATE_OFFSET, 1); /* any write triggers gate */ } Debug2(5,"scalerVS_arm: gate open; SR=0x%x; irq vector=%d\n", readReg16(addr, STATUS_OFFSET), (int)readReg16(addr,IRQ_3_GATE_VECTOR_OFFSET) & 0x00ff); } else { /*** stop counting ***/ /* disarm scaler */ writeReg16(addr, DISARM_OFFSET, 1); /* any write resets ARM */ /* * Stop counter (change trigger mode from internal gate to external gate * (external gate should be 1?) */ u16 = readReg16(addr, CLOCK_TRIG_MODE_OFFSET); writeReg16(addr, CLOCK_TRIG_MODE_OFFSET, u16 & 0x000f); /* set hardware-done flag */ scalerVS_state[card]->done = 1; } return(0); }
/*************************************************** * scalerVS_write_preset() * This hardware has no preset capability, but we can set a time gate. * What we do here is put the hardware in "trigger" mode, set the gate-clock * frequency and the number of gate-clock periods to count for. We're going to * get called once for each channel, but we do all the real work on the first call. * From then on, we just make sure the presets are zero, and fix them if they aren't. ****************************************************/ STATIC long scalerVS_write_preset(scalerRecord *psr, int signal, long val) { devScalerPvt *dpvt = psr->dpvt; int card = dpvt->card; epicsInt32 *ppreset; unsigned short *pgate; volatile char *addr; unsigned short gate_freq_ix; double gate_time, gate_freq, gate_periods; if (devScaler_VSDebug >= 5) { printf("%s(%d):",__FILE__,__LINE__); printf("scalerVS_write_preset: card %d, signal %d, val %ld\n", card, signal, val); } if (card >= scalerVS_total_cards) return(ERROR); if (signal >= MAX_SCALER_CHANNELS) return(ERROR); addr = scalerVS_state[card]->localAddr; callbackGetUser(psr, scalerVS_state[card]->pcallback); if (signal > 0) { if (val != 0) { ppreset = &(psr->pr1); ppreset[signal] = 0; db_post_events(psr,&(ppreset[signal]),DBE_VALUE); pgate = &(psr->g1); pgate[signal] = 0; db_post_events(psr,&(pgate[signal]),DBE_VALUE); } return(0); } if (psr->g1 != 1) { psr->g1 = 1; db_post_events(psr,&(psr->g1),DBE_VALUE); } /*** set count time ***/ gate_time = val / psr->freq; /* * find largest gate-clock frequency that will allow us to specify the * requested count time with the 16-bit gate-preset register. Note that * the scaler counts for one extra clock cycle, so we allow 0xffff + 1 * = 65536 cycles. */ gate_freq_ix = 0; do { gate_freq = gate_freq_table[gate_freq_ix]; gate_periods = gate_time * gate_freq; if (devScaler_VSDebug >= 10) { printf("%s(%d):",__FILE__,__LINE__); printf("scalerVS_write_preset: try f=%.0f, n=%.0f, ix=%d\n", gate_freq, gate_periods, gate_freq_ix); } } while ((gate_periods > 65536) && (++gate_freq_ix < GATE_FREQ_TABLE_LENGTH)); if ((gate_periods < 4) && (gate_freq_ix == 0)) { /* The scaler can't count this short. Just count as short as possible */ printf("devScaler_VS: min. counting time is 4E-7 seconds.\n"); } /* docs recommend min of 4 periods; we're going to subtract 1 before writing. */ if (gate_periods < 5) gate_periods = 5; if ((gate_periods > 65536) && (gate_freq_ix >= GATE_FREQ_TABLE_LENGTH)) { /* The scaler can't count this long. Just count as long as possible */ printf("devScaler_VS: max. counting time is 655.36 seconds.\n"); gate_periods = 65536; gate_freq_ix = GATE_FREQ_TABLE_LENGTH - 1; } /* set clock frequency, and specify that software should trigger gate-start */ writeReg16(addr, CLOCK_TRIG_MODE_OFFSET, gate_freq_bits[gate_freq_ix] | 0x10); /* Set the gate-size register to the number of clock periods to count. */ /* Scaler must be in trigger mode at the time gate size is written. */ /* Docs say to specify (desired_clock_periods - 1) */ writeReg16(addr, INTERNAL_GATE_SIZE_OFFSET, (uint16)(gate_periods-1)); /* save preset and frequency mask in scalerVS_state */ scalerVS_state[card]->gate_periods = gate_periods; scalerVS_state[card]->gate_freq_ix = gate_freq_ix; /* tell record what preset and clock rate we're using */ psr->pr1 = gate_periods; psr->freq = gate_freq_table[gate_freq_ix]; Debug2(10,"scalerVS_write_preset: gate_periods=%f, gate_freq=%f\n", gate_periods, gate_freq); return(0); }
void pawsNpcDialogWindow::HandleMessage(MsgEntry* me) { if(me->GetType() == MSGTYPE_DIALOG_MENU) { psDialogMenuMessage mesg(me); Debug2(LOG_QUESTS, 0,"Got psDialogMenuMessage: %s\n", mesg.xml.GetDataSafe()); responseList->Clear(); SelfPopulateXML(mesg.xml); if(useBubbles) { speechBubble->Hide(); // hide previous npc say response LoadQuest(mesg.xml); AdjustForPromptWindow(); // should be done before DisplayQuestBubbles DisplayQuestBubbles(displayIndex); gotNewMenu = true; } else { AdjustForPromptWindow(); } Show(); } else if(me->GetType() == MSGTYPE_CHAT) { psChatMessage chatMsg(me); Debug2(LOG_QUESTS, 0,"Got Chat message from NPC: %s\n", (const char*)chatMsg.sText); GEMClientActor* actor = dynamic_cast<GEMClientActor*>(psengine->GetCelClient()->FindObject(chatMsg.actor)); if(!actor) return; // handle the basic action types, in the future we could play an animation as well csString inText ; switch(chatMsg.iChatType) { case CHAT_NPC_ME: { inText.Format("(%s %s)", (const char*)chatMsg.sPerson, ((const char*)chatMsg.sText)); break; } case CHAT_NPC_MY: { inText.Format("(%s's %s)", (const char*)chatMsg.sPerson, ((const char*)chatMsg.sText)); break; } case CHAT_NPC_NARRATE: { inText.Format("(%s)", (const char*)chatMsg.sText); break; } default: inText = chatMsg.sText; } NpcSays(inText, actor); //checks if the NPC Dialogue is displayed, in this case don't show the normal overhead bubble if(IsVisible()) return; } else if(me->GetType() == MSGTYPE_REMOVE_OBJECT) { psRemoveObject mesg(me); //if the current target is being removed hide this window if(IsVisible() && targetEID.IsValid() && mesg.objectEID.IsValid() && mesg.objectEID == targetEID) { Hide(); //In these cases something might have gone wrong with the camera //eg: the character is being teleported. So we need to have it reset //correctly. psengine->GetPSCamera()->ResetCameraPositioning(); } } }
void pawsNpcDialogWindow::OnListAction(pawsListBox* widget, int status) { if(status == LISTBOX_HIGHLIGHTED) { pawsTextBox* fld = dynamic_cast<pawsTextBox*>(widget->GetSelectedRow()->FindWidgetXMLBinding("text")); Debug2(LOG_QUESTS, 0, "Pressed: %s\n",fld->GetText()); } else if(status == LISTBOX_SELECTED) { //if no row is selected if(!widget->GetSelectedRow()) return; pawsTextBox* fld = dynamic_cast<pawsTextBox*>(widget->GetSelectedRow()->FindWidgetXMLBinding("text")); Debug2(LOG_QUESTS, 0,"Player chose '%s'.\n", fld->GetText()); pawsTextBox* trig = dynamic_cast<pawsTextBox*>(widget->GetSelectedRow()->FindWidgetXMLBinding("trig")); Debug2(LOG_QUESTS, 0,"Player says '%s'.\n", trig->GetText()); csString trigger(trig->GetText()); // Send the server the original trigger csString cmd; if(trigger.GetAt(0) == '=') // prompt window signal { // cut the questID and convert it to a number as it's needed by the prompt window management size_t pos = trigger.FindFirst("{"); size_t endPos = trigger.FindFirst("}"); int questID = -1; if(pos != SIZET_NOT_FOUND) { unsigned long value = strtoul(trigger.GetData() + pos + 1, NULL, 0); questID = value; // check for overflows if(questID < -1) { questID = -1; } endPos += 1; } else { endPos = 1; } pawsStringPromptWindow::Create(csString(trigger.GetData()+endPos), csString(""), false, 320, 30, this, trigger.GetData()+endPos, questID); } else { if(trigger.GetAt(0) != '<') { cmd.Format("/tellnpc %s", trigger.GetData()); psengine->GetCmdHandler()->Publish(cmd); } else { psSimpleStringMessage gift(0,MSGTYPE_EXCHANGE_AUTOGIVE,trigger); gift.SendMessage(); } DisplayTextInChat(fld->GetText()); } Hide(); } }