2 * ===============================================================
3 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
4 * ===============================================================
6 * This file is not open source and may not be copied without explicit
7 * written permission of AlliedModders LLC. This file may not be redistributed
8 * in whole or significant part.
9 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
14 #include "CConCmdManager.h"
15 #include "sm_srvcmds.h"
16 #include "sm_stringutil.h"
18 CConCmdManager g_ConCmds;
20 SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
21 SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
29 typedef List<PlCmdInfo> CmdList;
31 CConCmdManager::CConCmdManager()
33 m_pCmds = sm_trie_create();
36 CConCmdManager::~CConCmdManager()
38 sm_trie_destroy(m_pCmds);
41 void CConCmdManager::OnSourceModAllInitialized()
43 g_PluginSys.AddPluginsListener(this);
44 g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
45 SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
48 void CConCmdManager::OnSourceModShutdown()
50 /* All commands should already be removed by the time we're done */
51 SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
52 g_RootMenu.RemoveRootConsoleCommand("cmds", this);
53 g_PluginSys.RemovePluginsListener(this);
56 void CConCmdManager::OnPluginLoaded(IPlugin *plugin)
61 void CConCmdManager::OnPluginDestroyed(IPlugin *plugin)
64 List<ConCmdInfo *> removed;
65 if (plugin->GetProperty("CommandList", (void **)&pList, true))
67 CmdList::iterator iter;
68 for (iter=pList->begin();
72 PlCmdInfo &cmd = (*iter);
73 if (cmd.type == Cmd_Server
74 || cmd.type == Cmd_Console)
76 ConCmdInfo *pInfo = cmd.pInfo;
77 /* See if this is being removed */
78 if (removed.find(pInfo) != removed.end())
82 /* See if there are still hooks */
84 && pInfo->srvhooks->GetFunctionCount())
89 && pInfo->conhooks->GetFunctionCount())
93 /* Remove the command */
95 removed.push_back(pInfo);
102 void CommandCallback()
104 g_ConCmds.InternalDispatch();
107 void CConCmdManager::SetCommandClient(int client)
109 m_CmdClient = client + 1;
112 ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type)
114 const char *cmd = engine->Cmd_Argv(0);
115 int args = engine->Cmd_Argc() - 1;
118 if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
120 cell_t result = type;
121 if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
123 pInfo->conhooks->PushCell(client);
124 pInfo->conhooks->PushCell(args);
125 pInfo->conhooks->Execute(&result);
127 if (result >= Pl_Stop)
131 type = (ResultType)result;
137 void CConCmdManager::InternalDispatch()
140 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
141 * OR this dispatch. They will NEVER go through both.
143 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
146 const char *cmd = engine->Cmd_Argv(0);
148 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
153 cell_t result = Pl_Continue;
154 int args = engine->Cmd_Argc() - 1;
156 if (m_CmdClient == 0)
158 if (pInfo->srvhooks && pInfo->srvhooks->GetFunctionCount())
160 pInfo->srvhooks->PushCell(args);
161 pInfo->srvhooks->Execute(&result);
164 /* Check if there's an early stop */
165 if (result >= Pl_Stop)
167 if (!pInfo->sourceMod)
169 RETURN_META(MRES_SUPERCEDE);
175 /* Execute console command hooks */
176 if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
178 pInfo->conhooks->PushCell(m_CmdClient);
179 pInfo->conhooks->PushCell(args);
180 pInfo->conhooks->Execute(&result);
183 if (result >= Pl_Handled)
185 if (!pInfo->sourceMod)
187 RETURN_META(MRES_SUPERCEDE);
193 void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
195 const char *description,
198 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
200 if (!pInfo->conhooks)
202 pInfo->conhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 2, NULL, Param_Cell, Param_Cell);
205 pInfo->conhooks->AddFunction(pFunction);
207 /* Add to the plugin */
209 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
210 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
212 pList = new CmdList();
213 pPlugin->SetProperty("CommandList", pList);
217 info.type = Cmd_Console;
218 pList->push_back(info);
221 void CConCmdManager::AddServerCommand(IPluginFunction *pFunction,
223 const char *description,
227 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
229 if (!pInfo->srvhooks)
231 pInfo->srvhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 1, NULL, Param_Cell);
234 pInfo->srvhooks->AddFunction(pFunction);
236 /* Add to the plugin */
238 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
239 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
241 pList = new CmdList();
242 pPlugin->SetProperty("CommandList", pList);
246 info.type = Cmd_Server;
247 pList->push_back(info);
250 void CConCmdManager::AddToCmdList(ConCmdInfo *info)
252 List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
254 bool inserted = false;
255 const char *orig = NULL;
257 orig = info->pCmd->GetName();
259 /* Insert this into the help list, SORTED alphabetically. */
260 while (iter != m_CmdList.end())
262 const char *cmd = NULL;
264 cmd = pInfo->pCmd->GetName();
265 if (strcmp(orig, cmd) < 0)
267 m_CmdList.insert(iter, info);
276 m_CmdList.push_back(info);
280 void CConCmdManager::RemoveConCmd(ConCmdInfo *info)
282 /* Remove console-specific information
283 * This should always be true as of right now
287 /* Remove from the trie */
288 sm_trie_delete(m_pCmds, info->pCmd->GetName());
292 /* Unlink from SourceMM */
293 g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
294 /* Delete the command's memory */
295 char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
296 char *new_name = const_cast<char *>(info->pCmd->GetName());
301 /* Remove the external hook */
302 SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
306 /* Remove from list */
307 m_CmdList.remove(info);
312 g_Forwards.ReleaseForward(info->srvhooks);
316 ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
319 if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
321 pInfo = new ConCmdInfo();
322 /* Find the commandopan */
323 ConCommandBase *pBase = icvar->GetCommands();
324 ConCommand *pCmd = NULL;
327 if (pBase->IsCommand()
328 && (strcmp(pBase->GetName(), name) == 0))
330 pCmd = (ConCommand *)pBase;
333 pBase = const_cast<ConCommandBase *>(pBase->GetNext());
338 /* Note that we have to duplicate because the source might not be
339 * a static string, and these expect static memory.
345 char *new_name = sm_strdup(name);
346 char *new_help = sm_strdup(description);
347 pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
348 pInfo->sourceMod = true;
350 SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
355 sm_trie_insert(m_pCmds, name, pInfo);
362 void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
366 const char *text = engine->Cmd_Argv(2);
368 CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
372 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
377 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
379 g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
384 g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
388 CmdList::iterator iter;
389 const char *type = NULL;
392 g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
393 for (iter=pList->begin();
397 PlCmdInfo &cmd = (*iter);
398 if (cmd.type == Cmd_Server)
401 } else if (cmd.type == Cmd_Console) {
403 } else if (cmd.type == Cmd_Client) {
406 name = cmd.pInfo->pCmd->GetName();
407 help = cmd.pInfo->pCmd->GetHelpText();
408 g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
414 g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");