core/ConCmdManager.cpp
changeset 579 3c25cd99fb05
child 582 00c843a1bb18
     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 +}