Added global pre and post forwards for client chat (bug 5394, r=psychonic).
1.1 --- a/core/ChatTriggers.cpp Mon Aug 05 11:36:59 2013 -0400
1.2 +++ b/core/ChatTriggers.cpp Mon Aug 05 11:46:29 2013 -0400
1.3 @@ -63,6 +63,10 @@
1.4 m_PubTriggerSize = 1;
1.5 m_PrivTriggerSize = 1;
1.6 m_bIsChatTrigger = false;
1.7 + m_bPluginIgnored = false;
1.8 +#if SOURCE_ENGINE == SE_EPISODEONE
1.9 + m_bIsINS = false;
1.10 +#endif
1.11 }
1.12
1.13 ChatTriggers::~ChatTriggers()
1.14 @@ -106,6 +110,8 @@
1.15 {
1.16 m_pShouldFloodBlock = g_Forwards.CreateForward("OnClientFloodCheck", ET_Event, 1, NULL, Param_Cell);
1.17 m_pDidFloodBlock = g_Forwards.CreateForward("OnClientFloodResult", ET_Event, 2, NULL, Param_Cell, Param_Cell);
1.18 + m_pOnClientSayCmd = g_Forwards.CreateForward("OnClientSayCommand", ET_Event, 3, NULL, Param_Cell, Param_String, Param_String);
1.19 + m_pOnClientSayCmd_Post = g_Forwards.CreateForward("OnClientSayCommand_Post", ET_Ignore, 3, NULL, Param_Cell, Param_String, Param_String);
1.20 }
1.21
1.22 void ChatTriggers::OnSourceModAllInitialized_Post()
1.23 @@ -128,23 +134,62 @@
1.24 SH_ADD_HOOK(ConCommand, Dispatch, m_pSayTeamCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.25 SH_ADD_HOOK(ConCommand, Dispatch, m_pSayTeamCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.26 }
1.27 +
1.28 +#if SOURCE_ENGINE == SE_EPISODEONE
1.29 + m_bIsINS = (strncmp(g_SourceMod.GetGameFolderName(), "insurgency") == 0);
1.30 +
1.31 + if (m_bIsINS)
1.32 + {
1.33 + m_pSay2Cmd = FindCommand("say2");
1.34 + if (m_pSay2Cmd)
1.35 + {
1.36 + SH_ADD_HOOK(ConCommand, Dispatch, m_pSay2Cmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.37 + SH_ADD_HOOK(ConCommand, Dispatch, m_pSay2Cmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.38 + }
1.39 + }
1.40 +#elif SOURCE_ENGINE == SE_NUCLEARDAWN
1.41 + m_pSaySquadCmd = FindCommand("say_squad");
1.42 +
1.43 + if (m_pSaySquadCmd)
1.44 + {
1.45 + SH_ADD_HOOK(ConCommand, Dispatch, m_pSaySquadCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.46 + SH_ADD_HOOK(ConCommand, Dispatch, m_pSaySquadCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.47 + }
1.48 +#endif
1.49 }
1.50
1.51 void ChatTriggers::OnSourceModShutdown()
1.52 {
1.53 - if (m_pSayTeamCmd)
1.54 - {
1.55 - SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSayTeamCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.56 - SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSayTeamCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.57 - }
1.58 if (m_pSayCmd)
1.59 {
1.60 SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSayCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.61 SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSayCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.62 }
1.63
1.64 + if (m_pSayTeamCmd)
1.65 + {
1.66 + SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSayTeamCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.67 + SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSayTeamCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.68 + }
1.69 +
1.70 +#if SOURCE_ENGINE == SE_EPISODEONE
1.71 + if (m_bIsINS && m_pSay2Cmd)
1.72 + {
1.73 + SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSay2Cmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.74 + SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSay2Cmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.75 + }
1.76 +#elif SOURCE_ENGINE == SE_NUCLEARDAWN
1.77 + if (m_pSaySquadCmd)
1.78 + {
1.79 + SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSaySquadCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Pre), false);
1.80 + SH_REMOVE_HOOK(ConCommand, Dispatch, m_pSaySquadCmd, SH_MEMBER(this, &ChatTriggers::OnSayCommand_Post), true);
1.81 + }
1.82 +#endif
1.83 +
1.84 g_Forwards.ReleaseForward(m_pShouldFloodBlock);
1.85 g_Forwards.ReleaseForward(m_pDidFloodBlock);
1.86 + g_Forwards.ReleaseForward(m_pOnClientSayCmd);
1.87 + g_Forwards.ReleaseForward(m_pOnClientSayCmd_Post);
1.88 }
1.89
1.90 #if SOURCE_ENGINE == SE_DOTA
1.91 @@ -158,21 +203,21 @@
1.92 {
1.93 CCommand command;
1.94 #endif
1.95 - int client;
1.96 - CPlayer *pPlayer;
1.97 -
1.98 - client = g_ConCmds.GetCommandClient();
1.99 + int client = g_ConCmds.GetCommandClient();
1.100 m_bIsChatTrigger = false;
1.101 m_bWasFloodedMessage = false;
1.102 + m_bPluginIgnored = false;
1.103
1.104 /* The server console cannot do this */
1.105 - if (client == 0 || (pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
1.106 + if (client == 0)
1.107 {
1.108 RETURN_META(MRES_IGNORED);
1.109 }
1.110
1.111 + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
1.112 +
1.113 /* We guarantee the client is connected */
1.114 - if (!pPlayer->IsConnected())
1.115 + if (!pPlayer || !pPlayer->IsConnected())
1.116 {
1.117 RETURN_META(MRES_IGNORED);
1.118 }
1.119 @@ -211,6 +256,14 @@
1.120 is_quoted = true;
1.121 }
1.122
1.123 + const char * pCommandName = command.Arg(0);
1.124 +#if SOURCE_ENGINE == SE_EPISODEONE
1.125 + if (m_bIsINS && strncmp(pCommandName, "say2") && strlen(args) >= 4)
1.126 + {
1.127 + args += 4;
1.128 + }
1.129 +#endif
1.130 +
1.131 bool is_trigger = false;
1.132 bool is_silent = false;
1.133
1.134 @@ -227,38 +280,37 @@
1.135 args = &args[m_PrivTriggerSize];
1.136 }
1.137
1.138 - if (!is_trigger)
1.139 - {
1.140 - RETURN_META(MRES_IGNORED);
1.141 - }
1.142 -
1.143 /**
1.144 * Test if this is actually a command!
1.145 */
1.146 - if (!PreProcessTrigger(PEntityOfEntIndex(client), args, is_quoted))
1.147 + if (is_trigger && PreProcessTrigger(PEntityOfEntIndex(client), args, is_quoted))
1.148 {
1.149 - CPlayer *pPlayer;
1.150 - if (is_silent
1.151 - && g_bSupressSilentFails
1.152 - && client != 0
1.153 - && (pPlayer = g_Players.GetPlayerByIndex(client)) != NULL
1.154 - && pPlayer->GetAdminId() != INVALID_ADMIN_ID)
1.155 + m_bIsChatTrigger = true;
1.156 +
1.157 + /**
1.158 + * We'll execute it in post.
1.159 + */
1.160 + m_bWillProcessInPost = true;
1.161 + m_bTriggerWasSilent = is_silent;
1.162 + }
1.163 +
1.164 + if (m_pOnClientSayCmd->GetFunctionCount() != 0)
1.165 + {
1.166 + cell_t res = Pl_Continue;
1.167 + m_pOnClientSayCmd->PushCell(client);
1.168 + m_pOnClientSayCmd->PushString(pCommandName);
1.169 + m_pOnClientSayCmd->PushString(command.ArgS());
1.170 + m_pOnClientSayCmd->Execute(&res);
1.171 +
1.172 + if (res >= Pl_Handled)
1.173 {
1.174 + m_bPluginIgnored = (res >= Pl_Stop);
1.175 RETURN_META(MRES_SUPERCEDE);
1.176 }
1.177 - RETURN_META(MRES_IGNORED);
1.178 }
1.179
1.180 - m_bIsChatTrigger = true;
1.181 -
1.182 - /**
1.183 - * We'll execute it in post.
1.184 - */
1.185 - m_bWillProcessInPost = true;
1.186 - m_bTriggerWasSilent = is_silent;
1.187 -
1.188 - /* If we're silent, block */
1.189 - if (is_silent)
1.190 + if (m_bWillProcessInPost || \
1.191 + (is_silent && g_bSupressSilentFails && pPlayer->GetAdminId() != INVALID_ADMIN_ID))
1.192 {
1.193 RETURN_META(MRES_SUPERCEDE);
1.194 }
1.195 @@ -275,15 +327,14 @@
1.196 void ChatTriggers::OnSayCommand_Post()
1.197 #endif
1.198 {
1.199 - m_bIsChatTrigger = false;
1.200 - m_bWasFloodedMessage = false;
1.201 + int client = g_ConCmds.GetCommandClient();
1.202 +
1.203 if (m_bWillProcessInPost)
1.204 {
1.205 /* Reset this for re-entrancy */
1.206 m_bWillProcessInPost = false;
1.207
1.208 /* Execute the cached command */
1.209 - int client = g_ConCmds.GetCommandClient();
1.210 unsigned int old = SetReplyTo(SM_REPLY_CHAT);
1.211 #if SOURCE_ENGINE == SE_DOTA
1.212 engine->ClientCommand(client, "%s", m_ToExecute);
1.213 @@ -292,6 +343,21 @@
1.214 #endif
1.215 SetReplyTo(old);
1.216 }
1.217 +
1.218 + if (m_bPluginIgnored)
1.219 + {
1.220 + m_bPluginIgnored = false;
1.221 + }
1.222 + else if (!m_bWasFloodedMessage && m_pOnClientSayCmd_Post->GetFunctionCount() != 0)
1.223 + {
1.224 + m_pOnClientSayCmd_Post->PushCell(client);
1.225 + m_pOnClientSayCmd_Post->PushString(command.Arg(0));
1.226 + m_pOnClientSayCmd_Post->PushString(command.ArgS());
1.227 + m_pOnClientSayCmd_Post->Execute(NULL);
1.228 + }
1.229 +
1.230 + m_bIsChatTrigger = false;
1.231 + m_bWasFloodedMessage = false;
1.232 }
1.233
1.234 bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
2.1 --- a/core/ChatTriggers.h Mon Aug 05 11:36:59 2013 -0400
2.2 +++ b/core/ChatTriggers.h Mon Aug 05 11:46:29 2013 -0400
2.3 @@ -75,6 +75,11 @@
2.4 private:
2.5 ConCommand *m_pSayCmd;
2.6 ConCommand *m_pSayTeamCmd;
2.7 +#if SOURCE_ENGINE == SE_EPISODEONE
2.8 + ConCommand *m_pSay2Cmd;
2.9 +#elif SOURCE_ENGINE == SE_NUCLEARDAWN
2.10 + ConCommand *m_pSaySquadCmd;
2.11 +#endif
2.12 char *m_PubTrigger;
2.13 size_t m_PubTriggerSize;
2.14 char *m_PrivTrigger;
2.15 @@ -83,10 +88,16 @@
2.16 bool m_bTriggerWasSilent;
2.17 bool m_bIsChatTrigger;
2.18 bool m_bWasFloodedMessage;
2.19 + bool m_bPluginIgnored;
2.20 unsigned int m_ReplyTo;
2.21 char m_ToExecute[300];
2.22 IForward *m_pShouldFloodBlock;
2.23 IForward *m_pDidFloodBlock;
2.24 + IForward *m_pOnClientSayCmd;
2.25 + IForward *m_pOnClientSayCmd_Post;
2.26 +#if SOURCE_ENGINE == SE_EPISODEONE
2.27 + bool m_bIsINS;
2.28 +#endif
2.29 };
2.30
2.31 extern ChatTriggers g_ChatTriggers;
3.1 --- a/plugins/basecomm.sp Mon Aug 05 11:36:59 2013 -0400
3.2 +++ b/plugins/basecomm.sp Mon Aug 05 11:46:29 2013 -0400
3.3 @@ -81,9 +81,6 @@
3.4 g_Cvar_Deadtalk = CreateConVar("sm_deadtalk", "0", "Controls how dead communicate. 0 - Off. 1 - Dead players ignore teams. 2 - Dead players talk to living teammates.", 0, true, 0.0, true, 2.0);
3.5 g_Cvar_Alltalk = FindConVar("sv_alltalk");
3.6
3.7 - AddCommandListener(Command_Say, "say");
3.8 - AddCommandListener(Command_Say, "say_team");
3.9 -
3.10 RegAdminCmd("sm_mute", Command_Mute, ADMFLAG_CHAT, "sm_mute <player> - Removes a player's ability to use voice.");
3.11 RegAdminCmd("sm_gag", Command_Gag, ADMFLAG_CHAT, "sm_gag <player> - Removes a player's ability to use chat.");
3.12 RegAdminCmd("sm_silence", Command_Silence, ADMFLAG_CHAT, "sm_silence <player> - Removes a player's ability to use voice or chat.");
3.13 @@ -154,13 +151,13 @@
3.14 return true;
3.15 }
3.16
3.17 -public Action:Command_Say(client, const String:command[], args)
3.18 +public Action:OnClientSayCommand(client, const String:command[], const String:sArgs[])
3.19 {
3.20 if (client)
3.21 {
3.22 if (g_Gagged[client])
3.23 {
3.24 - return Plugin_Handled;
3.25 + return Plugin_Stop;
3.26 }
3.27 }
3.28
4.1 --- a/plugins/basetriggers.sp Mon Aug 05 11:36:59 2013 -0400
4.2 +++ b/plugins/basetriggers.sp Mon Aug 05 11:46:29 2013 -0400
4.3 @@ -75,10 +75,6 @@
4.4 g_Cvar_TimeleftInterval = CreateConVar("sm_timeleft_interval", "0.0", "Display timeleft every x seconds. Default 0.", 0, true, 0.0, true, 1800.0);
4.5 g_Cvar_FriendlyFire = FindConVar("mp_friendlyfire");
4.6
4.7 - AddCommandListener(Command_Say, "say");
4.8 - AddCommandListener(Command_Say, "say2");
4.9 - AddCommandListener(Command_Say, "say_team");
4.10 -
4.11 RegConsoleCmd("timeleft", Command_Timeleft);
4.12 RegConsoleCmd("nextmap", Command_Nextmap);
4.13 RegConsoleCmd("motd", Command_Motd);
4.14 @@ -236,22 +232,22 @@
4.15 return Plugin_Handled;
4.16 }
4.17
4.18 -public Action:Command_Say(client, const String:command[], argc)
4.19 +public OnClientSayCommand_Post(client, const String:command[], const String:sArgs[])
4.20 {
4.21 decl String:text[192];
4.22 new startidx = 0;
4.23 - if (GetCmdArgString(text, sizeof(text)) < 1)
4.24 +
4.25 + if (strcopy(text, sizeof(text), sArgs) < 1)
4.26 {
4.27 - return Plugin_Continue;
4.28 + return;
4.29 }
4.30
4.31 - if (text[strlen(text)-1] == '"')
4.32 + if (text[0] == '"')
4.33 {
4.34 - text[strlen(text)-1] = '\0';
4.35 startidx = 1;
4.36 }
4.37
4.38 - if (strcmp(command, "say2", false) == 0)
4.39 + if ((strcmp(command, "say2", false) == 0) && strlen(sArgs) >= 4)
4.40 startidx += 4;
4.41
4.42 if (strcmp(text[startidx], "timeleft", false) == 0)
4.43 @@ -342,8 +338,6 @@
4.44 {
4.45 ShowMOTDPanel(client, "Message Of The Day", "motd", MOTDPANEL_TYPE_INDEX);
4.46 }
4.47 -
4.48 - return Plugin_Continue;
4.49 }
4.50
4.51 ShowTimeLeft(client, who)
5.1 --- a/plugins/include/console.inc Mon Aug 05 11:36:59 2013 -0400
5.2 +++ b/plugins/include/console.inc Mon Aug 05 11:46:29 2013 -0400
5.3 @@ -938,3 +938,24 @@
5.4 */
5.5 native RemoveCommandListener(CommandListener:callback, const String:command[]="");
5.6
5.7 +/**
5.8 + * Global listener for the chat commands.
5.9 + *
5.10 + * @param client Client index.
5.11 + * @param command Command name.
5.12 + * @param sArgs Chat argument string.
5.13 + *
5.14 + * @return An Action value. Returning Plugin_Handled bypasses the game function call.
5.15 + Returning Plugin_Stop bypasses the post hook as well as the game function.
5.16 + */
5.17 +forward Action:OnClientSayCommand(client, const String:command[], const String:sArgs[]);
5.18 +
5.19 +/**
5.20 + * Global post listener for the chat commands.
5.21 + *
5.22 + * @param client Client index.
5.23 + * @param command Command name.
5.24 + * @param sArgs Chat argument string.
5.25 + *
5.26 + */
5.27 +forward OnClientSayCommand_Post(client, const String:command[], const String:sArgs[]);