diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 2bad9d21c..1b9e99a5b 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -698,6 +698,11 @@ void CGameContext::ConMe(IConsole::IResult *pResult, void *pUserData) "/me is disabled on this server, admin can enable it by using sv_slash_me"); } +void CGameContext::ConWhisper(IConsole::IResult *pResult, void *pUserData) +{ + // This will never be called +} + void CGameContext::ConSetEyeEmote(IConsole::IResult *pResult, void *pUserData) { diff --git a/src/game/server/ddracechat.h b/src/game/server/ddracechat.h index eb504bc84..87ec03696 100644 --- a/src/game/server/ddracechat.h +++ b/src/game/server/ddracechat.h @@ -13,6 +13,8 @@ CHAT_COMMAND("settings", "?s", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSettings, this, " CHAT_COMMAND("help", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConHelp, this, "Shows help to command r, general help if left blank") CHAT_COMMAND("info", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConInfo, this, "Shows info about this server") CHAT_COMMAND("me", "r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMe, this, "Like the famous irc command '/me says hi' will display ' says hi'") +CHAT_COMMAND("w", "sr", CFGFLAG_CHAT|CFGFLAG_SERVER, ConWhisper, this, "Whisper something to someone (private message)") +CHAT_COMMAND("whisper", "sr", CFGFLAG_CHAT|CFGFLAG_SERVER, ConWhisper, this, "Whisper something to someone (private message)") CHAT_COMMAND("pause", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTogglePause, this, "Toggles pause (if not activated on the server, it toggles spec)") CHAT_COMMAND("spec", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConToggleSpec, this, "Toggles spec") CHAT_COMMAND("rankteam", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTeamRank, this, "Shows the team rank of player with name r (your team rank by default)") diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 16c19e4be..71806f622 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -805,27 +805,42 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) *pMessage = ' '; pMessage++; } - if(pMsg->m_pMessage[0]=='/') // TODO: Add spam protection + if(pMsg->m_pMessage[0]=='/') { - m_ChatResponseTargetID = ClientID; - Server()->RestrictRconOutput(ClientID); - Console()->SetFlagMask(CFGFLAG_CHAT); - - if (pPlayer->m_Authed) - Console()->SetAccessLevel(pPlayer->m_Authed == CServer::AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD); + if (pMsg->m_pMessage[1]=='w' && pMsg->m_pMessage[2]==' ') + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 3, 256); + Whisper(pPlayer->GetCID(), pWhisperMsg); + } + else if (str_comp(pMsg->m_pMessage+1, "whisper ") == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 9, 256); + Whisper(pPlayer->GetCID(), pWhisperMsg); + } else - Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_USER); - Console()->SetPrintOutputLevel(m_ChatPrintCBIndex, 0); + { + m_ChatResponseTargetID = ClientID; + Server()->RestrictRconOutput(ClientID); + Console()->SetFlagMask(CFGFLAG_CHAT); - Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID); - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "%d used %s", ClientID, pMsg->m_pMessage); - Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "chat-command", aBuf); + if (pPlayer->m_Authed) + Console()->SetAccessLevel(pPlayer->m_Authed == CServer::AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD); + else + Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_USER); + Console()->SetPrintOutputLevel(m_ChatPrintCBIndex, 0); - Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); - Console()->SetFlagMask(CFGFLAG_SERVER); - m_ChatResponseTargetID = -1; - Server()->RestrictRconOutput(-1); + Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%d used %s", ClientID, pMsg->m_pMessage); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "chat-command", aBuf); + + Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); + Console()->SetFlagMask(CFGFLAG_SERVER); + m_ChatResponseTargetID = -1; + Server()->RestrictRconOutput(-1); + } } else SendChat(ClientID, Team, pMsg->m_pMessage, ClientID); @@ -2214,3 +2229,101 @@ void CGameContext::ResetTuning() Tuning()->Set("shotgun_curvature", 0); SendTuningParams(-1); } + +bool CheckClientID2(int ClientID) +{ + dbg_assert(ClientID >= 0 || ClientID < MAX_CLIENTS, + "The Client ID is wrong"); + if (ClientID < 0 || ClientID >= MAX_CLIENTS) + return false; + return true; +} + +void CGameContext::Whisper(int ClientID, char *pStr) +{ + char *pName; + char *pMessage; + int Error = 0; + + pStr = str_skip_whitespaces(pStr); + + // add token + if(*pStr == '"') + { + pStr++; + + pName = pStr; // we might have to process escape data + while(1) + { + if(pStr[0] == '"') + break; + else if(pStr[0] == '\\') + { + if(pStr[1] == '\\') + pStr++; // skip due to escape + else if(pStr[1] == '"') + pStr++; // skip due to escape + } + else if(pStr[0] == 0) + Error = 1; + + pStr++; + } + + // write null termination + *pStr = 0; + pStr++; + } + else + { + pName = pStr; + while(1) + { + if(pStr[0] == ' ') + { + break; + } + pStr++; + } + } + + if(pStr[0] != ' ') + { + Error = 1; + } + + *pStr = 0; + pStr++; + + pMessage = pStr; + + if (!CheckClientID2(ClientID)) + return; + + int Victim; + for(Victim = 0; Victim < MAX_CLIENTS; Victim++) + if (str_comp(pName, Server()->ClientName(Victim)) == 0) + break; + + char aBuf[256]; + + if (Error) + { + str_format(aBuf, sizeof(aBuf), "Invalid whisper"); + SendChatTarget(ClientID, aBuf); + return; + } + + if (Victim >= MAX_CLIENTS || !CheckClientID2(Victim)) + { + str_format(aBuf, sizeof(aBuf), "No player with name \"%s\" found", pName); + SendChatTarget(ClientID, aBuf); + return; + } + + str_format(aBuf, sizeof(aBuf), "[← %s] %s", Server()->ClientName(ClientID), pMessage); + SendChatTarget(Victim, aBuf); + + str_format(aBuf, sizeof(aBuf), "[→ %s] %s", Server()->ClientName(Victim), pMessage); + SendChatTarget(ClientID, aBuf); +} diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 988e3506b..573ce27c4 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -242,6 +242,7 @@ private: static void ConBroadTime(IConsole::IResult *pResult, void *pUserData); static void ConJoinTeam(IConsole::IResult *pResult, void *pUserData); static void ConMe(IConsole::IResult *pResult, void *pUserData); + static void ConWhisper(IConsole::IResult *pResult, void *pUserData); static void ConSetEyeEmote(IConsole::IResult *pResult, void *pUserData); static void ConToggleBroadcast(IConsole::IResult *pResult, void *pUserData); static void ConEyeEmote(IConsole::IResult *pResult, void *pUserData); @@ -273,6 +274,7 @@ private: CMute m_aMutes[MAX_MUTES]; int m_NumMutes; void Mute(IConsole::IResult *pResult, NETADDR *Addr, int Secs, const char *pDisplayName); + void Whisper(int ClientID, char *pStr); public: CLayers *Layers() { return &m_Layers; }