void ChatManager::SendTell(psChatMessage& msg, const char* who,Client *client,Client *target)
{
    Debug2(LOG_CHAT, client->GetClientNum(), "SendTell: %s!", msg.sText.GetDataSafe());

    // Sanity check that we are sending to correct clientnum!
    csString targetName = msg.sPerson;
    targetName = NormalizeCharacterName(targetName);
    CS_ASSERT(strcasecmp(target->GetName(), targetName) == 0);

    // Create a new message and send it to that person if found
    psChatMessage cmsg(target->GetClientNum(), client->GetActor()->GetEID(), who, 0, msg.sText, msg.iChatType, msg.translate);
    cmsg.SendMessage();

    // Echo the message back to the speaker also
    psChatMessage cmsg2(client->GetClientNum(), client->GetActor()->GetEID(), targetName, 0, msg.sText, CHAT_TELLSELF, msg.translate);
    cmsg2.SendMessage();

    // Save to both actors' chat history (PS#2789)
    client->GetActor()->LogChatMessage(who, msg);
    target->GetActor()->LogChatMessage(who, msg);
}
示例#2
0
void CharCreationManager::HandleUploadMessage(MsgEntry* me, Client* client)
{
    Debug1(LOG_NEWCHAR, me->clientnum,"New Character is being created");

    psCharUploadMessage upload(me);

    if(!upload.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psUploadMessage from client %u.",me->clientnum);
        return;
    }

    AccountID acctID = client->GetAccountID();
    if(!acctID.IsValid())
    {
        Error2("Player tried to upload a character to unknown account %s.", ShowID(acctID));

        psCharRejectedMessage reject(me->clientnum);

        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer(me->clientnum,"Could not find your account.");
        return;
    }

    // Check to see if the player already has 4 accounts;
    csString query;
    query.Format("SELECT id FROM characters WHERE account_id=%d", acctID.Unbox());
    Result result(db->Select(query));
    if(result.IsValid() && result.Count() >= CHARACTERS_ALLOWED)
    {
        psserver->RemovePlayer(me->clientnum,"At your character limit.");
        return;
    }

    csString playerName =  upload.name;
    csString lastName =  upload.lastname;

    playerName = NormalizeCharacterName(playerName);
    lastName = NormalizeCharacterName(lastName);

    // Check banned names
    if(psserver->GetCharManager()->IsBanned(playerName))
    {
        csString error;
        error.Format("The name %s is banned", playerName.GetData());
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::RESERVED_NAME,
                                     (char*)error.GetData());
        reject.SendMessage();
        return;
    }

    if(psserver->GetCharManager()->IsBanned(lastName))
    {
        csString error;
        error.Format("The lastname %s is banned", lastName.GetData());
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::RESERVED_NAME,
                                     (char*)error.GetData());
        reject.SendMessage();
        return;
    }

    Debug3(LOG_NEWCHAR, me->clientnum,"Got player firstname (%s) and lastname (%s)\n",playerName.GetData(), lastName.GetData());

    ///////////////////////////////////////////////////////////////
    //  Check to see if the player name is valid
    ///////////////////////////////////////////////////////////////
    if(playerName.Length() == 0 || !FilterName(playerName))
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_LEGAL_NAME,
                                     "The name you specifed is not a legal player name.");

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    if(lastName.Length() != 0 && !FilterName(lastName))
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_LEGAL_NAME,
                                     "The name you specifed is not a legal lastname.");

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    Debug2(LOG_NEWCHAR, me->clientnum,"Checking player firstname '%s'..\n",playerName.GetData());
    ///////////////////////////////////////////////////////////////
    //  Check to see if the character name is unique in 'characters'.
    ///////////////////////////////////////////////////////////////
    if(!IsUnique(playerName))
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_UNIQUE_NAME,
                                     "The firstname you specifed is not unique.");

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    if(lastName.Length())
    {
        Debug2(LOG_NEWCHAR, me->clientnum,"Checking player lastname '%s'..\n",lastName.GetData());
        if(!IsLastNameAvailable(lastName, acctID))
        {
            psCharRejectedMessage reject(me->clientnum,
                                         psCharRejectedMessage::NON_UNIQUE_NAME,
                                         "The lastname you specifed is not unique.");

            psserver->GetEventManager()->SendMessage(reject.msg);
            return;
        }
    }
    ///////////////////////////////////////////////////////////////
    //  Check to see if the character name is on the reserve list.
    ///////////////////////////////////////////////////////////////
    int reservedName = IsReserved(playerName, acctID);
    if(reservedName == NAME_RESERVED)
    {
        csString error;
        error.Format("The name %s is reserved", playerName.GetData());
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::RESERVED_NAME,
                                     (char*)error.GetData());

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }


    csString error;
    if(!psserver->charCreationManager->Validate(upload, error))
    {
        error.Append(", your creation choices are invalid.");
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::INVALID_CREATION,
                                     (char*)error.GetData());

        reject.SendMessage();
        return;
    }

    ///////////////////////////////////////////////////////////////
    //  Create the psCharacter structure for the player.
    ///////////////////////////////////////////////////////////////
    psCharacter* chardata=new psCharacter();
    chardata->SetCharType(PSCHARACTER_TYPE_PLAYER);
    chardata->SetFullName(playerName,lastName);
    chardata->SetCreationInfo(upload.bio);

    psRaceInfo* raceinfo=psserver->GetCacheManager()->GetRaceInfoByNameGender(upload.race, (PSCHARACTER_GENDER)upload.gender);
    if(raceinfo==NULL)
    {
        Error3("Invalid race/gender combination on character creation:  Race='%d' Gender='%d'", upload.race, upload.gender);
        psCharRejectedMessage reject(me->clientnum);
        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer(me->clientnum,"Player tried to create an invalid race/gender.");
        delete chardata;
        return;
    }
    chardata->SetRaceInfo(raceinfo);
    chardata->SetHitPoints(50.0);
    chardata->GetMaxHP().SetBase(0.0);
    chardata->GetMaxMana().SetBase(0.0);

    //range is unused here
    float x,y,z,yrot,range;
    const char* sectorname;
    InstanceID newinstance = DEFAULT_INSTANCE;

    //get the option entries for tutorial from the server options. Note it's tutorial:variousdata
    optionEntry* tutorialEntry = psserver->GetCacheManager()->getOptionSafe("tutorial","");
    sectorname = tutorialEntry->getOptionSafe("sectorname", "tutorial")->getValue();

    psSectorInfo* sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName(sectorname);

    if(!sectorinfo || PlayerHasFinishedTutorial(acctID, sectorinfo->uid))
    {
        raceinfo->GetStartingLocation(x,y,z,yrot,range,sectorname);
        sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName(sectorname);

        //As we aren't going in the tutorial disable the tutorial help messages disable them
        for(int i = 0; i < TutorialManager::TUTOREVENTTYPE_COUNT; i++)
            chardata->CompleteHelpEvent(i);
    }
    else
    {
        // Try tutorial level first.
        x = tutorialEntry->getOptionSafe("sectorx", "-225.37")->getValueAsDouble();
        y = tutorialEntry->getOptionSafe("sectory", "-21.32")->getValueAsDouble();
        z = tutorialEntry->getOptionSafe("sectorz", "26.79")->getValueAsDouble();
        yrot = tutorialEntry->getOptionSafe("sectoryrot", "-2.04")->getValueAsDouble();
    }

    bool sectorFound = true;

    if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) == NULL)
    {
        Error2("Sector='%s' found but no map file was detected for it. Using NPCroom1", sectorname);
        sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName("NPCroom1");
        if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) == NULL)
        {
            Error1("NPCroom1 failed - Critical");
            sectorFound = false;
        }
        else if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name))
        {
            sectorFound = true;
        }
        else
        {
            sectorFound = false;
        }
    }
    else if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name))
    {
        sectorFound = true;
    }
    else
    {
        sectorFound = false;
    }


    if(!sectorFound)
    {
        Error2("Unresolvable starting sector='%s'", sectorname);
        psCharRejectedMessage reject(me->clientnum);
        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer(me->clientnum,"No starting Sector.");
        delete chardata;
        return;
    }

    chardata->SetLocationInWorld(newinstance, sectorinfo, x, y, z, yrot);

    psTrait* trait;
//    CPrintf(CON_DEBUG, "Trait: %d\n", upload.selectedFace );
    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedFace);
    if(trait)
        chardata->SetTraitForLocation(trait->location, trait);

    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedHairStyle);
    if(trait)
        chardata->SetTraitForLocation(trait->location, trait);

    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedBeardStyle);
    if(trait)
        chardata->SetTraitForLocation(trait->location, trait);

    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedHairColour);
    if(trait)
        chardata->SetTraitForLocation(trait->location, trait);

    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedSkinColour);
    if(trait)
        chardata->SetTraitForLocation(trait->location, trait);

    gemActor* actor = new gemActor(gemSupervisor, cacheManager, entityManager, chardata,
                                   raceinfo->mesh_name,
                                   newinstance,
                                   EntityManager::GetSingleton().FindSector(sectorinfo->name),
                                   csVector3(x,y,z),yrot,
                                   client->GetClientNum());

    actor->SetupCharData();

    if(!upload.verify)
    {
        if(!psServer::CharacterLoader.NewCharacterData(acctID,chardata))
        {
            Error1("Character could not be created.");
            psCharRejectedMessage reject(me->clientnum);
            psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
            psserver->RemovePlayer(me->clientnum,"Your character could not be created in the database.");
            delete chardata;
            return;
        }
    }

    // Check to see if a path name was set. If so we will use that to generate
    // the character starting stats and skills.
    if(upload.path != "None")
    {
        // Progression Event name is PATH_PathName
        csString name("PATH_");
        name.Append(upload.path);
        ProgressionScript* script = psserver->GetProgressionManager()->FindScript(name.GetData());
        if(script)
        {
            // The script uses the race base character points to calculate starting stats.
            MathEnvironment env;
            env.Define("CharPoints", raceinfo->initialCP);
            env.Define("Actor", actor);
            script->Run(&env);
        }
    }
    else
    {
        //int cpUsage = psserver->charCreationManager->CalculateCPChoice( upload.choices ) +
        //              psserver->charCreationManager->CalculateCPLife(upload.lifeEvents );
        for(size_t ci = 0; ci < upload.choices.GetSize(); ci++)
        {
            CharCreationManager::CreationChoice* choice = psserver->charCreationManager->FindChoice(upload.choices[ci]);
            if(choice)
            {
                csString name(psserver->charCreationManager->FindChoice(upload.choices[ci])->name.GetData());
                Debug3(LOG_NEWCHAR, me->clientnum,"Choice: %s Creation Script: %s", name.GetData(), choice->eventScript.GetData());

                MathEnvironment env;
                env.Define("Actor", actor);
                if(choice->choiceArea == FATHER_JOB || choice->choiceArea == MOTHER_JOB)
                {
                    int modifier = (choice->choiceArea == FATHER_JOB) ? upload.fatherMod : upload.motherMod;
                    if(modifier > 3 || modifier < 1)
                        modifier = 1;

                    env.Define("ParentStatus", modifier);
                }
                ProgressionScript* script = psserver->GetProgressionManager()->FindScript(choice->eventScript);
                if(script)
                    script->Run(&env);
            }
            else
            {
                Debug2(LOG_NEWCHAR, me->clientnum,"Character Choice %d not found\n", upload.choices[ci]);
            }
        }
        for(size_t li = 0; li < upload.lifeEvents.GetSize(); li++)
        {
            MathEnvironment env;
            env.Define("Actor", actor);
            LifeEventChoiceServer* lifeEvent = psserver->charCreationManager->FindLifeEvent(upload.lifeEvents[li]);
            if(!lifeEvent)
            {
                Error2("No LifeEvent Script found: %d", upload.lifeEvents[li]);
                continue;
            }

            csString scriptName(lifeEvent->eventScript.GetData());
            Debug2(LOG_NEWCHAR, me->clientnum, "LifeEvent Script: %s", scriptName.GetDataSafe());

            ProgressionScript* script = psserver->GetProgressionManager()->FindScript(scriptName);
            if(script)
                script->Run(&env);
        }
    }

    if(!upload.verify)
    {

        if(reservedName == NAME_RESERVED_FOR_YOU)
        {
            AssignScript(chardata);
        }
        // This function recalculates the Max HP, Mana and Stamina of the new character
        chardata->RecalculateStats();

        // Make sure the new player have HP, Mana and Samina that was calculated
        chardata->SetHitPoints(chardata->GetMaxHP().Base());
        chardata->SetMana(chardata->GetMaxMana().Base());
        chardata->SetStamina(chardata->GetMaxPStamina().Base(),true);
        chardata->SetStamina(chardata->GetMaxMStamina().Base(),false);


        psServer::CharacterLoader.SaveCharacterData(chardata, actor);
        Debug1(LOG_NEWCHAR,me->clientnum,"Player Creation Complete");

        // Remove cached objects to make sure that the client gets a fresh character
        // list from the database if it logs out and in within 2 minutes.
        iCachedObject* obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("list",client->GetAccountID().Unbox()));
        if(obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }
        obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("auth",client->GetAccountID().Unbox()));
        if(obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }

        // Here everything is ok
        client->SetPID(chardata->GetPID());
        client->SetName(playerName);

        psCharApprovedMessage app(me->clientnum);
        if(app.valid)
            psserver->GetEventManager()->SendMessage(app.msg);
        else
            Bug2("Could not create valid psCharApprovedMessage for client %u.\n",me->clientnum);
    }
    else
    {
        psCharVerificationMesg mesg(me->clientnum);
        size_t z;
        //unfortunately count goes out of valid area so we need to check on charisma

        for(z = 0; z < psserver->GetCacheManager()->GetSkillAmount(); z++)
        {
            unsigned int rank = chardata->Skills().GetSkillRank((PSSKILL) z).Base();

            psSkillInfo* info = psserver->GetCacheManager()->GetSkillByID(z);
            csString name("Not found");
            if(info)
                name.Replace(info->name);

            if(rank > 0)
            {
                if(z >= PSSKILL_AGI && z <= PSSKILL_WILL)
                {
                    mesg.AddStat(rank, name);
                }
                else
                {
                    mesg.AddSkill(rank, name);
                }
            }
        }
        mesg.Construct();
        mesg.SendMessage();
    }

    delete actor;

    if(!upload.verify)
    {
        // Remove char data from the cache
        iCachedObject* obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("char", chardata->GetPID().Unbox()));
        if(obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }
    }
}
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 ChatManager::HandleChatMessage(MsgEntry *me, Client *client)
{
    psChatMessage msg(me);

    // Dont
    if (!msg.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psChatMessage from client %u.\n",me->clientnum);
        return;
    }

    const char *pType = msg.GetTypeText();

    if (msg.iChatType != CHAT_TELL && msg.iChatType != CHAT_AWAY)
    {
        Debug4(LOG_CHAT, client->GetClientNum(),
                "%s %s: %s\n", client->GetName(),
                pType, msg.sText.GetData());
    }
    else
    {
        Debug5(LOG_CHAT,client->GetClientNum(), "%s %s %s: %s\n", client->GetName(),
               pType, msg.sPerson.GetData(),msg.sText.GetData());
    }

    bool saveFlood = true;

    if (!client->IsMute())
    {
      // Send Chat to other players
      switch (msg.iChatType)
      {
          case CHAT_GUILD:
          {
              SendGuild(client, msg);
              break;
          }
          case CHAT_ALLIANCE:
          {
              SendAlliance(client, msg);
              break;
          }
          case CHAT_GROUP:
          {
              SendGroup(client, msg);
              break;
           }
          case CHAT_AUCTION:
          case CHAT_SHOUT:
          {
              SendShout(client, msg);
              break;
          }
          case CHAT_CHANNEL:
          {
              csArray<uint32_t> subscribed = channelSubscriptions.GetAll(client->GetClientNum());
              bool found = false;
              for(size_t i = 0; i < subscribed.GetSize(); i++)
              {
                  if(subscribed[i] == msg.channelID)
                      found = true;
              }
              if(!found)
              {
                  psserver->SendSystemError(client->GetClientNum(), "You have not yet joined this channel.");
                  break;
              }

              // channel 1 is public
              if(msg.channelID == 1)
                  CPrintf (CON_WARNING, "Gossip %s: %s\n", client->GetName(), msg.sText.GetData());

              psChatMessage newMsg(client->GetClientNum(), client->GetActor()->GetEID(), client->GetName(), 0, msg.sText, msg.iChatType, msg.translate, msg.channelID);

              csArray<uint32_t> subscribers = channelSubscribers.GetAll(msg.channelID);
              csArray<PublishDestination> destArray;
              for (size_t i = 0; i < subscribers.GetSize(); i++)
              {
                  destArray.Push(PublishDestination(subscribers[i], NULL, 0, 0));
                  Client *target = psserver->GetConnections()->Find(subscribers[i]);
                  if (target && target->IsReady())
                      target->GetActor()->LogChatMessage(client->GetActor()->GetFirstName(), newMsg);
              }

              newMsg.Multicast(destArray, 0, PROX_LIST_ANY_RANGE );
              break;
          }
          case CHAT_PET_ACTION:
          {
              gemNPC *pet = NULL;

              // Check if a specific pet's name was specified, in one of these forms:
              // - /mypet Petname ...
              // - /mypet Petname's ...
              size_t numPets = client->GetNumPets();
              for (size_t i = 0; i < numPets; i++)
              {
                  if ((pet = dynamic_cast <gemNPC*>(client->GetPet(i)))
                      && msg.sText.StartsWith(pet->GetCharacterData()->GetCharName(), true))
                  {
                      size_t n = strlen(pet->GetCharacterData()->GetCharName());
                      if (msg.sText.Length() >= n + 1 && msg.sText.GetAt(n) == ' ')
                      {
                          msg.sText.DeleteAt(0, n);
                          msg.sText.LTrim();
                          break;
                      }
                      else if (msg.sText.Length() >= n + 3 && msg.sText.GetAt(n) == '\''
                               && msg.sText.GetAt(n + 1) == 's' && msg.sText.GetAt(n + 2) == ' ')
                      {
                          msg.sText.DeleteAt(0, n);
                          break;
                      }
                  }
                  else pet = NULL;
              }
              // If no particular pet was specified, assume the default familiar...
              if (!pet)
                  pet = dynamic_cast <gemNPC*>(client->GetFamiliar());

              // Send the message or an appropriate error...
              if (!pet)
                  psserver->SendSystemInfo(me->clientnum, "You have no familiar to command.");
              else
                  SendSay(client->GetClientNum(), pet, msg, pet->GetCharacterData()->GetCharFullName());

              break;
          }
          case CHAT_SAY:
          {
              // Send to all if there's no NPC response or the response is public
              SendSay(client->GetClientNum(), client->GetActor(), msg, client->GetName());
              break;
          }
          case CHAT_NPC:
          {
              // Only the speaker sees his successful chatting with an npc.
              // This helps quests stay secret.
              //psChatMessage newMsg(client->GetClientNum(), client->GetName(), 0,
            //    msg.sText, msg.iChatType, msg.translate);
              //newMsg.SendMessage();
              saveFlood = false;

              gemObject *target = client->GetTargetObject();
              gemNPC *targetnpc = dynamic_cast<gemNPC*>(target);
              NpcResponse *resp = CheckNPCResponse(msg,client,targetnpc);
              if (resp)
              {
                  csTicks delay = resp->ExecuteScript(client->GetActor(), targetnpc);
                  if (delay != (csTicks)-1 && resp->menu )
                      resp->menu->ShowMenu(client, delay, targetnpc);
              }
              break;
          }
          case CHAT_AWAY:
          {
              saveFlood = false; //do not check Away messages for flooding
              msg.iChatType = CHAT_TELL; //do regard it as tell message from now on
              //intentionally no break, so it falls through to CHAT_TELL
          }
          case CHAT_TELL:
          {
              if ( msg.sPerson.Length() == 0 )
              {
                  psserver->SendSystemError(client->GetClientNum(), "You must specify name of player.");
                  break;
              }

              Client *target = FindPlayerClient(msg.sPerson);
              if (target && !target->IsSuperClient())
              {
                  if (!target->IsReady())
                      psserver->SendSystemError(client->GetClientNum(), "%s is not ready yet.", msg.sPerson.GetDataSafe());
                  else
                      SendTell(msg, client->GetName(), client, target);
              }
              else
                  psserver->SendSystemError(client->GetClientNum(), "%s is not found online.", msg.sPerson.GetDataSafe());

              break;
          }
          case CHAT_REPORT:
          {
              // First thing to extract the name of the player to log
              csString targetName;
              int index = (int)msg.sText.FindFirst(' ', 0);
              targetName = (index == -1) ? msg.sText : msg.sText.Slice(0, index);
              targetName = NormalizeCharacterName(targetName);

              if ( targetName.Length() == 0 )
              {
                  psserver->SendSystemError(client->GetClientNum(), "You must specify name of player.");
                  break;
              }

              Client * target = psserver->GetConnections()->Find(targetName);
              if ( !target )
              {
                  psserver->SendSystemError(client->GetClientNum(), "%s is not found online.", targetName.GetData());
                  break;
              }
              if (target->IsSuperClient())
              {
                  psserver->SendSystemError(client->GetClientNum(), "Can't report NPCs.");
                  break;
              }

              // Add an active report to the target.
              if (target->GetActor()->AddChatReport(client->GetActor()))
              {
                  // Add report removal event.
                  psserver->GetEventManager()->Push(new psEndChatLoggingEvent(target->GetClientNum(), 300000));
                  psserver->SendSystemInfo(client->GetClientNum(), "Last 5 minutes of %s's chat were logged. Logging will continue for another 5 minutes.", targetName.GetData());
              }
              else
                  psserver->SendSystemError(client->GetClientNum(), "Could not start logging %s, due to a server error.", targetName.GetData());
              break;
         }
         case CHAT_ADVISOR:
         case CHAT_ADVICE:
             break;

         default:
         {
              Error2("Unknown Chat Type: %d\n",msg.iChatType);
              break;
         }
       }
    }
    else
    {
        //User is muted but tries to chat anyway. Remind the user that he/she/it is muted
        psserver->SendSystemInfo(client->GetClientNum(),"You can't send messages because you are muted.");
    }

    if (saveFlood)
        client->FloodControl(msg.iChatType, msg.sText, msg.sPerson);
}