1 #include "ChatTriggers.h"
2 #include "sm_stringutil.h"
3 #include "TextParsers.h"
4 #include "ConCmdManager.h"
6 /* :HACKHACK: We can't SH_DECL here because ConCmdManager.cpp does */
7 extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
9 #if SH_IMPL_VERSION >= 4
10 extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
12 extern bool __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
15 ChatTriggers g_ChatTriggers;
17 ChatTriggers::ChatTriggers() : m_pSayCmd(NULL), m_bWillProcessInPost(false),
18 m_ReplyTo(SM_REPLY_CONSOLE)
20 m_PubTrigger = sm_strdup("!");
21 m_PrivTrigger = sm_strdup("/");
23 m_PrivTriggerSize = 1;
26 ChatTriggers::~ChatTriggers()
28 delete [] m_PubTrigger;
30 delete [] m_PrivTrigger;
34 ConfigResult ChatTriggers::OnSourceModConfigChanged(const char *key,
40 if (strcmp(key, "PublicChatTrigger") == 0)
42 delete [] m_PubTrigger;
43 m_PubTrigger = sm_strdup(value);
44 m_PubTriggerSize = strlen(m_PubTrigger);
45 return ConfigResult_Accept;
46 } else if (strcmp(key, "SilentChatTrigger") == 0) {
47 delete [] m_PrivTrigger;
48 m_PrivTrigger = sm_strdup(value);
49 m_PrivTriggerSize = strlen(m_PrivTrigger);
50 return ConfigResult_Accept;
53 return ConfigResult_Ignore;
56 void ChatTriggers::OnSourceModGameInitialized()
58 unsigned int total = 2;
59 ConCommandBase *pCmd = icvar->GetCommands();
63 if (pCmd->IsCommand())
65 name = pCmd->GetName();
66 if (!m_pSayCmd && strcmp(name, "say") == 0)
68 m_pSayCmd = (ConCommand *)pCmd;
73 } else if (!m_pSayTeamCmd && strcmp(name, "say_team") == 0) {
74 m_pSayTeamCmd = (ConCommand *)pCmd;
81 pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
86 SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
87 SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
91 SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
92 SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
96 void ChatTriggers::OnSourceModShutdown()
100 SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
101 SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
105 SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
106 SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
110 void ChatTriggers::OnSayCommand_Pre()
112 int client = g_ConCmds.GetCommandClient();
114 /* The server console cannot do this */
117 RETURN_META(MRES_IGNORED);
120 const char *args = engine->Cmd_Args();
122 /* Handle quoted string sets */
123 bool is_quoted = false;
130 bool is_trigger = false;
131 bool is_silent = false;
133 /* Check for either trigger */
134 if (m_PubTriggerSize && strncmp(args, m_PubTrigger, m_PubTriggerSize) == 0)
137 } else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0) {
144 RETURN_META(MRES_IGNORED);
147 /* If we're a public command, process later */
150 /* We have to process this in _post_ instead. Darn. */
151 m_bWillProcessInPost = true;
152 RETURN_META(MRES_IGNORED);
155 /* Otherwise, process now */
156 if (ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PrivTriggerSize], is_quoted))
158 /* If we succeed, block the original say! */
159 RETURN_META(MRES_SUPERCEDE);
162 RETURN_META(MRES_IGNORED);
165 void ChatTriggers::OnSayCommand_Post()
167 if (m_bWillProcessInPost)
169 /* Reset this for re-entrancy */
170 m_bWillProcessInPost = false;
172 /* Get our arguments */
173 const char *args = engine->Cmd_Args();
175 /* Handle quotations */
176 bool is_quoted = false;
183 int client = g_ConCmds.GetCommandClient();
185 ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PubTriggerSize], is_quoted);
189 bool ChatTriggers::ProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
191 /* Eat up whitespace */
192 while (*args != '\0' && IsWhitespace(args))
197 /* Check if we're still valid */
203 /* Extract a command. This is kind of sloppy. */
206 const char *inptr = args;
207 while (*inptr != '\0'
208 && !IsWhitespace(inptr)
209 && cmd_len < sizeof(cmd_buf) - 1)
211 cmd_buf[cmd_len++] = *inptr++;
213 cmd_buf[cmd_len] = '\0';
215 /* See if we have this registered */
216 bool prepended = false;
217 if (!g_ConCmds.LookForSourceModCommand(cmd_buf))
219 /* Check if we had an "sm_" prefix */
220 if (strncmp(cmd_buf, "sm_", 3) == 0)
225 /* Now, prepend. Don't worry about the buffers. This will
226 * work because the sizes are limited from earlier.
229 strcpy(new_buf, "sm_");
230 strncopy(&new_buf[3], cmd_buf, sizeof(new_buf)-3);
233 if (!g_ConCmds.LookForSourceModCommand(new_buf))
241 /* See if we need to do extra string manipulation */
242 if (is_quoted || prepended)
244 static char buffer[300];
247 /* Check if we need to prepend sm_ */
250 len = UTIL_Format(buffer, sizeof(buffer), "sm_%s", args);
252 len = strncopy(buffer, args, sizeof(buffer));
255 /* Check if we need to strip a quote */
258 if (buffer[len-1] == '"')
260 buffer[--len] = '\0';
267 /* Finally, execute! */
268 unsigned int old = SetReplyTo(SM_REPLY_CHAT);
269 serverpluginhelpers->ClientCommand(pEdict, args);
275 unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
277 unsigned int old = m_ReplyTo;
284 unsigned int ChatTriggers::GetReplyTo()