core/sm_srvcmds.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 25 12:13:30 2013 -0700 (2013-08-25)
changeset 4070 4bd9fbf607d9
parent 3872 4c93f74cae51
child 4074 84b49d686a77
permissions -rw-r--r--
Switch ConVar, CoreConfig off KTrie (bug 5884 part 8, r=ds).
     1 /**
     2  * vim: set ts=4 sw=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "sm_srvcmds.h"
    33 #include <sourcemod_version.h>
    34 #include "sm_stringutil.h"
    35 #include "CoreConfig.h"
    36 #include "ConVarManager.h"
    37 #include "logic_bridge.h"
    38 
    39 RootConsoleMenu g_RootMenu;
    40 
    41 ConVar sourcemod_version("sourcemod_version", SM_VERSION_STRING, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY, "SourceMod Version");
    42 
    43 RootConsoleMenu::RootConsoleMenu()
    44 {
    45 	m_pCommands = sm_trie_create();
    46 	m_CfgExecDone = false;
    47 }
    48 
    49 RootConsoleMenu::~RootConsoleMenu()
    50 {
    51 	sm_trie_destroy(m_pCommands);
    52 
    53 	List<ConsoleEntry *>::iterator iter;
    54 	for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
    55 	{
    56 		delete (*iter);
    57 	}
    58 	m_Menu.clear();
    59 }
    60 
    61 void RootConsoleMenu::OnSourceModStartup(bool late)
    62 {
    63 #if SOURCE_ENGINE >= SE_ORANGEBOX
    64 	g_pCVar = icvar;
    65 #endif
    66 	CONVAR_REGISTER(this);
    67 	AddRootConsoleCommand("version", "Display version information", this);
    68 	AddRootConsoleCommand("credits", "Display credits listing", this);
    69 }
    70 
    71 void RootConsoleMenu::OnSourceModAllInitialized()
    72 {
    73 	sharesys->AddInterface(NULL, this);
    74 }
    75 
    76 void RootConsoleMenu::OnSourceModShutdown()
    77 {
    78 	RemoveRootConsoleCommand("credits", this);
    79 	RemoveRootConsoleCommand("version", this);
    80 }
    81 
    82 bool RootConsoleMenu::RegisterConCommandBase(ConCommandBase *pCommand)
    83 {
    84 	META_REGCVAR(pCommand);
    85 
    86 	/* Override values of convars created by SourceMod convar manager if specified on command line */
    87 	const char *cmdLineValue = icvar->GetCommandLineValue(pCommand->GetName());
    88 	if (cmdLineValue && !pCommand->IsCommand())
    89 	{
    90 		static_cast<ConVar *>(pCommand)->SetValue(cmdLineValue);
    91 	}
    92 
    93 	return true;
    94 }
    95 
    96 void RootConsoleMenu::ConsolePrint(const char *fmt, ...)
    97 {
    98 	char buffer[512];
    99 
   100 	va_list ap;
   101 	va_start(ap, fmt);
   102 	size_t len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
   103 	va_end(ap);
   104 
   105 	if (len >= sizeof(buffer) - 1)
   106 	{
   107 		buffer[510] = '\n';
   108 		buffer[511] = '\0';
   109 	} else {
   110 		buffer[len++] = '\n';
   111 		buffer[len] = '\0';
   112 	}
   113 	
   114 	META_CONPRINT(buffer);
   115 }
   116 
   117 bool RootConsoleMenu::AddRootConsoleCommand(const char *cmd, const char *text, IRootConsoleCommand *pHandler)
   118 {
   119 	return _AddRootConsoleCommand(cmd, text, pHandler, false);
   120 }
   121 
   122 bool RootConsoleMenu::AddRootConsoleCommand2(const char *cmd, const char *text, IRootConsoleCommand *pHandler)
   123 {
   124 	return _AddRootConsoleCommand(cmd, text, pHandler, true);
   125 }
   126 
   127 bool RootConsoleMenu::_AddRootConsoleCommand(const char *cmd,
   128 											 const char *text,
   129 											 IRootConsoleCommand *pHandler,
   130 											 bool version2)
   131 {
   132 	if (sm_trie_retrieve(m_pCommands, cmd, NULL))
   133 	{
   134 		return false;
   135 	}
   136 
   137 	/* Sort this into the menu */
   138 	List<ConsoleEntry *>::iterator iter = m_Menu.begin();
   139 	ConsoleEntry *pEntry;
   140 	bool inserted = false;
   141 	while (iter != m_Menu.end())
   142 	{
   143 		pEntry = (*iter);
   144 		if (strcmp(cmd, pEntry->command.c_str()) < 0)
   145 		{
   146 			ConsoleEntry *pNew = new ConsoleEntry;
   147 			pNew->command.assign(cmd);
   148 			pNew->description.assign(text);
   149 			pNew->version2 = version2;
   150 			pNew->cmd = pHandler;
   151 			sm_trie_insert(m_pCommands, cmd, pNew);
   152 			m_Menu.insert(iter, pNew);
   153 			inserted = true;
   154 			break;
   155 		}
   156 		iter++;
   157 	}
   158 
   159 	if (!inserted)
   160 	{
   161 		ConsoleEntry *pNew = new ConsoleEntry;
   162 		pNew->command.assign(cmd);
   163 		pNew->description.assign(text);
   164 		pNew->version2 = version2;
   165 		pNew->cmd = pHandler;
   166 		sm_trie_insert(m_pCommands, cmd, pNew);
   167 		m_Menu.push_back(pNew);
   168 	}
   169 
   170 	return true;
   171 }
   172 
   173 bool RootConsoleMenu::RemoveRootConsoleCommand(const char *cmd, IRootConsoleCommand *pHandler)
   174 {
   175 	sm_trie_delete(m_pCommands, cmd);
   176 
   177 	List<ConsoleEntry *>::iterator iter;
   178 	ConsoleEntry *pEntry;
   179 	for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
   180 	{
   181 		pEntry = (*iter);
   182 		if (pEntry->command.compare(cmd) == 0)
   183 		{
   184 			delete pEntry;
   185 			m_Menu.erase(iter);
   186 			break;
   187 		}
   188 	}
   189 
   190 	return true;
   191 }
   192 
   193 void RootConsoleMenu::DrawGenericOption(const char *cmd, const char *text)
   194 {
   195 	char buffer[255];
   196 	size_t len, cmdlen = strlen(cmd);
   197 
   198 	len = UTIL_Format(buffer, sizeof(buffer), "    %s", cmd);
   199 	if (cmdlen < 16)
   200 	{
   201 		size_t num = 16 - cmdlen;
   202 		for (size_t i = 0; i < num; i++)
   203 		{
   204 			buffer[len++] = ' ';
   205 		}
   206 		len += snprintf(&buffer[len], sizeof(buffer) - len, " - %s", text);
   207 		ConsolePrint("%s", buffer);
   208 	}
   209 }
   210 
   211 const char *RootConsoleMenu::GetInterfaceName()
   212 {
   213 	return SMINTERFACE_ROOTCONSOLE_NAME;
   214 }
   215 
   216 unsigned int RootConsoleMenu::GetInterfaceVersion()
   217 {
   218 	return SMINTERFACE_ROOTCONSOLE_VERSION;
   219 }
   220 
   221 #if SOURCE_ENGINE==SE_EPISODEONE || SOURCE_ENGINE==SE_DARKMESSIAH
   222 class CCommandArgs : public ICommandArgs
   223 {
   224 public:
   225 	CCommandArgs(const CCommand& _cmd)
   226 	{
   227 	}
   228 	const char *Arg(int n) const
   229 	{
   230 		return engine->Cmd_Argv(n);
   231 	}
   232 	int ArgC() const
   233 	{
   234 		return engine->Cmd_Argc();
   235 	}
   236 	const char *ArgS() const
   237 	{
   238 		return engine->Cmd_Args();
   239 	}
   240 };
   241 #else
   242 class CCommandArgs : public ICommandArgs
   243 {
   244 	const CCommand *cmd;
   245 public:
   246 	CCommandArgs(const CCommand& _cmd) : cmd(&_cmd)
   247 	{
   248 	}
   249 	const char *Arg(int n) const
   250 	{
   251 		return cmd->Arg(n);
   252 	}
   253 	int ArgC() const
   254 	{
   255 		return cmd->ArgC();
   256 	}
   257 	const char *ArgS() const
   258 	{
   259 		return cmd->ArgS();
   260 	}
   261 };
   262 #endif
   263 
   264 void RootConsoleMenu::GotRootCmd(const CCommand &cmd)
   265 {
   266 	unsigned int argnum = cmd.ArgC();
   267 
   268 	if (argnum >= 2)
   269 	{
   270 		const char *cmdname = cmd.Arg(1);
   271 		if (strcmp(cmdname, "internal") == 0)
   272 		{
   273 			if (argnum >= 3)
   274 			{
   275 				const char *arg = cmd.Arg(2);
   276 				if (strcmp(arg, "1") == 0)
   277 				{
   278 					SM_ConfigsExecuted_Global();
   279 				}
   280 				else if (strcmp(arg, "2") == 0)
   281 				{
   282 					if (argnum >= 4)
   283 					{
   284 						SM_ConfigsExecuted_Plugin(atoi(cmd.Arg(3)));
   285 					}
   286 				}
   287 			}
   288 			return;
   289 		}
   290 
   291 		CCommandArgs ocmd(cmd);
   292 
   293 		ConsoleEntry *entry;
   294 		if (sm_trie_retrieve(m_pCommands, cmdname, (void **)&entry))
   295 		{
   296 			if (entry->version2)
   297 			{
   298 				entry->cmd->OnRootConsoleCommand2(cmdname, &ocmd);
   299 			}
   300 			else
   301 			{
   302 				entry->cmd->OnRootConsoleCommand(cmdname, cmd);
   303 			}
   304 			return;
   305 		}
   306 	}
   307 
   308 	ConsolePrint("SourceMod Menu:");
   309 	ConsolePrint("Usage: sm <command> [arguments]");
   310 
   311 	List<ConsoleEntry *>::iterator iter;
   312 	ConsoleEntry *pEntry;
   313 	for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
   314 	{
   315 		pEntry = (*iter);
   316 		DrawGenericOption(pEntry->command.c_str(), pEntry->description.c_str());
   317 	}
   318 }
   319 
   320 void RootConsoleMenu::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
   321 {
   322 	if (strcmp(cmdname, "credits") == 0)
   323 	{
   324 		ConsolePrint(" SourceMod was developed by AlliedModders, LLC.");
   325 		ConsolePrint(" Development would not have been possible without the following people:");
   326 		ConsolePrint("  David \"BAILOPAN\" Anderson");
   327 		ConsolePrint("  Matt \"pRED\" Woodrow");
   328 		ConsolePrint("  Scott \"DS\" Ehlert");
   329 		ConsolePrint("  Fyren");
   330 		ConsolePrint("  Nicholas \"psychonic\" Hastings");
   331 		ConsolePrint("  Asher \"asherkin\" Baker");
   332 		ConsolePrint("  Borja \"faluco\" Ferrer");
   333 		ConsolePrint("  Pavol \"PM OnoTo\" Marko");
   334 		ConsolePrint(" Special thanks to Liam, ferret, and Mani");
   335 		ConsolePrint(" Special thanks to Viper and SteamFriends");
   336 		ConsolePrint(" http://www.sourcemod.net/");
   337 	}
   338 	else if (strcmp(cmdname, "version") == 0)
   339 	{
   340 		ConsolePrint(" SourceMod Version Information:");
   341 		ConsolePrint("    SourceMod Version: %s", SM_VERSION_STRING);
   342 		ConsolePrint("    SourcePawn Engine: %s (build %s)", g_pSourcePawn2->GetEngineName(), g_pSourcePawn2->GetVersionString());
   343 		ConsolePrint("    SourcePawn API: v1 = %d, v2 = %d", g_pSourcePawn->GetEngineAPIVersion(), g_pSourcePawn2->GetAPIVersion());
   344 		ConsolePrint("    Compiled on: %s %s", __DATE__, __TIME__);
   345 		ConsolePrint("    Build ID: %s", SM_BUILD_UNIQUEID);
   346 		ConsolePrint("    http://www.sourcemod.net/");
   347 	}
   348 }
   349 
   350 CON_COMMAND(sm, "SourceMod Menu")
   351 {
   352 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   353 	CCommand args;
   354 #endif
   355 	g_RootMenu.GotRootCmd(args);
   356 }
   357 
   358 FILE *g_pHndlLog = NULL;
   359 
   360 void write_handles_to_log(const char *fmt, ...)
   361 {
   362 	va_list ap;
   363 	
   364 	va_start(ap, fmt);
   365 	vfprintf(g_pHndlLog, fmt, ap);
   366 	fprintf(g_pHndlLog, "\n");
   367 	va_end(ap);
   368 }
   369 
   370 void write_handles_to_game(const char *fmt, ...)
   371 {
   372 	size_t len;
   373 	va_list ap;
   374 	char buffer[1024];
   375 	
   376 	va_start(ap, fmt);
   377 	len = UTIL_FormatArgs(buffer, sizeof(buffer)-2, fmt, ap);
   378 	va_end(ap);
   379 
   380 	buffer[len] = '\n';
   381 	buffer[len+1] = '\0';
   382 
   383 	engine->LogPrint(buffer);
   384 }
   385 
   386 CON_COMMAND(sm_dump_handles, "Dumps Handle usage to a file for finding Handle leaks")
   387 {
   388 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   389 	CCommand args;
   390 #endif
   391 	if (args.ArgC() < 2)
   392 	{
   393 		g_RootMenu.ConsolePrint("Usage: sm_dump_handles <file> or <log> for game logs");
   394 		return;
   395 	}
   396 
   397 	if (strcmp(args.Arg(1), "log") != 0)
   398 	{
   399 		const char *arg = args.Arg(1);
   400 		FILE *fp = fopen(arg, "wt");
   401 		if (!fp)
   402 		{
   403 			g_RootMenu.ConsolePrint("Could not find file \"%s\"", arg);
   404 			return;
   405 		}
   406 
   407 		g_pHndlLog = fp;
   408 		logicore.DumpHandles(write_handles_to_log);
   409 		g_pHndlLog = NULL;
   410 
   411 		fclose(fp);
   412 	}
   413 	else
   414 	{
   415 		logicore.DumpHandles(write_handles_to_game);
   416 	}
   417 }
   418