Added global pre and post forwards for client chat (bug 5394, r=psychonic).
authorKyle Sanderson <kyle.leet@gmail.com>
Mon Aug 05 11:46:29 2013 -0400 (2013-08-05)
changeset 39749b3779f472d4
parent 3973 5c5ed57cca88
child 3975 835659a15ce3
Added global pre and post forwards for client chat (bug 5394, r=psychonic).
core/ChatTriggers.cpp
core/ChatTriggers.h
plugins/basecomm.sp
plugins/basetriggers.sp
plugins/include/console.inc
     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[]);