1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/ChatTriggers.cpp Wed Jun 06 22:16:15 2007 +0000
1.3 @@ -0,0 +1,232 @@
1.4 +#include "ChatTriggers.h"
1.5 +#include "sm_stringutil.h"
1.6 +#include "TextParsers.h"
1.7 +#include "ConCmdManager.h"
1.8 +
1.9 +/* :HACKHACK: We can't SH_DECL here because ConCmdManager.cpp does */
1.10 +extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
1.11 +extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
1.12 +
1.13 +ChatTriggers g_ChatTriggers;
1.14 +
1.15 +ChatTriggers::ChatTriggers() : m_pSayCmd(NULL), m_bWillProcessInPost(false),
1.16 + m_ReplyTo(SM_REPLY_CONSOLE)
1.17 +{
1.18 + m_PubTrigger = sm_strdup("!");
1.19 + m_PrivTrigger = sm_strdup("/");
1.20 + m_PubTriggerSize = 1;
1.21 + m_PrivTriggerSize = 1;
1.22 +}
1.23 +
1.24 +void ChatTriggers::OnSourceModGameInitialized()
1.25 +{
1.26 + ConCommandBase *pCmd = icvar->GetCommands();
1.27 + const char *name;
1.28 + while (pCmd)
1.29 + {
1.30 + if (pCmd->IsCommand())
1.31 + {
1.32 + name = pCmd->GetName();
1.33 + if (strcmp(name, "say") == 0)
1.34 + {
1.35 + m_pSayCmd = (ConCommand *)pCmd;
1.36 + break;
1.37 + }
1.38 + }
1.39 + pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
1.40 + }
1.41 +
1.42 + if (m_pSayCmd)
1.43 + {
1.44 + SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
1.45 + SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
1.46 + }
1.47 +}
1.48 +
1.49 +void ChatTriggers::OnSourceModShutdown()
1.50 +{
1.51 + if (m_pSayCmd)
1.52 + {
1.53 + SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
1.54 + SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
1.55 + }
1.56 +}
1.57 +
1.58 +void ChatTriggers::OnSayCommand_Pre()
1.59 +{
1.60 + int client = g_ConCmds.GetCommandClient();
1.61 +
1.62 + /* The server console cannot do this */
1.63 + if (client == 0)
1.64 + {
1.65 + RETURN_META(MRES_IGNORED);
1.66 + }
1.67 +
1.68 + const char *args = engine->Cmd_Args();
1.69 +
1.70 + /* Handle quoted string sets */
1.71 + bool is_quoted = false;
1.72 + if (args[0] == '"')
1.73 + {
1.74 + args++;
1.75 + is_quoted = true;
1.76 + }
1.77 +
1.78 + bool is_trigger = false;
1.79 + bool is_silent = false;
1.80 +
1.81 + /* Check for either trigger */
1.82 + if (m_PubTriggerSize && strncmp(args, m_PubTrigger, m_PubTriggerSize) == 0)
1.83 + {
1.84 + is_trigger = true;
1.85 + } else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0) {
1.86 + is_trigger = true;
1.87 + is_silent = true;
1.88 + }
1.89 +
1.90 + if (!is_trigger)
1.91 + {
1.92 + RETURN_META(MRES_IGNORED);
1.93 + }
1.94 +
1.95 + /* If we're a public command, process later */
1.96 + if (!is_silent)
1.97 + {
1.98 + /* We have to process this in _post_ instead. Darn. */
1.99 + m_bWillProcessInPost = true;
1.100 + RETURN_META(MRES_IGNORED);
1.101 + }
1.102 +
1.103 + /* Otherwise, process now */
1.104 + if (ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PrivTriggerSize], is_quoted))
1.105 + {
1.106 + /* If we succeed, block the original say! */
1.107 + RETURN_META(MRES_SUPERCEDE);
1.108 + }
1.109 +
1.110 + RETURN_META(MRES_IGNORED);
1.111 +}
1.112 +
1.113 +void ChatTriggers::OnSayCommand_Post()
1.114 +{
1.115 + if (m_bWillProcessInPost)
1.116 + {
1.117 + /* Reset this for re-entrancy */
1.118 + m_bWillProcessInPost = false;
1.119 +
1.120 + /* Get our arguments */
1.121 + const char *args = engine->Cmd_Args();
1.122 +
1.123 + /* Handle quotations */
1.124 + bool is_quoted = false;
1.125 + if (args[0] == '"')
1.126 + {
1.127 + args++;
1.128 + is_quoted = true;
1.129 + }
1.130 +
1.131 + int client = g_ConCmds.GetCommandClient();
1.132 +
1.133 + ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PubTriggerSize], is_quoted);
1.134 + }
1.135 +}
1.136 +
1.137 +bool ChatTriggers::ProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
1.138 +{
1.139 + /* Eat up whitespace */
1.140 + while (*args != '\0' && IsWhitespace(args))
1.141 + {
1.142 + args++;
1.143 + }
1.144 +
1.145 + /* Check if we're still valid */
1.146 + if (*args == '\0')
1.147 + {
1.148 + return false;
1.149 + }
1.150 +
1.151 + /* Extract a command. This is kind of sloppy. */
1.152 + char cmd_buf[64];
1.153 + size_t cmd_len = 0;
1.154 + const char *inptr = args;
1.155 + while (*inptr != '\0'
1.156 + && !IsWhitespace(inptr)
1.157 + && cmd_len < sizeof(cmd_buf) - 1)
1.158 + {
1.159 + cmd_buf[cmd_len++] = *inptr++;
1.160 + }
1.161 + cmd_buf[cmd_len] = '\0';
1.162 +
1.163 + /* See if we have this registered */
1.164 + bool prepended = false;
1.165 + if (!g_ConCmds.LookForSourceModCommand(cmd_buf))
1.166 + {
1.167 + /* Check if we had an "sm_" prefix */
1.168 + if (strncmp(cmd_buf, "sm_", 3) == 0)
1.169 + {
1.170 + return false;
1.171 + }
1.172 +
1.173 + /* Now, prepend. Don't worry about the buffers. This will
1.174 + * work because the sizes are limited from earlier.
1.175 + */
1.176 + char new_buf[80];
1.177 + strcpy(new_buf, "sm_");
1.178 + strncopy(&new_buf[3], cmd_buf, sizeof(new_buf)-3);
1.179 +
1.180 + /* Recheck */
1.181 + if (!g_ConCmds.LookForSourceModCommand(new_buf))
1.182 + {
1.183 + return false;
1.184 + }
1.185 +
1.186 + prepended = true;
1.187 + }
1.188 +
1.189 + /* See if we need to do extra string manipulation */
1.190 + if (is_quoted || prepended)
1.191 + {
1.192 + static char buffer[300];
1.193 + size_t len;
1.194 +
1.195 + /* Check if we need to prepend sm_ */
1.196 + if (prepended)
1.197 + {
1.198 + len = UTIL_Format(buffer, sizeof(buffer), "sm_%s", args);
1.199 + } else {
1.200 + len = strncopy(buffer, args, sizeof(buffer));
1.201 + }
1.202 +
1.203 + /* Check if we need to strip a quote */
1.204 + if (is_quoted)
1.205 + {
1.206 + if (buffer[len-1] == '"')
1.207 + {
1.208 + buffer[--len] = '\0';
1.209 + }
1.210 + }
1.211 +
1.212 + args = buffer;
1.213 + }
1.214 +
1.215 + /* Finally, execute! */
1.216 + unsigned int old = SetReplyTo(SM_REPLY_CHAT);
1.217 + serverpluginhelpers->ClientCommand(pEdict, args);
1.218 + SetReplyTo(old);
1.219 +
1.220 + return true;
1.221 +}
1.222 +
1.223 +unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
1.224 +{
1.225 + unsigned int old = m_ReplyTo;
1.226 +
1.227 + m_ReplyTo = reply;
1.228 +
1.229 + return old;
1.230 +}
1.231 +
1.232 +unsigned int ChatTriggers::GetReplyTo()
1.233 +{
1.234 + return m_ReplyTo;
1.235 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/core/ChatTriggers.h Wed Jun 06 22:16:15 2007 +0000
2.3 @@ -0,0 +1,37 @@
2.4 +#ifndef _INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
2.5 +#define _INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
2.6 +
2.7 +#include "sm_globals.h"
2.8 +#include "sourcemm_api.h"
2.9 +
2.10 +#define SM_REPLY_CONSOLE 0
2.11 +#define SM_REPLY_CHAT 1
2.12 +
2.13 +class ChatTriggers : public SMGlobalClass
2.14 +{
2.15 +public:
2.16 + ChatTriggers();
2.17 +public: //SMGlobalClass
2.18 + void OnSourceModGameInitialized();
2.19 + void OnSourceModShutdown();
2.20 +private: //ConCommand
2.21 + void OnSayCommand_Pre();
2.22 + void OnSayCommand_Post();
2.23 +public:
2.24 + unsigned int GetReplyTo();
2.25 + unsigned int SetReplyTo(unsigned int reply);
2.26 +private:
2.27 + bool ProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted);
2.28 +private:
2.29 + ConCommand *m_pSayCmd;
2.30 + char *m_PubTrigger;
2.31 + size_t m_PubTriggerSize;
2.32 + char *m_PrivTrigger;
2.33 + size_t m_PrivTriggerSize;
2.34 + bool m_bWillProcessInPost;
2.35 + unsigned int m_ReplyTo;
2.36 +};
2.37 +
2.38 +extern ChatTriggers g_ChatTriggers;
2.39 +
2.40 +#endif //_INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
3.1 --- a/core/ConCmdManager.cpp Wed Jun 06 20:51:17 2007 +0000
3.2 +++ b/core/ConCmdManager.cpp Wed Jun 06 22:16:15 2007 +0000
3.3 @@ -58,13 +58,17 @@
3.4 SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
3.5
3.6 ConCommandBase *pCmd = icvar->GetCommands();
3.7 + const char *name;
3.8 while (pCmd)
3.9 {
3.10 - if (pCmd->IsCommand()
3.11 - && strcmp(pCmd->GetName(), "exec") == 0)
3.12 + if (pCmd->IsCommand())
3.13 {
3.14 - m_pExecCmd = (ConCommand *)pCmd;
3.15 - break;
3.16 + name = pCmd->GetName();
3.17 + if (strcmp(name, "exec") == 0)
3.18 + {
3.19 + m_pExecCmd = (ConCommand *)pCmd;
3.20 + break;
3.21 + }
3.22 }
3.23 pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
3.24 }
3.25 @@ -756,6 +760,18 @@
3.26 delete info;
3.27 }
3.28
3.29 +bool ConCmdManager::LookForSourceModCommand(const char *cmd)
3.30 +{
3.31 + ConCmdInfo *pInfo;
3.32 +
3.33 + if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
3.34 + {
3.35 + return false;
3.36 + }
3.37 +
3.38 + return pInfo->sourceMod && (pInfo->conhooks.size() > 0);
3.39 +}
3.40 +
3.41 ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
3.42 {
3.43 ConCmdInfo *pInfo;
4.1 --- a/core/ConCmdManager.h Wed Jun 06 20:51:17 2007 +0000
4.2 +++ b/core/ConCmdManager.h Wed Jun 06 22:16:15 2007 +0000
4.3 @@ -101,6 +101,7 @@
4.4 ResultType DispatchClientCommand(int client, ResultType type);
4.5 void UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits);
4.6 void NotifyExecDone(const char *file);
4.7 + bool LookForSourceModCommand(const char *cmd);
4.8 private:
4.9 void InternalDispatch();
4.10 ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args);
5.1 --- a/core/msvc8/sourcemod_mm.vcproj Wed Jun 06 20:51:17 2007 +0000
5.2 +++ b/core/msvc8/sourcemod_mm.vcproj Wed Jun 06 22:16:15 2007 +0000
5.3 @@ -190,6 +190,10 @@
5.4 >
5.5 </File>
5.6 <File
5.7 + RelativePath="..\ChatTriggers.cpp"
5.8 + >
5.9 + </File>
5.10 + <File
5.11 RelativePath="..\ConCmdManager.cpp"
5.12 >
5.13 </File>
5.14 @@ -312,6 +316,10 @@
5.15 >
5.16 </File>
5.17 <File
5.18 + RelativePath="..\ChatTriggers.h"
5.19 + >
5.20 + </File>
5.21 + <File
5.22 RelativePath="..\ConCmdManager.h"
5.23 >
5.24 </File>
6.1 --- a/core/sm_globals.h Wed Jun 06 20:51:17 2007 +0000
6.2 +++ b/core/sm_globals.h Wed Jun 06 22:16:15 2007 +0000
6.3 @@ -121,6 +121,13 @@
6.4 virtual void OnSourceModVSPReceived(IServerPluginCallbacks *iface)
6.5 {
6.6 }
6.7 +
6.8 + /**
6.9 + * @brief Called once all MM:S plugins are loaded.
6.10 + */
6.11 + virtual void OnSourceModGameInitialized()
6.12 + {
6.13 + }
6.14 private:
6.15 SMGlobalClass *m_pGlobalClassNext;
6.16 static SMGlobalClass *head;
7.1 --- a/core/smn_console.cpp Wed Jun 06 20:51:17 2007 +0000
7.2 +++ b/core/smn_console.cpp Wed Jun 06 22:16:15 2007 +0000
7.3 @@ -21,6 +21,7 @@
7.4 #include "PluginSys.h"
7.5 #include "sm_stringutil.h"
7.6 #include "PlayerManager.h"
7.7 +#include "ChatTriggers.h"
7.8
7.9 enum ConVarBounds
7.10 {
7.11 @@ -28,6 +29,8 @@
7.12 ConVarBound_Lower
7.13 };
7.14
7.15 +#define HUD_PRINTTALK 3
7.16 +
7.17 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
7.18 {
7.19 char *name, *defaultVal, *helpText;
7.20 @@ -632,6 +635,77 @@
7.21 return 1;
7.22 }
7.23
7.24 +
7.25 +static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
7.26 +{
7.27 + CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
7.28 +
7.29 + if (!pPlayer)
7.30 + {
7.31 + return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
7.32 + }
7.33 +
7.34 + if (!pPlayer->IsConnected())
7.35 + {
7.36 + return pContext->ThrowNativeError("Player %d is not connected", params[1]);
7.37 + }
7.38 +
7.39 + char buffer[256];
7.40 + g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
7.41 +
7.42 + unsigned int old = g_ChatTriggers.SetReplyTo(SM_REPLY_CONSOLE);
7.43 + serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
7.44 + g_ChatTriggers.SetReplyTo(old);
7.45 +
7.46 + return 1;
7.47 +}
7.48 +
7.49 +static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
7.50 +{
7.51 + /* Build the format string */
7.52 + char buffer[1024];
7.53 + size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
7.54 +
7.55 + /* If we're printing to the server, shortcut out */
7.56 + if (params[1] == 0)
7.57 + {
7.58 + /* Print */
7.59 + buffer[len++] = '\n';
7.60 + buffer[len] = '\0';
7.61 + META_CONPRINT(buffer);
7.62 + return 1;
7.63 + }
7.64 +
7.65 + CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
7.66 +
7.67 + if (!pPlayer)
7.68 + {
7.69 + return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
7.70 + }
7.71 +
7.72 + if (!pPlayer->IsConnected())
7.73 + {
7.74 + return pContext->ThrowNativeError("Client %d is not connected", params[1]);
7.75 + }
7.76 +
7.77 + unsigned int replyto = g_ChatTriggers.GetReplyTo();
7.78 + if (replyto == SM_REPLY_CONSOLE)
7.79 + {
7.80 + buffer[len++] = '\n';
7.81 + buffer[len] = '\0';
7.82 + engine->ClientPrintf(pPlayer->GetEdict(), buffer);
7.83 + } else if (replyto == SM_REPLY_CHAT) {
7.84 + if (len >= 191)
7.85 + {
7.86 + len = 191;
7.87 + }
7.88 + buffer[len] = '\0';
7.89 + g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
7.90 + }
7.91 +
7.92 + return 1;
7.93 +}
7.94 +
7.95 REGISTER_NATIVES(consoleNatives)
7.96 {
7.97 {"CreateConVar", sm_CreateConVar},
7.98 @@ -665,5 +739,7 @@
7.99 {"InsertServerCommand", sm_InsertServerCommand},
7.100 {"ServerExecute", sm_ServerExecute},
7.101 {"ClientCommand", sm_ClientCommand},
7.102 + {"FakeClientCommand", FakeClientCommand},
7.103 + {"ReplyToCommand", ReplyToCommand},
7.104 {NULL, NULL}
7.105 };
8.1 --- a/core/smn_halflife.cpp Wed Jun 06 20:51:17 2007 +0000
8.2 +++ b/core/smn_halflife.cpp Wed Jun 06 22:16:15 2007 +0000
8.3 @@ -233,28 +233,6 @@
8.4 return enginesound->IsSoundPrecached(sample) ? 1 : 0;
8.5 }
8.6
8.7 -static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
8.8 -{
8.9 - CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
8.10 -
8.11 - if (!pPlayer)
8.12 - {
8.13 - return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
8.14 - }
8.15 -
8.16 - if (!pPlayer->IsConnected())
8.17 - {
8.18 - return pContext->ThrowNativeError("Player %d is not connected", params[1]);
8.19 - }
8.20 -
8.21 - char buffer[256];
8.22 - g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
8.23 -
8.24 - serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
8.25 -
8.26 - return 1;
8.27 -}
8.28 -
8.29 static cell_t smn_CreateDialog(IPluginContext *pContext, const cell_t *params)
8.30 {
8.31 KeyValues *pKV;
8.32 @@ -354,7 +332,6 @@
8.33 {"IsGenericPrecached", IsGenericPrecached},
8.34 {"PrecacheSound", PrecacheSound},
8.35 {"IsSoundPrecached", IsSoundPrecached},
8.36 - {"FakeClientCommand", FakeClientCommand},
8.37 {"CreateDialog", smn_CreateDialog},
8.38 {"PrintToChat", PrintToChat},
8.39 {"PrintCenterText", PrintCenterText},
9.1 --- a/core/sourcemm_api.cpp Wed Jun 06 20:51:17 2007 +0000
9.2 +++ b/core/sourcemm_api.cpp Wed Jun 06 22:16:15 2007 +0000
9.3 @@ -88,6 +88,7 @@
9.4
9.5 void SourceMod_Core::AllPluginsLoaded()
9.6 {
9.7 + g_SourceMod.AllPluginsLoaded();
9.8 }
9.9
9.10 const char *SourceMod_Core::GetAuthor()
10.1 --- a/core/sourcemod.cpp Wed Jun 06 20:51:17 2007 +0000
10.2 +++ b/core/sourcemod.cpp Wed Jun 06 22:16:15 2007 +0000
10.3 @@ -656,6 +656,16 @@
10.4 return g_pVM;
10.5 }
10.6
10.7 +void SourceModBase::AllPluginsLoaded()
10.8 +{
10.9 + SMGlobalClass *base = SMGlobalClass::head;
10.10 + while (base)
10.11 + {
10.12 + base->OnSourceModGameInitialized();
10.13 + base = base->m_pGlobalClassNext;
10.14 + }
10.15 +}
10.16 +
10.17 SMGlobalClass *SMGlobalClass::head = NULL;
10.18
10.19 SMGlobalClass::SMGlobalClass()
11.1 --- a/core/sourcemod.h Wed Jun 06 20:51:17 2007 +0000
11.2 +++ b/core/sourcemod.h Wed Jun 06 22:16:15 2007 +0000
11.3 @@ -97,6 +97,7 @@
11.4 const char *GetGameFolderName() const;
11.5 ISourcePawnEngine *GetScriptingEngine();
11.6 IVirtualMachine *GetScriptingVM();
11.7 + void AllPluginsLoaded();
11.8 private:
11.9 /**
11.10 * @brief Loading plugins
12.1 --- a/plugins/include/console.inc Wed Jun 06 20:51:17 2007 +0000
12.2 +++ b/plugins/include/console.inc Wed Jun 06 22:16:15 2007 +0000
12.3 @@ -133,7 +133,7 @@
12.4 /**
12.5 * Sends a message to a client's console.
12.6 *
12.7 - * @param client Player index.
12.8 + * @param client Client index.
12.9 * @param format Formatting rules.
12.10 * @param ... Variable number of format parameters.
12.11 * @noreturn
12.12 @@ -142,6 +142,21 @@
12.13 native PrintToConsole(client, const String:format[], any:...);
12.14
12.15 /**
12.16 + * Reples to a message in a command.
12.17 + *
12.18 + * A client index of 0 will use PrintToServer().
12.19 + * If the command was from the console, PrintToConsole() is used.
12.20 + * If the command was from chat, PrintToChat() is used.
12.21 + *
12.22 + * @param client Client index, or 0 for server.
12.23 + * @param format Formatting rules.
12.24 + * @param ... Variable number of format parameters.
12.25 + * @noreturn
12.26 + * @error If the client is not connected or invalid.
12.27 + */
12.28 +native ReplyToCommand(client, const String:fornmat[], any:...);
12.29 +
12.30 +/**
12.31 * Called when a server-only command is invoked.
12.32 *
12.33 * @params args Number of arguments that were in the argument string.