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 "AdminCache.h"
17 #include "sm_stringutil.h"
19 CConCmdManager g_ConCmds;
21 SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
22 SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
30 typedef List<PlCmdInfo> CmdList;
31 void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info);
33 CConCmdManager::CConCmdManager() : m_Strings(1024)
35 m_pCmds = sm_trie_create();
36 m_pCmdGrps = sm_trie_create();
39 CConCmdManager::~CConCmdManager()
41 sm_trie_destroy(m_pCmds);
42 sm_trie_destroy(m_pCmdGrps);
45 void CConCmdManager::OnSourceModAllInitialized()
47 g_PluginSys.AddPluginsListener(this);
48 g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
49 SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
52 void CConCmdManager::OnSourceModShutdown()
54 /* All commands should already be removed by the time we're done */
55 SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
56 g_RootMenu.RemoveRootConsoleCommand("cmds", this);
57 g_PluginSys.RemovePluginsListener(this);
60 void CConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext)
62 List<CmdHook *>::iterator iter = cmdlist.begin();
65 while (iter != cmdlist.end())
68 if (pHook->pf->GetParentContext() == pContext)
72 iter = cmdlist.erase(iter);
79 void CConCmdManager::OnPluginDestroyed(IPlugin *plugin)
82 List<ConCmdInfo *> removed;
83 if (plugin->GetProperty("CommandList", (void **)&pList, true))
85 IPluginContext *pContext = plugin->GetBaseContext();
86 CmdList::iterator iter;
88 * Only bother if there's an actual command list on this plugin...
90 for (iter=pList->begin();
94 PlCmdInfo &cmd = (*iter);
95 ConCmdInfo *pInfo = cmd.pInfo;
97 /* Has this chain already been fully cleaned/removed? */
98 if (removed.find(pInfo) != removed.end())
103 /* Remove any hooks from us on this command */
104 RemoveConCmds(pInfo->conhooks, pContext);
105 RemoveConCmds(pInfo->srvhooks, pContext);
107 /* See if there are still hooks */
108 if (pInfo->srvhooks.size())
112 if (pInfo->conhooks.size())
117 /* Remove the command, it should be safe now */
119 removed.push_back(pInfo);
125 void CommandCallback()
127 g_ConCmds.InternalDispatch();
130 void CConCmdManager::SetCommandClient(int client)
132 m_CmdClient = client + 1;
135 ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type)
137 const char *cmd = engine->Cmd_Argv(0);
138 int args = engine->Cmd_Argc() - 1;
141 if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
143 cell_t result = type;
144 cell_t tempres = result;
145 List<CmdHook *>::iterator iter;
147 for (iter=pInfo->conhooks.begin();
148 iter!=pInfo->conhooks.end();
153 && pHook->pAdmin->eflags)
155 /* :TODO: admin calculations */
157 pHook->pf->PushCell(client);
158 pHook->pf->PushCell(args);
159 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
161 if (tempres > result)
165 if (result == Pl_Stop)
171 type = (ResultType)result;
177 void CConCmdManager::InternalDispatch()
180 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
181 * OR this dispatch. They will NEVER go through both.
183 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
186 const char *cmd = engine->Cmd_Argv(0);
188 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
193 cell_t result = Pl_Continue;
194 int args = engine->Cmd_Argc() - 1;
196 List<CmdHook *>::iterator iter;
199 /* Execute server-only commands if viable */
200 if (m_CmdClient == 0 && pInfo->srvhooks.size())
202 cell_t tempres = result;
203 for (iter=pInfo->srvhooks.begin();
204 iter!=pInfo->srvhooks.end();
208 pHook->pf->PushCell(args);
209 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
211 if (tempres > result)
215 if (result == Pl_Stop)
222 /* Check if there's an early stop */
223 if (result >= Pl_Stop)
225 if (!pInfo->sourceMod)
227 RETURN_META(MRES_SUPERCEDE);
233 /* Execute console commands */
234 if (pInfo->conhooks.size())
236 cell_t tempres = result;
237 for (iter=pInfo->conhooks.begin();
238 iter!=pInfo->conhooks.end();
244 && pHook->pAdmin->eflags)
246 /* :TODO: check admin stuff */
248 pHook->pf->PushCell(m_CmdClient);
249 pHook->pf->PushCell(args);
250 if (pHook->pf->Execute(&tempres) != SP_ERROR_NONE)
252 if (tempres > result)
256 if (result == Pl_Stop)
264 if (result >= Pl_Handled)
266 if (!pInfo->sourceMod)
268 RETURN_META(MRES_SUPERCEDE);
274 ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args)
279 void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
281 const char *description,
284 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
285 CmdHook *pHook = new CmdHook();
287 pHook->pf = pFunction;
288 if (description && description[0])
290 pHook->helptext.assign(description);
292 pInfo->conhooks.push_back(pHook);
294 /* Add to the plugin */
296 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
297 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
299 pList = new CmdList();
300 pPlugin->SetProperty("CommandList", pList);
304 info.type = Cmd_Console;
306 AddToPlCmdList(pList, info);
309 bool CConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
313 const char *description,
316 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
318 CmdHook *pHook = new CmdHook();
319 AdminCmdInfo *pAdmin = new AdminCmdInfo();
321 pHook->pf = pFunction;
322 if (description && description[0])
324 pHook->helptext.assign(description);
326 pHook->pAdmin = pAdmin;
330 if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object))
332 grpid = m_Strings.AddString(group);
333 sm_trie_insert(m_pCmdGrps, group, (void *)grpid);
338 pAdmin->cmdGrpId = grpid;
339 pAdmin->flags = flags;
341 /* First get the command group override, if any */
342 bool override = g_Admins.GetCommandOverride(group,
343 Override_CommandGroup,
346 /* Next get the command override, if any */
347 if (g_Admins.GetCommandOverride(name,
354 /* Assign normal flags if there were no overrides */
357 pAdmin->eflags = pAdmin->flags;
360 /* Finally, add the hook */
361 pInfo->conhooks.push_back(pHook);
363 /* Now add to the plugin */
365 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
366 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
368 pList = new CmdList();
369 pPlugin->SetProperty("CommandList", pList);
373 info.type = Cmd_Admin;
375 AddToPlCmdList(pList, info);
380 void CConCmdManager::AddServerCommand(IPluginFunction *pFunction,
382 const char *description,
386 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
387 CmdHook *pHook = new CmdHook();
389 pHook->pf = pFunction;
390 if (description && description[0])
392 pHook->helptext.assign(description);
395 pInfo->srvhooks.push_back(pHook);
397 /* Add to the plugin */
399 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
400 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
402 pList = new CmdList();
403 pPlugin->SetProperty("CommandList", pList);
407 info.type = Cmd_Server;
409 AddToPlCmdList(pList, info);
412 void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info)
414 CmdList::iterator iter = pList->begin();
415 bool inserted = false;
416 const char *orig = NULL;
418 orig = info.pInfo->pCmd->GetName();
420 /* Insert this into the help list, SORTED alphabetically. */
421 while (iter != pList->end())
423 PlCmdInfo &obj = (*iter);
424 const char *cmd = obj.pInfo->pCmd->GetName();
425 if (strcmp(orig, cmd) < 0)
427 pList->insert(iter, info);
436 pList->push_back(info);
440 void CConCmdManager::AddToCmdList(ConCmdInfo *info)
442 List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
444 bool inserted = false;
445 const char *orig = NULL;
447 orig = info->pCmd->GetName();
449 /* Insert this into the help list, SORTED alphabetically. */
450 while (iter != m_CmdList.end())
452 const char *cmd = NULL;
454 cmd = pInfo->pCmd->GetName();
455 if (strcmp(orig, cmd) < 0)
457 m_CmdList.insert(iter, info);
466 m_CmdList.push_back(info);
470 void CConCmdManager::RemoveConCmd(ConCmdInfo *info)
472 /* Remove console-specific information
473 * This should always be true as of right now
477 /* Remove from the trie */
478 sm_trie_delete(m_pCmds, info->pCmd->GetName());
482 /* Unlink from SourceMM */
483 g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
484 /* Delete the command's memory */
485 char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
486 char *new_name = const_cast<char *>(info->pCmd->GetName());
491 /* Remove the external hook */
492 SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
496 /* Remove from list */
497 m_CmdList.remove(info);
502 ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
505 if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
507 pInfo = new ConCmdInfo();
508 /* Find the commandopan */
509 ConCommandBase *pBase = icvar->GetCommands();
510 ConCommand *pCmd = NULL;
513 if (pBase->IsCommand()
514 && (strcmp(pBase->GetName(), name) == 0))
516 pCmd = (ConCommand *)pBase;
519 pBase = const_cast<ConCommandBase *>(pBase->GetNext());
524 /* Note that we have to duplicate because the source might not be
525 * a static string, and these expect static memory.
531 char *new_name = sm_strdup(name);
532 char *new_help = sm_strdup(description);
533 pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
534 pInfo->sourceMod = true;
536 SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
541 sm_trie_insert(m_pCmds, name, pInfo);
548 void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
552 const char *text = engine->Cmd_Argv(2);
554 CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
558 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
563 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
565 g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
570 g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
574 CmdList::iterator iter;
575 const char *type = NULL;
578 g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
579 for (iter=pList->begin();
583 PlCmdInfo &cmd = (*iter);
584 if (cmd.type == Cmd_Server)
587 } else if (cmd.type == Cmd_Console) {
589 } else if (cmd.type == Cmd_Admin) {
592 name = cmd.pInfo->pCmd->GetName();
593 if (cmd.pHook->helptext.size())
595 help = cmd.pHook->helptext.c_str();
597 help = cmd.pInfo->pCmd->GetHelpText();
599 g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
605 g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");