/* * chatmanager.cpp * * Copyright (C) 2003 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include "util/serverconsole.h" #include "cachemanager.h" #include "chatmanager.h" #include "clients.h" #include "playergroup.h" #include "gem.h" #include "util/log.h" #include "util/pserror.h" #include "net/msghandler.h" #include "bulkobjects/psnpcdialog.h" #include "bulkobjects/dictionary.h" #include "bulkobjects/psguildinfo.h" #include "globals.h" #include "psserver.h" #include "npcmanager.h" #include "util/eventmanager.h" #include "util/strutil.h" ChatManager::ChatManager() { psserver->GetEventManager()->Subscribe(this,MSGTYPE_CHAT,REQUIRE_ALIVE); } ChatManager::~ChatManager() { psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_CHAT); } bool ChatManager::FloodControl(csString& newmessage, Client *client) { size_t count=0; //Shovel 1 back client->ShovelFlood(newmessage); //Delete everything older than 10 (or if other defined) secs client->CheckBuffer(); for (int i = 0; i <= (int)(client->GetFloodMax()-1); i++) { if (client->GetFlood(i).str==newmessage) count++; } //Now when the loops is finished, check what count is if (count==client->GetFloodWarn()) { psserver->SendSystemInfo(client->GetClientNum(),"Flood warning. Stop or you will be muted."); } if (count==client->GetFloodMax()) { client->SetMute(true); client->ClearFlood(); psserver->SendSystemInfo(client->GetClientNum(),"BAM! Muted."); return false; } return true; } void ChatManager::HandleMessage(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; } char *pType; msg.GetTypeText(pType); if (msg.iChatType != CHAT_TELL) { Notify4(LOG_CHAT, "%s %s: %s\n", client->GetName(), pType, (const char *) msg.sText); } else Notify5(LOG_CHAT,"%s %s %s: %s\n", client->GetName(), pType, (const char *)msg.sPerson,(const char *)msg.sText); bool saveFlood = true; if (!client->IsMute()) { // Send Chat to other players switch (msg.iChatType) { case CHAT_GUILD: { SendGuild(client, msg); break; } case CHAT_GROUP: { SendGroup(client, msg); break; } case CHAT_AUCTION: case CHAT_SHOUT: { SendShout(client, msg); break; } case CHAT_MY: case CHAT_ME: case CHAT_SAY: { // Send to all if there's no NPC response or the response is public SendSay(client, 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(), msg.sText, msg.iChatType, msg.translate); newMsg.SendMessage(); saveFlood = false; gemObject *target = client->GetTargetObject(); gemNPC *targetnpc = dynamic_cast(target); NpcResponse *resp = CheckNPCResponse(msg,client,targetnpc); if (resp) resp->ExecuteScript(client, targetnpc); break; } case CHAT_TELL: { Client *target=NULL; if ( (target = FindPlayerClient(msg.sPerson, client->GetClientNum()))) { SendTell(msg, client->GetName(), client, target); // Save to chat history client->GetActor()->LogMessage(client->GetActor()->GetName(), msg); target->GetActor()->LogMessage(client->GetActor()->GetName(), msg); } break; } case CHAT_REPORT: { // First thing to extract the name of the player to log csString targetName; int index = (int)msg.sText.FindFirst(' ', 0); if ( index == -1 ) targetName = msg.sText; else targetName = msg.sText.Slice(0, index); targetName = NormalizeCharacterName(targetName); if ( msg.sText.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 online.", targetName.GetData()); break; } if (!client->GetActor()->IsLoggingChat()) { psserver->SendSystemError(client->GetClientNum(), "%s will be logged for five minutes now.", targetName.GetData()); psserver->SendSystemError(target->GetClientNum(), "Your last 5 minutes of chat has been reported to the GMs, logging will now continue."); } else { if (target->GetClientNum() != client->GetActor()->GetReportTargetId()) { psserver->SendSystemError(client->GetClientNum(), "Previous logging is still active."); break; } psserver->SendSystemError(client->GetClientNum(), "Logging for another five minutes."); } client->GetActor()->AddChatReport(target->GetActor()); psserver->GetEventManager()->Push(new psEndChatLoggingEvent(client->GetClientNum(), 300000)); 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) FloodControl(msg.sText,client); } /// TODO: This function is guaranteed not to work atm.-Keith void ChatManager::SendNotice(psChatMessage& msg) { SendSay(NULL, msg, "Server"); } void ChatManager::SendShout(Client *c, psChatMessage& msg) { psChatMessage newMsg(c->GetClientNum(), c->GetName(), msg.sText, msg.iChatType, msg.translate); if (c->GetActor()->GetCharacterData()->GetTotalOnlineTime() > 3600) { csArray& clients = c->GetActor()->GetMulticastClients(); newMsg.Multicast(clients, 0, PROX_LIST_ANY_RANGE ); // The message is saved to the chat history of all the clients around for (size_t i = 0; i < clients.Length(); i++) { Client *target = psserver->GetConnections()->Find(clients[i].client); if (target && target->IsReady()) target->GetActor()->LogMessage(c->GetActor()->GetName(), newMsg); } } else { psserver->SendSystemError(c->GetClientNum(), "You are not allowed to shout or auction until you have been in-game for at least 1 hour."); psserver->SendSystemInfo(c->GetClientNum(), "Please use the Help tab or /advisor if you need help."); } } void ChatManager::SendSay(Client *p, psChatMessage& msg,const char* who) { if (p) { psChatMessage newMsg(p->GetClientNum(), who, msg.sText, msg.iChatType, msg.translate); csArray& clients = p->GetActor()->GetMulticastClients(); newMsg.Multicast(clients, 0, CHAT_SAY_RANGE ); // The message is saved to the chat history of all the clients around for (size_t i = 0; i < clients.Length(); i++) { Client *target = psserver->GetConnections()->Find(clients[i].client); if (target && target->IsReady() && clients[i].dist < CHAT_SAY_RANGE) target->GetActor()->LogMessage(p->GetActor()->GetName(), newMsg); } } } void ChatManager::SendGuild(Client *client, psChatMessage& msg) { psGuildInfo * guild; psGuildLevel * level; guild = client->GetCharacterData()->GetGuild(); if (guild == NULL) { psserver->SendSystemInfo(client->GetClientNum(), "You are not in a guild."); return; } level = client->GetCharacterData()->GetGuildLevel(); if (level!=NULL && level->HasRights(RIGHTS_CHAT)==false) { psserver->SendSystemInfo(client->GetClientNum(), "You are not allowed to use chat channel of your guild."); return; } SendGuild(client->GetName(), guild, msg); } void ChatManager::SendGuild(const csString & sender, psGuildInfo * guild, psChatMessage& msg) { ClientIterator iter(*psserver->GetConnections() ); Client * client; psGuildLevel * level; for (client = iter.First(); client != NULL; client = iter.Next()) { if (client->GetGuildID() == guild->id) { level = client->GetCharacterData()->GetGuildLevel(); if (level!=NULL && level->HasRights(RIGHTS_VIEW_CHAT)) { psChatMessage newMsg(client->GetClientNum(), sender, msg.sText, msg.iChatType, msg.translate); newMsg.SendMessage(); } } } } void ChatManager::SendGroup(Client * client, psChatMessage& msg) { csRef group = client->GetActor()->GetGroup(); if (group) { psChatMessage newMsg(0, client->GetName(), msg.sText, msg.iChatType, msg.translate); group->Broadcast(newMsg.msg); } else { psserver->SendSystemInfo(client->GetClientNum(), "You are not part of any group."); } } void ChatManager::SendTell(psChatMessage& msg, const char* who,Client *client,Client *p) { Debug1(LOG_CHAT,client->GetClientNum(),"SendTell!\n"); // Sanity check that we are sending to correct clientnum! csString targetName = msg.sPerson; NormalizeCharacterName(targetName); CS_ASSERT(strcasecmp(p->GetName(), targetName) == 0); // Create a new message and send it to that person if found psChatMessage cmsg(p->GetClientNum(), who, msg.sText, msg.iChatType, msg.translate); cmsg.SendMessage(); // Echo the message back to the speaker also psChatMessage cmsg2(client->GetClientNum(), msg.sPerson, msg.sText, CHAT_TELLSELF, msg.translate); cmsg2.SendMessage(); } #define MAX_NPC_DIALOG_DIST 5 NpcResponse *ChatManager::CheckNPCEvent(Client *client,csString& triggerText,gemNPC * &target) { gemNPC *npc = target; if (npc && npc->IsAlive()) { csString trigger(triggerText); trigger.Downcase(); psNPCDialog *npcdlg = npc->GetNPCDialogPtr(); if (npcdlg) // if NULL, then NPC never speaks { float dist = npc->RangeTo( client->GetActor() ); if (dist > MAX_NPC_DIALOG_DIST) return NULL; Notify3(LOG_NPC, "%s checking trigger %s.\n",target->GetName(),trigger.GetData() ); return npcdlg->Respond(trigger,client); } else Notify2(LOG_NPC,"NPC %s cannot speak.\n",npc->GetName() ); // can comment this out later } return NULL; } NpcResponse *ChatManager::CheckNPCResponse(psChatMessage& msg,Client *client,gemNPC * &target) { return CheckNPCEvent(client,msg.sText,target); // } psEndChatLoggingEvent::psEndChatLoggingEvent(uint32_t _clientnum, const int delayticks=5000) : psGameEvent(0,delayticks,"psEndChatLoggingEvent") { #ifdef _psEndChatLoggingEvent_DEBUG_ CPrintf(CON_DEBUG, "EndOfChatLoggingEvent created for clientnum %i!", _clientnum); #endif clientnum = _clientnum; } void psEndChatLoggingEvent::Trigger() { #ifdef _psEndChatLoggingEvent_DEBUG_ CPrintf(CON_DEBUG, "EndOfChatLoggingEvent is about to happen on clientnum %i!", clientnum); #endif Client *client = NULL; client = psserver->GetConnections()->Find(clientnum); if (!client) { #ifdef _psEndChatLoggingEvent_DEBUG_ CPrintf(CON_DEBUG, "EndOfChatLoggingEvent on unknown client!"); #endif return; } client->GetActor()->RemoveChatReport(); }