1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/ConCmdManager.cpp Tue Mar 06 06:15:19 2007 +0000
1.3 @@ -0,0 +1,788 @@
1.4 +/**
1.5 + * ===============================================================
1.6 + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
1.7 + * ===============================================================
1.8 + *
1.9 + * This file is not open source and may not be copied without explicit
1.10 + * written permission of AlliedModders LLC. This file may not be redistributed
1.11 + * in whole or significant part.
1.12 + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
1.13 + *
1.14 + * Version: $Id$
1.15 + */
1.16 +
1.17 +#include "ConCmdManager.h"
1.18 +#include "sm_srvcmds.h"
1.19 +#include "AdminCache.h"
1.20 +#include "sm_stringutil.h"
1.21 +#include "CPlayerManager.h"
1.22 +#include "CTranslator.h"
1.23 +
1.24 +ConCmdManager g_ConCmds;
1.25 +
1.26 +SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
1.27 +SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
1.28 +
1.29 +struct PlCmdInfo
1.30 +{
1.31 + ConCmdInfo *pInfo;
1.32 + CmdHook *pHook;
1.33 + CmdType type;
1.34 +};
1.35 +typedef List<PlCmdInfo> CmdList;
1.36 +void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info);
1.37 +
1.38 +ConCmdManager::ConCmdManager() : m_Strings(1024)
1.39 +{
1.40 + m_pCmds = sm_trie_create();
1.41 + m_pCmdGrps = sm_trie_create();
1.42 +}
1.43 +
1.44 +ConCmdManager::~ConCmdManager()
1.45 +{
1.46 + sm_trie_destroy(m_pCmds);
1.47 + sm_trie_destroy(m_pCmdGrps);
1.48 +}
1.49 +
1.50 +void ConCmdManager::OnSourceModAllInitialized()
1.51 +{
1.52 + g_PluginSys.AddPluginsListener(this);
1.53 + g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
1.54 + SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
1.55 +}
1.56 +
1.57 +void ConCmdManager::OnSourceModShutdown()
1.58 +{
1.59 + /* All commands should already be removed by the time we're done */
1.60 + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
1.61 + g_RootMenu.RemoveRootConsoleCommand("cmds", this);
1.62 + g_PluginSys.RemovePluginsListener(this);
1.63 +}
1.64 +
1.65 +void ConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext)
1.66 +{
1.67 + List<CmdHook *>::iterator iter = cmdlist.begin();
1.68 + CmdHook *pHook;
1.69 +
1.70 + while (iter != cmdlist.end())
1.71 + {
1.72 + pHook = (*iter);
1.73 + if (pHook->pf->GetParentContext() == pContext)
1.74 + {
1.75 + delete pHook->pAdmin;
1.76 + delete pHook;
1.77 + iter = cmdlist.erase(iter);
1.78 + } else {
1.79 + iter++;
1.80 + }
1.81 + }
1.82 +}
1.83 +
1.84 +void ConCmdManager::OnPluginDestroyed(IPlugin *plugin)
1.85 +{
1.86 + CmdList *pList;
1.87 + List<ConCmdInfo *> removed;
1.88 + if (plugin->GetProperty("CommandList", (void **)&pList, true))
1.89 + {
1.90 + IPluginContext *pContext = plugin->GetBaseContext();
1.91 + CmdList::iterator iter;
1.92 + /* First pass!
1.93 + * Only bother if there's an actual command list on this plugin...
1.94 + */
1.95 + for (iter=pList->begin();
1.96 + iter!=pList->end();
1.97 + iter++)
1.98 + {
1.99 + PlCmdInfo &cmd = (*iter);
1.100 + ConCmdInfo *pInfo = cmd.pInfo;
1.101 +
1.102 + /* Has this chain already been fully cleaned/removed? */
1.103 + if (removed.find(pInfo) != removed.end())
1.104 + {
1.105 + continue;
1.106 + }
1.107 +
1.108 + /* Remove any hooks from us on this command */
1.109 + RemoveConCmds(pInfo->conhooks, pContext);
1.110 + RemoveConCmds(pInfo->srvhooks, pContext);
1.111 +
1.112 + /* See if there are still hooks */
1.113 + if (pInfo->srvhooks.size())
1.114 + {
1.115 + continue;
1.116 + }
1.117 + if (pInfo->conhooks.size())
1.118 + {
1.119 + continue;
1.120 + }
1.121 +
1.122 + /* Remove the command, it should be safe now */
1.123 + RemoveConCmd(pInfo);
1.124 + removed.push_back(pInfo);
1.125 + }
1.126 + delete pList;
1.127 + }
1.128 +}
1.129 +
1.130 +void CommandCallback()
1.131 +{
1.132 + g_ConCmds.InternalDispatch();
1.133 +}
1.134 +
1.135 +void ConCmdManager::SetCommandClient(int client)
1.136 +{
1.137 + m_CmdClient = client + 1;
1.138 +}
1.139 +
1.140 +ResultType ConCmdManager::DispatchClientCommand(int client, ResultType type)
1.141 +{
1.142 + const char *cmd = engine->Cmd_Argv(0);
1.143 + int args = engine->Cmd_Argc() - 1;
1.144 +
1.145 + ConCmdInfo *pInfo;
1.146 + if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
1.147 + {
1.148 + cell_t result = type;
1.149 + cell_t tempres = result;
1.150 + List<CmdHook *>::iterator iter;
1.151 + CmdHook *pHook;
1.152 + for (iter=pInfo->conhooks.begin();
1.153 + iter!=pInfo->conhooks.end();
1.154 + iter++)
1.155 + {
1.156 + pHook = (*iter);
1.157 + if (!pHook->pf->IsRunnable())
1.158 + {
1.159 + continue;
1.160 + }
1.161 + if (pHook->pAdmin && !CheckAccess(client, cmd, pHook->pAdmin))
1.162 + {
1.163 + if (result < Pl_Handled)
1.164 + {
1.165 + result = Pl_Handled;
1.166 + }
1.167 + continue;
1.168 + }
1.169 + pHook->pf->PushCell(client);
1.170 + pHook->pf->PushCell(args);
1.171 + if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
1.172 + {
1.173 + if (tempres > result)
1.174 + {
1.175 + result = tempres;
1.176 + }
1.177 + if (result == Pl_Stop)
1.178 + {
1.179 + break;
1.180 + }
1.181 + }
1.182 + }
1.183 + type = (ResultType)result;
1.184 + }
1.185 +
1.186 + return type;
1.187 +}
1.188 +
1.189 +void ConCmdManager::InternalDispatch()
1.190 +{
1.191 + /**
1.192 + * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
1.193 + * OR this dispatch. They will NEVER go through both.
1.194 + * --
1.195 + * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
1.196 + */
1.197 +
1.198 + const char *cmd = engine->Cmd_Argv(0);
1.199 + ConCmdInfo *pInfo;
1.200 + if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
1.201 + {
1.202 + return;
1.203 + }
1.204 +
1.205 + cell_t result = Pl_Continue;
1.206 + int args = engine->Cmd_Argc() - 1;
1.207 +
1.208 + List<CmdHook *>::iterator iter;
1.209 + CmdHook *pHook;
1.210 +
1.211 + /* Execute server-only commands if viable */
1.212 + if (m_CmdClient == 0 && pInfo->srvhooks.size())
1.213 + {
1.214 + cell_t tempres = result;
1.215 + for (iter=pInfo->srvhooks.begin();
1.216 + iter!=pInfo->srvhooks.end();
1.217 + iter++)
1.218 + {
1.219 + pHook = (*iter);
1.220 + if (!pHook->pf->IsRunnable())
1.221 + {
1.222 + continue;
1.223 + }
1.224 + pHook->pf->PushCell(args);
1.225 + if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
1.226 + {
1.227 + if (tempres > result)
1.228 + {
1.229 + result = tempres;
1.230 + }
1.231 + if (result == Pl_Stop)
1.232 + {
1.233 + break;
1.234 + }
1.235 + }
1.236 + }
1.237 +
1.238 + /* Check if there's an early stop */
1.239 + if (result >= Pl_Stop)
1.240 + {
1.241 + if (!pInfo->sourceMod)
1.242 + {
1.243 + RETURN_META(MRES_SUPERCEDE);
1.244 + }
1.245 + return;
1.246 + }
1.247 + }
1.248 +
1.249 + /* Execute console commands */
1.250 + if (pInfo->conhooks.size())
1.251 + {
1.252 + cell_t tempres = result;
1.253 + for (iter=pInfo->conhooks.begin();
1.254 + iter!=pInfo->conhooks.end();
1.255 + iter++)
1.256 + {
1.257 + pHook = (*iter);
1.258 + if (!pHook->pf->IsRunnable())
1.259 + {
1.260 + continue;
1.261 + }
1.262 + if (m_CmdClient
1.263 + && pHook->pAdmin
1.264 + && !CheckAccess(m_CmdClient, cmd, pHook->pAdmin))
1.265 + {
1.266 + if (result < Pl_Handled)
1.267 + {
1.268 + result = Pl_Handled;
1.269 + }
1.270 + continue;
1.271 + }
1.272 + pHook->pf->PushCell(m_CmdClient);
1.273 + pHook->pf->PushCell(args);
1.274 + if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
1.275 + {
1.276 + if (tempres > result)
1.277 + {
1.278 + result = tempres;
1.279 + }
1.280 + if (result == Pl_Stop)
1.281 + {
1.282 + break;
1.283 + }
1.284 + }
1.285 + }
1.286 + }
1.287 +
1.288 + if (result >= Pl_Handled)
1.289 + {
1.290 + if (!pInfo->sourceMod)
1.291 + {
1.292 + RETURN_META(MRES_SUPERCEDE);
1.293 + }
1.294 + return;
1.295 + }
1.296 +}
1.297 +
1.298 +bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin)
1.299 +{
1.300 + FlagBits cmdflags = pAdmin->eflags;
1.301 + if (cmdflags == 0)
1.302 + {
1.303 + return true;
1.304 + }
1.305 +
1.306 + CPlayer *player = g_Players.GetPlayerByIndex(client);
1.307 + if (!player)
1.308 + {
1.309 + return false;
1.310 + }
1.311 +
1.312 + AdminId adm = player->GetAdminId();
1.313 + if (adm != INVALID_ADMIN_ID)
1.314 + {
1.315 + FlagBits bits = g_Admins.GetAdminFlags(adm, Access_Effective);
1.316 +
1.317 + /* root knows all, WHOA */
1.318 + if ((bits & ADMFLAG_ROOT) == ADMFLAG_ROOT)
1.319 + {
1.320 + return true;
1.321 + }
1.322 +
1.323 + /* See if our other flags match */
1.324 + if ((bits & cmdflags) == cmdflags)
1.325 + {
1.326 + return true;
1.327 + }
1.328 +
1.329 + /* Check for overrides */
1.330 + unsigned int groups = g_Admins.GetAdminGroupCount(adm);
1.331 + GroupId gid;
1.332 + OverrideRule rule;
1.333 + bool override = false;
1.334 + for (unsigned int i=0; i<groups; i++)
1.335 + {
1.336 + gid = g_Admins.GetAdminGroup(adm, i, NULL);
1.337 + /* First get group-level override */
1.338 + override = g_Admins.GetGroupCommandOverride(gid, cmd, Override_CommandGroup, &rule);
1.339 + /* Now get the specific command override */
1.340 + if (g_Admins.GetGroupCommandOverride(gid, cmd, Override_Command, &rule))
1.341 + {
1.342 + override = true;
1.343 + }
1.344 + if (override)
1.345 + {
1.346 + if (rule == Command_Allow)
1.347 + {
1.348 + return true;
1.349 + } else if (rule == Command_Deny) {
1.350 + break;
1.351 + }
1.352 + }
1.353 + }
1.354 + }
1.355 +
1.356 + if (player->IsFakeClient()
1.357 + || player->GetEdict() == NULL)
1.358 + {
1.359 + return false;
1.360 + }
1.361 +
1.362 + /* If we got here, the command failed... */
1.363 + char buffer[128];
1.364 + if (g_Translator.CoreTrans(client, buffer, sizeof(buffer), "No Access", NULL, NULL)
1.365 + != Trans_Okay)
1.366 + {
1.367 + snprintf(buffer, sizeof(buffer), "You do not have access to this command");
1.368 + }
1.369 +
1.370 + char fullbuffer[192];
1.371 + snprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer);
1.372 +
1.373 + engine->ClientPrintf(player->GetEdict(), fullbuffer);
1.374 +
1.375 + return false;
1.376 +}
1.377 +
1.378 +bool ConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
1.379 + const char *name,
1.380 + const char *description,
1.381 + int flags)
1.382 +{
1.383 + ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
1.384 +
1.385 + if (!pInfo)
1.386 + {
1.387 + return false;
1.388 + }
1.389 +
1.390 + CmdHook *pHook = new CmdHook();
1.391 +
1.392 + pHook->pf = pFunction;
1.393 + if (description && description[0])
1.394 + {
1.395 + pHook->helptext.assign(description);
1.396 + }
1.397 + pInfo->conhooks.push_back(pHook);
1.398 +
1.399 + /* Add to the plugin */
1.400 + CmdList *pList;
1.401 + IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
1.402 + if (!pPlugin->GetProperty("CommandList", (void **)&pList))
1.403 + {
1.404 + pList = new CmdList();
1.405 + pPlugin->SetProperty("CommandList", pList);
1.406 + }
1.407 + PlCmdInfo info;
1.408 + info.pInfo = pInfo;
1.409 + info.type = Cmd_Console;
1.410 + info.pHook = pHook;
1.411 + AddToPlCmdList(pList, info);
1.412 +
1.413 + return true;
1.414 +}
1.415 +
1.416 +bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
1.417 + const char *name,
1.418 + const char *group,
1.419 + int adminflags,
1.420 + const char *description,
1.421 + int flags)
1.422 +{
1.423 + ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
1.424 +
1.425 + if (!pInfo)
1.426 + {
1.427 + return false;
1.428 + }
1.429 +
1.430 + CmdHook *pHook = new CmdHook();
1.431 + AdminCmdInfo *pAdmin = new AdminCmdInfo();
1.432 +
1.433 + pHook->pf = pFunction;
1.434 + if (description && description[0])
1.435 + {
1.436 + pHook->helptext.assign(description);
1.437 + }
1.438 + pHook->pAdmin = pAdmin;
1.439 +
1.440 + void *object;
1.441 + int grpid;
1.442 + if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object))
1.443 + {
1.444 + grpid = m_Strings.AddString(group);
1.445 + sm_trie_insert(m_pCmdGrps, group, (void *)grpid);
1.446 + } else {
1.447 + grpid = (int)object;
1.448 + }
1.449 +
1.450 + pAdmin->cmdGrpId = grpid;
1.451 + pAdmin->flags = adminflags;
1.452 +
1.453 + /* First get the command group override, if any */
1.454 + bool override = g_Admins.GetCommandOverride(group,
1.455 + Override_CommandGroup,
1.456 + &(pAdmin->eflags));
1.457 +
1.458 + /* Next get the command override, if any */
1.459 + if (g_Admins.GetCommandOverride(name,
1.460 + Override_Command,
1.461 + &(pAdmin->eflags)))
1.462 + {
1.463 + override = true;
1.464 + }
1.465 +
1.466 + /* Assign normal flags if there were no overrides */
1.467 + if (!override)
1.468 + {
1.469 + pAdmin->eflags = pAdmin->flags;
1.470 + }
1.471 +
1.472 + /* Finally, add the hook */
1.473 + pInfo->conhooks.push_back(pHook);
1.474 +
1.475 + /* Now add to the plugin */
1.476 + CmdList *pList;
1.477 + IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
1.478 + if (!pPlugin->GetProperty("CommandList", (void **)&pList))
1.479 + {
1.480 + pList = new CmdList();
1.481 + pPlugin->SetProperty("CommandList", pList);
1.482 + }
1.483 + PlCmdInfo info;
1.484 + info.pInfo = pInfo;
1.485 + info.type = Cmd_Admin;
1.486 + info.pHook = pHook;
1.487 + AddToPlCmdList(pList, info);
1.488 +
1.489 + return true;
1.490 +}
1.491 +
1.492 +bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction,
1.493 + const char *name,
1.494 + const char *description,
1.495 + int flags)
1.496 +
1.497 +{
1.498 + ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
1.499 +
1.500 + if (!pInfo)
1.501 + {
1.502 + return false;
1.503 + }
1.504 +
1.505 + CmdHook *pHook = new CmdHook();
1.506 +
1.507 + pHook->pf = pFunction;
1.508 + if (description && description[0])
1.509 + {
1.510 + pHook->helptext.assign(description);
1.511 + }
1.512 +
1.513 + pInfo->srvhooks.push_back(pHook);
1.514 +
1.515 + /* Add to the plugin */
1.516 + CmdList *pList;
1.517 + IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
1.518 + if (!pPlugin->GetProperty("CommandList", (void **)&pList))
1.519 + {
1.520 + pList = new CmdList();
1.521 + pPlugin->SetProperty("CommandList", pList);
1.522 + }
1.523 + PlCmdInfo info;
1.524 + info.pInfo = pInfo;
1.525 + info.type = Cmd_Server;
1.526 + info.pHook = pHook;
1.527 + AddToPlCmdList(pList, info);
1.528 +
1.529 + return true;
1.530 +}
1.531 +
1.532 +void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info)
1.533 +{
1.534 + CmdList::iterator iter = pList->begin();
1.535 + bool inserted = false;
1.536 + const char *orig = NULL;
1.537 +
1.538 + orig = info.pInfo->pCmd->GetName();
1.539 +
1.540 + /* Insert this into the help list, SORTED alphabetically. */
1.541 + while (iter != pList->end())
1.542 + {
1.543 + PlCmdInfo &obj = (*iter);
1.544 + const char *cmd = obj.pInfo->pCmd->GetName();
1.545 + if (strcmp(orig, cmd) < 0)
1.546 + {
1.547 + pList->insert(iter, info);
1.548 + inserted = true;
1.549 + break;
1.550 + }
1.551 + iter++;
1.552 + }
1.553 +
1.554 + if (!inserted)
1.555 + {
1.556 + pList->push_back(info);
1.557 + }
1.558 +}
1.559 +
1.560 +void ConCmdManager::AddToCmdList(ConCmdInfo *info)
1.561 +{
1.562 + List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
1.563 + ConCmdInfo *pInfo;
1.564 + bool inserted = false;
1.565 + const char *orig = NULL;
1.566 +
1.567 + orig = info->pCmd->GetName();
1.568 +
1.569 + /* Insert this into the help list, SORTED alphabetically. */
1.570 + while (iter != m_CmdList.end())
1.571 + {
1.572 + const char *cmd = NULL;
1.573 + pInfo = (*iter);
1.574 + cmd = pInfo->pCmd->GetName();
1.575 + if (strcmp(orig, cmd) < 0)
1.576 + {
1.577 + m_CmdList.insert(iter, info);
1.578 + inserted = true;
1.579 + break;
1.580 + }
1.581 + iter++;
1.582 + }
1.583 +
1.584 + if (!inserted)
1.585 + {
1.586 + m_CmdList.push_back(info);
1.587 + }
1.588 +}
1.589 +
1.590 +void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits)
1.591 +{
1.592 + ConCmdInfo *pInfo;
1.593 +
1.594 + if (type == Override_Command)
1.595 + {
1.596 + if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
1.597 + {
1.598 + return;
1.599 + }
1.600 +
1.601 + List<CmdHook *>::iterator iter;
1.602 + CmdHook *pHook;
1.603 +
1.604 + for (iter=pInfo->conhooks.begin(); iter!=pInfo->conhooks.end(); iter++)
1.605 + {
1.606 + pHook = (*iter);
1.607 + if (pHook->pAdmin)
1.608 + {
1.609 + if (bits)
1.610 + {
1.611 + pHook->pAdmin->eflags = bits;
1.612 + } else {
1.613 + pHook->pAdmin->eflags = pHook->pAdmin->flags;
1.614 + }
1.615 + }
1.616 + }
1.617 + } else if (type == Override_CommandGroup) {
1.618 + void *object;
1.619 + if (!sm_trie_retrieve(m_pCmdGrps, cmd, &object))
1.620 + {
1.621 + return;
1.622 + }
1.623 + int grpid = (int)object;
1.624 +
1.625 + /* This is bad :( loop through all commands */
1.626 + List<ConCmdInfo *>::iterator iter;
1.627 + CmdHook *pHook;
1.628 + for (iter=m_CmdList.begin(); iter!=m_CmdList.end(); iter++)
1.629 + {
1.630 + pInfo = (*iter);
1.631 + for (List<CmdHook *>::iterator citer=pInfo->conhooks.begin();
1.632 + citer!=pInfo->conhooks.end();
1.633 + citer++)
1.634 + {
1.635 + pHook = (*citer);
1.636 + if (pHook->pAdmin && pHook->pAdmin->cmdGrpId == grpid)
1.637 + {
1.638 + if (bits)
1.639 + {
1.640 + pHook->pAdmin->eflags = bits;
1.641 + } else {
1.642 + pHook->pAdmin->eflags = pHook->pAdmin->flags;
1.643 + }
1.644 + }
1.645 + }
1.646 + }
1.647 + }
1.648 +}
1.649 +
1.650 +void ConCmdManager::RemoveConCmd(ConCmdInfo *info)
1.651 +{
1.652 + /* Remove console-specific information
1.653 + * This should always be true as of right now
1.654 + */
1.655 + if (info->pCmd)
1.656 + {
1.657 + /* Remove from the trie */
1.658 + sm_trie_delete(m_pCmds, info->pCmd->GetName());
1.659 +
1.660 + if (info->sourceMod)
1.661 + {
1.662 + /* Unlink from SourceMM */
1.663 + g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
1.664 + /* Delete the command's memory */
1.665 + char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
1.666 + char *new_name = const_cast<char *>(info->pCmd->GetName());
1.667 + delete [] new_help;
1.668 + delete [] new_name;
1.669 + delete info->pCmd;
1.670 + } else {
1.671 + /* Remove the external hook */
1.672 + SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
1.673 + }
1.674 + }
1.675 +
1.676 + /* Remove from list */
1.677 + m_CmdList.remove(info);
1.678 +
1.679 + delete info;
1.680 +}
1.681 +
1.682 +ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
1.683 +{
1.684 + ConCmdInfo *pInfo;
1.685 + if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
1.686 + {
1.687 + pInfo = new ConCmdInfo();
1.688 + /* Find the commandopan */
1.689 + ConCommandBase *pBase = icvar->GetCommands();
1.690 + ConCommand *pCmd = NULL;
1.691 + while (pBase)
1.692 + {
1.693 + if (strcmp(pBase->GetName(), name) == 0)
1.694 + {
1.695 + /* Don't want to return convar with same name */
1.696 + if (!pBase->IsCommand())
1.697 + {
1.698 + return NULL;
1.699 + }
1.700 +
1.701 + pCmd = (ConCommand *)pBase;
1.702 + break;
1.703 + }
1.704 + pBase = const_cast<ConCommandBase *>(pBase->GetNext());
1.705 + }
1.706 +
1.707 + if (!pCmd)
1.708 + {
1.709 + /* Note that we have to duplicate because the source might not be
1.710 + * a static string, and these expect static memory.
1.711 + */
1.712 + if (!description)
1.713 + {
1.714 + description = "";
1.715 + }
1.716 + char *new_name = sm_strdup(name);
1.717 + char *new_help = sm_strdup(description);
1.718 + pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
1.719 + pInfo->sourceMod = true;
1.720 + } else {
1.721 + SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
1.722 + }
1.723 +
1.724 + pInfo->pCmd = pCmd;
1.725 +
1.726 + sm_trie_insert(m_pCmds, name, pInfo);
1.727 + AddToCmdList(pInfo);
1.728 + }
1.729 +
1.730 + return pInfo;
1.731 +}
1.732 +
1.733 +void ConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
1.734 +{
1.735 + if (argcount >= 3)
1.736 + {
1.737 + const char *text = engine->Cmd_Argv(2);
1.738 + int id = atoi(text);
1.739 + CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
1.740 +
1.741 + if (!pPlugin)
1.742 + {
1.743 + g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
1.744 + return;
1.745 + }
1.746 +
1.747 + CmdList *pList;
1.748 + if (!pPlugin->GetProperty("CommandList", (void **)&pList))
1.749 + {
1.750 + g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
1.751 + return;
1.752 + }
1.753 + if (!pList->size())
1.754 + {
1.755 + g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
1.756 + return;
1.757 + }
1.758 +
1.759 + CmdList::iterator iter;
1.760 + const char *type = NULL;
1.761 + const char *name;
1.762 + const char *help;
1.763 + g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
1.764 + for (iter=pList->begin();
1.765 + iter!=pList->end();
1.766 + iter++)
1.767 + {
1.768 + PlCmdInfo &cmd = (*iter);
1.769 + if (cmd.type == Cmd_Server)
1.770 + {
1.771 + type = "server";
1.772 + } else if (cmd.type == Cmd_Console) {
1.773 + type = "console";
1.774 + } else if (cmd.type == Cmd_Admin) {
1.775 + type = "admin";
1.776 + }
1.777 + name = cmd.pInfo->pCmd->GetName();
1.778 + if (cmd.pHook->helptext.size())
1.779 + {
1.780 + help = cmd.pHook->helptext.c_str();
1.781 + } else {
1.782 + help = cmd.pInfo->pCmd->GetHelpText();
1.783 + }
1.784 + g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
1.785 + }
1.786 +
1.787 + return;
1.788 + }
1.789 +
1.790 + g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
1.791 +}