1 #include "CConCmdManager.h"
2 #include "sm_srvcmds.h"
4 CConCmdManager g_ConCmds;
6 SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
7 SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
15 typedef List<PlCmdInfo> CmdList;
17 char *sm_strdup(const char *str)
19 char *ptr = new char[strlen(str)+1];
24 CConCmdManager::CConCmdManager()
26 m_pCmds = sm_trie_create();
29 CConCmdManager::~CConCmdManager()
31 sm_trie_destroy(m_pCmds);
34 void CConCmdManager::OnSourceModAllInitialized()
36 g_PluginSys.AddPluginsListener(this);
37 g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
38 SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
41 void CConCmdManager::OnSourceModShutdown()
43 /* All commands should already be removed by the time we're done */
44 SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
45 g_RootMenu.RemoveRootConsoleCommand("cmds", this);
46 g_PluginSys.RemovePluginsListener(this);
49 void CConCmdManager::OnPluginLoaded(IPlugin *plugin)
54 void CConCmdManager::OnPluginDestroyed(IPlugin *plugin)
57 List<ConCmdInfo *> removed;
58 if (plugin->GetProperty("CommandList", (void **)&pList, true))
60 CmdList::iterator iter;
61 for (iter=pList->begin();
65 PlCmdInfo &cmd = (*iter);
66 if (cmd.type == Cmd_Server
67 || cmd.type == Cmd_Console)
69 ConCmdInfo *pInfo = cmd.pInfo;
70 /* See if this is being removed */
71 if (removed.find(pInfo) != removed.end())
75 /* See if there are still hooks */
77 && pInfo->srvhooks->GetFunctionCount())
82 && pInfo->conhooks->GetFunctionCount())
86 /* Remove the command */
88 removed.push_back(pInfo);
95 void CommandCallback()
97 g_ConCmds.InternalDispatch();
100 void CConCmdManager::SetCommandClient(int client)
102 m_CmdClient = client + 1;
105 ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type)
107 const char *cmd = engine->Cmd_Argv(0);
108 int args = engine->Cmd_Argc() - 1;
111 if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
113 cell_t result = type;
114 if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
116 pInfo->conhooks->PushCell(client);
117 pInfo->conhooks->PushCell(args);
118 pInfo->conhooks->Execute(&result);
120 if (result >= Pl_Stop)
124 type = (ResultType)result;
130 void CConCmdManager::InternalDispatch()
133 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
134 * OR this dispatch. They will NEVER go through both.
136 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
139 const char *cmd = engine->Cmd_Argv(0);
141 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
146 cell_t result = Pl_Continue;
147 int args = engine->Cmd_Argc() - 1;
149 if (m_CmdClient == 0)
151 if (pInfo->srvhooks && pInfo->srvhooks->GetFunctionCount())
153 pInfo->srvhooks->PushCell(args);
154 pInfo->srvhooks->Execute(&result);
157 /* Check if there's an early stop */
158 if (result >= Pl_Stop)
160 if (!pInfo->sourceMod)
162 RETURN_META(MRES_SUPERCEDE);
168 /* Execute console command hooks */
169 if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
171 pInfo->conhooks->PushCell(m_CmdClient);
172 pInfo->conhooks->PushCell(args);
173 pInfo->conhooks->Execute(&result);
176 if (result >= Pl_Handled)
178 if (!pInfo->sourceMod)
180 RETURN_META(MRES_SUPERCEDE);
186 void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
188 const char *description,
191 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
193 if (!pInfo->conhooks)
195 pInfo->conhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 2, NULL, Param_Cell, Param_Cell);
198 pInfo->conhooks->AddFunction(pFunction);
200 /* Add to the plugin */
202 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
203 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
205 pList = new CmdList();
206 pPlugin->SetProperty("CommandList", pList);
210 info.type = Cmd_Console;
211 pList->push_back(info);
214 void CConCmdManager::AddServerCommand(IPluginFunction *pFunction,
216 const char *description,
220 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
222 if (!pInfo->srvhooks)
224 pInfo->srvhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 1, NULL, Param_Cell);
227 pInfo->srvhooks->AddFunction(pFunction);
229 /* Add to the plugin */
231 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
232 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
234 pList = new CmdList();
235 pPlugin->SetProperty("CommandList", pList);
239 info.type = Cmd_Server;
240 pList->push_back(info);
243 void CConCmdManager::AddToCmdList(ConCmdInfo *info)
245 List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
247 bool inserted = false;
252 orig = info->pCmd->GetName();
255 /* Insert this into the help list, SORTED alphabetically. */
256 while (iter != m_CmdList.end())
258 const char *cmd = NULL;
262 cmd = pInfo->pCmd->GetName();
264 if (strcmp(orig, cmd) < 0)
266 m_CmdList.insert(iter, info);
275 m_CmdList.push_back(info);
279 void CConCmdManager::RemoveConCmd(ConCmdInfo *info)
281 /* Remove console-specific information */
284 /* Remove from the trie */
285 sm_trie_delete(m_pCmds, info->pCmd->GetName());
289 /* Unlink from SourceMM */
290 g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
291 /* Delete the command's memory */
292 char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
293 char *new_name = const_cast<char *>(info->pCmd->GetName());
298 /* Remove the external hook */
299 SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
303 /* Remove from list */
304 m_CmdList.remove(info);
309 g_Forwards.ReleaseForward(info->srvhooks);
313 ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
316 if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
318 pInfo = new ConCmdInfo();
319 /* Find the commandopan */
320 ConCommandBase *pBase = icvar->GetCommands();
321 ConCommand *pCmd = NULL;
324 if (pBase->IsCommand()
325 && (strcmp(pBase->GetName(), name) == 0))
327 pCmd = (ConCommand *)pBase;
330 pBase = const_cast<ConCommandBase *>(pBase->GetNext());
335 /* Note that we have to duplicate because the source might not be
336 * a static string, and these expect static memory.
342 char *new_name = sm_strdup(name);
343 char *new_help = sm_strdup(description);
344 pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
345 pInfo->sourceMod = true;
347 SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
352 sm_trie_insert(m_pCmds, name, pInfo);
359 void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
363 const char *text = engine->Cmd_Argv(2);
365 CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
369 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
374 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
376 g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
381 g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
385 CmdList::iterator iter;
386 const char *type = NULL;
389 g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
390 for (iter=pList->begin();
394 PlCmdInfo &cmd = (*iter);
395 if (cmd.type == Cmd_Server)
398 } else if (cmd.type == Cmd_Console) {
400 } else if (cmd.type == Cmd_Client) {
403 name = cmd.pInfo->pCmd->GetName();
404 help = cmd.pInfo->pCmd->GetHelpText();
405 g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
411 g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");