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_bTriggerWasSilent(false), 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 args = &args[m_PubTriggerSize];
138 } else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0) {
141 args = &args[m_PrivTriggerSize];
146 RETURN_META(MRES_IGNORED);
150 * Test if this is actually a command!
152 if (!PreProcessTrigger(engine->PEntityOfEntIndex(client), args, is_quoted))
154 RETURN_META(MRES_IGNORED);
158 * We'll execute it in post.
160 m_bWillProcessInPost = true;
161 m_bTriggerWasSilent = is_silent;
163 /* If we're silent, block */
166 RETURN_META(MRES_SUPERCEDE);
169 /* Otherwise, let the command continue */
170 RETURN_META(MRES_IGNORED);
173 void ChatTriggers::OnSayCommand_Post()
175 if (m_bWillProcessInPost)
177 /* Reset this for re-entrancy */
178 m_bWillProcessInPost = false;
180 /* Execute the cached command */
181 int client = g_ConCmds.GetCommandClient();
182 unsigned int old = SetReplyTo(SM_REPLY_CHAT);
183 serverpluginhelpers->ClientCommand(engine->PEntityOfEntIndex(client), m_ToExecute);
188 bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
190 /* Eat up whitespace */
191 while (*args != '\0' && IsWhitespace(args))
196 /* Check if we're still valid */
202 /* Extract a command. This is kind of sloppy. */
205 const char *inptr = args;
206 while (*inptr != '\0'
207 && !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)
246 /* Check if we need to prepend sm_ */
249 len = UTIL_Format(m_ToExecute, sizeof(m_ToExecute), "sm_%s", args);
251 len = strncopy(m_ToExecute, args, sizeof(m_ToExecute));
254 /* Check if we need to strip a quote */
257 if (m_ToExecute[len-1] == '"')
259 m_ToExecute[--len] = '\0';
263 strncopy(m_ToExecute, args, sizeof(m_ToExecute));
269 unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
271 unsigned int old = m_ReplyTo;
278 unsigned int ChatTriggers::GetReplyTo()