core/CConCmdManager.cpp
author Scott Ehlert <ds@alliedmods.net>
Sat Feb 17 05:33:31 2007 +0000 (2007-02-17)
changeset 511 c29f8796bcf4
parent 504 1d6072ba774e
child 513 0c299b5ad420
permissions -rw-r--r--
Initial fixes to ensure static strings passed to new convars
Also fixed minor warning in smn_bitbuffer.cpp
     1 /**
     2 * ===============================================================
     3 * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4 * ===============================================================
     5 *
     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
    10 *
    11 * Version: $Id$
    12 */
    13 
    14 #include "CConCmdManager.h"
    15 #include "sm_srvcmds.h"
    16 #include "sm_stringutil.h"
    17 
    18 CConCmdManager g_ConCmds;
    19 
    20 SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
    21 SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
    22 
    23 struct PlCmdInfo
    24 {
    25 	ConCmdInfo *pInfo;
    26 	CmdType type;
    27 };
    28 
    29 typedef List<PlCmdInfo> CmdList;
    30 
    31 CConCmdManager::CConCmdManager()
    32 {
    33 	m_pCmds = sm_trie_create();
    34 }
    35 
    36 CConCmdManager::~CConCmdManager()
    37 {
    38 	sm_trie_destroy(m_pCmds);
    39 }
    40 
    41 void CConCmdManager::OnSourceModAllInitialized()
    42 {
    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);
    46 }
    47 
    48 void CConCmdManager::OnSourceModShutdown()
    49 {
    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);
    54 }
    55 
    56 void CConCmdManager::OnPluginLoaded(IPlugin *plugin)
    57 {
    58 	/* Nothing yet... */
    59 }
    60 
    61 void CConCmdManager::OnPluginDestroyed(IPlugin *plugin)
    62 {
    63 	CmdList *pList;
    64 	List<ConCmdInfo *> removed;
    65 	if (plugin->GetProperty("CommandList", (void **)&pList, true))
    66 	{
    67 		CmdList::iterator iter;
    68 		for (iter=pList->begin();
    69 			 iter!=pList->end();
    70 			 iter++)
    71 		{
    72 			PlCmdInfo &cmd = (*iter);
    73 			if (cmd.type == Cmd_Server
    74 				|| cmd.type == Cmd_Console)
    75 			{
    76 				ConCmdInfo *pInfo = cmd.pInfo;
    77 				/* See if this is being removed */
    78 				if (removed.find(pInfo) != removed.end())
    79 				{
    80 					continue;
    81 				}
    82 				/* See if there are still hooks */
    83 				if (pInfo->srvhooks
    84 					&& pInfo->srvhooks->GetFunctionCount())
    85 				{
    86 					continue;
    87 				}
    88 				if (pInfo->conhooks
    89 					&& pInfo->conhooks->GetFunctionCount())
    90 				{
    91 					continue;
    92 				}
    93 				/* Remove the command */
    94 				RemoveConCmd(pInfo);
    95 				removed.push_back(pInfo);
    96 			}
    97 		}
    98 		delete pList;
    99 	}
   100 }
   101 
   102 void CommandCallback()
   103 {
   104 	g_ConCmds.InternalDispatch();
   105 }
   106 
   107 void CConCmdManager::SetCommandClient(int client)
   108 {
   109 	m_CmdClient = client + 1;
   110 }
   111 
   112 ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type)
   113 {
   114 	const char *cmd = engine->Cmd_Argv(0);
   115 	int args = engine->Cmd_Argc() - 1;
   116 
   117 	ConCmdInfo *pInfo;
   118 	if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
   119 	{
   120 		cell_t result = type;
   121 		if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
   122 		{
   123 			pInfo->conhooks->PushCell(client);
   124 			pInfo->conhooks->PushCell(args);
   125 			pInfo->conhooks->Execute(&result);
   126 		}
   127 		if (result >= Pl_Stop)
   128 		{
   129 			return Pl_Stop;
   130 		}
   131 		type = (ResultType)result;
   132 	}
   133 
   134 	return type;
   135 }
   136 
   137 void CConCmdManager::InternalDispatch()
   138 {
   139 	/**
   140 	 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
   141 	 * OR this dispatch.  They will NEVER go through both.
   142 	 * -- 
   143 	 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
   144 	 */
   145 
   146 	const char *cmd = engine->Cmd_Argv(0);
   147 	ConCmdInfo *pInfo;
   148 	if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
   149 	{
   150 		return;
   151 	}
   152 
   153 	cell_t result = Pl_Continue;
   154 	int args = engine->Cmd_Argc() - 1;
   155 
   156 	if (m_CmdClient == 0)
   157 	{
   158 		if (pInfo->srvhooks && pInfo->srvhooks->GetFunctionCount())
   159 		{
   160 			pInfo->srvhooks->PushCell(args);
   161 			pInfo->srvhooks->Execute(&result);
   162 		}
   163 	
   164 		/* Check if there's an early stop */
   165 		if (result >= Pl_Stop)
   166 		{
   167 			if (!pInfo->sourceMod)
   168 			{
   169 				RETURN_META(MRES_SUPERCEDE);
   170 			}
   171 			return;
   172 		}
   173 	}
   174 
   175 	/* Execute console command hooks */
   176 	if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount())
   177 	{
   178 		pInfo->conhooks->PushCell(m_CmdClient);
   179 		pInfo->conhooks->PushCell(args);
   180 		pInfo->conhooks->Execute(&result);
   181 	}
   182 
   183 	if (result >= Pl_Handled)
   184 	{
   185 		if (!pInfo->sourceMod)
   186 		{
   187 			RETURN_META(MRES_SUPERCEDE);
   188 		}
   189 		return;
   190 	}
   191 }
   192 
   193 void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction, 
   194 									   const char *name, 
   195 									   const char *description, 
   196 									   int flags)
   197 {
   198 	ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
   199 
   200 	if (!pInfo->conhooks)
   201 	{
   202 		pInfo->conhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 2, NULL, Param_Cell, Param_Cell);
   203 	}
   204 
   205 	pInfo->conhooks->AddFunction(pFunction);
   206 
   207 	/* Add to the plugin */
   208 	CmdList *pList;
   209 	IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
   210 	if (!pPlugin->GetProperty("CommandList", (void **)&pList))
   211 	{
   212 		pList = new CmdList();
   213 		pPlugin->SetProperty("CommandList", pList);
   214 	}
   215 	PlCmdInfo info;
   216 	info.pInfo = pInfo;
   217 	info.type = Cmd_Console;
   218 	pList->push_back(info);
   219 }
   220 
   221 void CConCmdManager::AddServerCommand(IPluginFunction *pFunction, 
   222 									  const char *name, 
   223 									  const char *description, 
   224 									  int flags)
   225 
   226 {
   227 	ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
   228 
   229 	if (!pInfo->srvhooks)
   230 	{
   231 		pInfo->srvhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 1, NULL, Param_Cell);
   232 	}
   233 
   234 	pInfo->srvhooks->AddFunction(pFunction);
   235 
   236 	/* Add to the plugin */
   237 	CmdList *pList;
   238 	IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
   239 	if (!pPlugin->GetProperty("CommandList", (void **)&pList))
   240 	{
   241 		pList = new CmdList();
   242 		pPlugin->SetProperty("CommandList", pList);
   243 	}
   244 	PlCmdInfo info;
   245 	info.pInfo = pInfo;
   246 	info.type = Cmd_Server;
   247 	pList->push_back(info);
   248 }
   249 
   250 void CConCmdManager::AddToCmdList(ConCmdInfo *info)
   251 {
   252 	List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
   253 	ConCmdInfo *pInfo;
   254 	bool inserted = false;
   255 	const char *orig = NULL;
   256 
   257 	orig = info->pCmd->GetName();
   258 
   259 	/* Insert this into the help list, SORTED alphabetically. */
   260 	while (iter != m_CmdList.end())
   261 	{
   262 		const char *cmd = NULL;
   263 		pInfo = (*iter);
   264 		cmd = pInfo->pCmd->GetName();
   265 		if (strcmp(orig, cmd) < 0)
   266 		{
   267 			m_CmdList.insert(iter, info);
   268 			inserted = true;
   269 			break;
   270 		}
   271 		iter++;
   272 	}
   273 
   274 	if (!inserted)
   275 	{
   276 		m_CmdList.push_back(info);
   277 	}
   278 }
   279 
   280 void CConCmdManager::RemoveConCmd(ConCmdInfo *info)
   281 {
   282 	/* Remove console-specific information
   283 	 * This should always be true as of right now
   284 	 */
   285 	if (info->pCmd)
   286 	{
   287 		/* Remove from the trie */
   288 		sm_trie_delete(m_pCmds, info->pCmd->GetName());
   289 
   290 		if (info->sourceMod)
   291 		{
   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());
   297 			delete [] new_help;
   298 			delete [] new_name;
   299 			delete info->pCmd;
   300 		} else {
   301 			/* Remove the external hook */
   302 			SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
   303 		}
   304 	}
   305 	
   306 	/* Remove from list */
   307 	m_CmdList.remove(info);
   308 	
   309 	/* Free forwards */
   310 	if (info->srvhooks)
   311 	{
   312 		g_Forwards.ReleaseForward(info->srvhooks);
   313 	}
   314 }
   315 
   316 ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
   317 {
   318 	ConCmdInfo *pInfo;
   319 	if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
   320 	{
   321 		pInfo = new ConCmdInfo();
   322 		/* Find the commandopan */
   323 		ConCommandBase *pBase = icvar->GetCommands();
   324 		ConCommand *pCmd = NULL;
   325 		while (pBase)
   326 		{
   327 			if (pBase->IsCommand()
   328 				&& (strcmp(pBase->GetName(), name) == 0))
   329 			{
   330 				pCmd = (ConCommand *)pBase;
   331 				break;
   332 			}
   333 			pBase = const_cast<ConCommandBase *>(pBase->GetNext());
   334 		}
   335 
   336 		if (!pCmd)
   337 		{
   338 			/* Note that we have to duplicate because the source might not be 
   339 			 * a static string, and these expect static memory.
   340 			 */
   341 			if (!description)
   342 			{
   343 				description = "";
   344 			}
   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;
   349 		} else {
   350 			SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
   351 		}
   352 
   353 		pInfo->pCmd = pCmd;
   354 
   355 		sm_trie_insert(m_pCmds, name, pInfo);
   356 		AddToCmdList(pInfo);
   357 	}
   358 
   359 	return pInfo;
   360 }
   361 
   362 void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
   363 {
   364 	if (argcount >= 3)
   365 	{
   366 		const char *text = engine->Cmd_Argv(2);
   367 		int id = atoi(text);
   368 		CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
   369 
   370 		if (!pPlugin)
   371 		{
   372 			g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
   373 			return;
   374 		}
   375 
   376 		CmdList *pList;
   377 		if (!pPlugin->GetProperty("CommandList", (void **)&pList))
   378 		{
   379 			g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
   380 			return;
   381 		}
   382 		if (!pList->size())
   383 		{
   384 			g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename());
   385 			return;
   386 		}
   387 
   388 		CmdList::iterator iter;
   389 		const char *type = NULL;
   390 		const char *name;
   391 		const char *help;
   392 		g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
   393 		for (iter=pList->begin();
   394 			 iter!=pList->end();
   395 			 iter++)
   396 		{
   397 			PlCmdInfo &cmd = (*iter);
   398 			if (cmd.type == Cmd_Server)
   399 			{
   400 				type = "server";
   401 			} else if (cmd.type == Cmd_Console) {
   402 				type = "console";
   403 			} else if (cmd.type == Cmd_Client) {
   404 				type = "client";
   405 			}
   406 			name = cmd.pInfo->pCmd->GetName();
   407 			help = cmd.pInfo->pCmd->GetHelpText();
   408 			g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);		
   409 		}
   410 
   411 		return;
   412 	}
   413 
   414 	g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
   415 }