core/sm_srvcmds.cpp
author Asher Baker <asherkin@gmail.com>
Fri Feb 17 03:28:02 2012 +0000 (2012-02-17)
changeset 3458 62ad320dd01a
parent 3311 534a91be575d
child 3621 d5510357db56
permissions -rw-r--r--
Generate and upload breakpad symbols if BREAKPAD_SYMBOL_SERVER variable is set. (bug 5229, r=dvander)
     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 "HandleSys.h"
    36 #include "CoreConfig.h"
    37 #include "ConVarManager.h"
    38 #include "ShareSys.h"
    39 
    40 RootConsoleMenu g_RootMenu;
    41 
    42 ConVar sourcemod_version("sourcemod_version", SM_FULL_VERSION, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY, "SourceMod Version");
    43 
    44 RootConsoleMenu::RootConsoleMenu()
    45 {
    46 	m_pCommands = sm_trie_create();
    47 	m_CfgExecDone = false;
    48 }
    49 
    50 RootConsoleMenu::~RootConsoleMenu()
    51 {
    52 	sm_trie_destroy(m_pCommands);
    53 
    54 	List<ConsoleEntry *>::iterator iter;
    55 	for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
    56 	{
    57 		delete (*iter);
    58 	}
    59 	m_Menu.clear();
    60 }
    61 
    62 void RootConsoleMenu::OnSourceModStartup(bool late)
    63 {
    64 #if SOURCE_ENGINE >= SE_ORANGEBOX
    65 	g_pCVar = icvar;
    66 #endif
    67 	CONVAR_REGISTER(this);
    68 	AddRootConsoleCommand("version", "Display version information", this);
    69 	AddRootConsoleCommand("credits", "Display credits listing", this);
    70 }
    71 
    72 void RootConsoleMenu::OnSourceModAllInitialized()
    73 {
    74 	g_ShareSys.AddInterface(NULL, this);
    75 }
    76 
    77 void RootConsoleMenu::OnSourceModShutdown()
    78 {
    79 	RemoveRootConsoleCommand("credits", this);
    80 	RemoveRootConsoleCommand("version", this);
    81 }
    82 
    83 bool RootConsoleMenu::RegisterConCommandBase(ConCommandBase *pCommand)
    84 {
    85 	META_REGCVAR(pCommand);
    86 
    87 	/* Override values of convars created by SourceMod convar manager if specified on command line */
    88 	const char *cmdLineValue = icvar->GetCommandLineValue(pCommand->GetName());
    89 	if (cmdLineValue && !pCommand->IsCommand())
    90 	{
    91 		static_cast<ConVar *>(pCommand)->SetValue(cmdLineValue);
    92 	}
    93 
    94 	return true;
    95 }
    96 
    97 void RootConsoleMenu::ConsolePrint(const char *fmt, ...)
    98 {
    99 	char buffer[512];
   100 
   101 	va_list ap;
   102 	va_start(ap, fmt);
   103 	size_t len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
   104 	va_end(ap);
   105 
   106 	if (len >= sizeof(buffer) - 1)
   107 	{
   108 		buffer[510] = '\n';
   109 		buffer[511] = '\0';
   110 	} else {
   111 		buffer[len++] = '\n';
   112 		buffer[len] = '\0';
   113 	}
   114 	
   115 	META_CONPRINT(buffer);
   116 }
   117 
   118 bool RootConsoleMenu::AddRootConsoleCommand(const char *cmd, const char *text, IRootConsoleCommand *pHandler)
   119 {
   120 	return _AddRootConsoleCommand(cmd, text, pHandler, false);
   121 }
   122 
   123 bool RootConsoleMenu::AddRootConsoleCommand2(const char *cmd, const char *text, IRootConsoleCommand *pHandler)
   124 {
   125 	return _AddRootConsoleCommand(cmd, text, pHandler, true);
   126 }
   127 
   128 bool RootConsoleMenu::_AddRootConsoleCommand(const char *cmd,
   129 											 const char *text,
   130 											 IRootConsoleCommand *pHandler,
   131 											 bool version2)
   132 {
   133 	if (sm_trie_retrieve(m_pCommands, cmd, NULL))
   134 	{
   135 		return false;
   136 	}
   137 
   138 	/* Sort this into the menu */
   139 	List<ConsoleEntry *>::iterator iter = m_Menu.begin();
   140 	ConsoleEntry *pEntry;
   141 	bool inserted = false;
   142 	while (iter != m_Menu.end())
   143 	{
   144 		pEntry = (*iter);
   145 		if (strcmp(cmd, pEntry->command.c_str()) < 0)
   146 		{
   147 			ConsoleEntry *pNew = new ConsoleEntry;
   148 			pNew->command.assign(cmd);
   149 			pNew->description.assign(text);
   150 			pNew->version2 = version2;
   151 			pNew->cmd = pHandler;
   152 			sm_trie_insert(m_pCommands, cmd, pNew);
   153 			m_Menu.insert(iter, pNew);
   154 			inserted = true;
   155 			break;
   156 		}
   157 		iter++;
   158 	}
   159 
   160 	if (!inserted)
   161 	{
   162 		ConsoleEntry *pNew = new ConsoleEntry;
   163 		pNew->command.assign(cmd);
   164 		pNew->description.assign(text);
   165 		pNew->version2 = version2;
   166 		pNew->cmd = pHandler;
   167 		sm_trie_insert(m_pCommands, cmd, pNew);
   168 		m_Menu.push_back(pNew);
   169 	}
   170 
   171 	return true;
   172 }
   173 
   174 bool RootConsoleMenu::RemoveRootConsoleCommand(const char *cmd, IRootConsoleCommand *pHandler)
   175 {
   176 	sm_trie_delete(m_pCommands, cmd);
   177 
   178 	List<ConsoleEntry *>::iterator iter;
   179 	ConsoleEntry *pEntry;
   180 	for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
   181 	{
   182 		pEntry = (*iter);
   183 		if (pEntry->command.compare(cmd) == 0)
   184 		{
   185 			delete pEntry;
   186 			m_Menu.erase(iter);
   187 			break;
   188 		}
   189 	}
   190 
   191 	return true;
   192 }
   193 
   194 void RootConsoleMenu::DrawGenericOption(const char *cmd, const char *text)
   195 {
   196 	char buffer[255];
   197 	size_t len, cmdlen = strlen(cmd);
   198 
   199 	len = UTIL_Format(buffer, sizeof(buffer), "    %s", cmd);
   200 	if (cmdlen < 16)
   201 	{
   202 		size_t num = 16 - cmdlen;
   203 		for (size_t i = 0; i < num; i++)
   204 		{
   205 			buffer[len++] = ' ';
   206 		}
   207 		len += snprintf(&buffer[len], sizeof(buffer) - len, " - %s", text);
   208 		ConsolePrint("%s", buffer);
   209 	}
   210 }
   211 
   212 const char *RootConsoleMenu::GetInterfaceName()
   213 {
   214 	return SMINTERFACE_ROOTCONSOLE_NAME;
   215 }
   216 
   217 unsigned int RootConsoleMenu::GetInterfaceVersion()
   218 {
   219 	return SMINTERFACE_ROOTCONSOLE_VERSION;
   220 }
   221 
   222 #if SOURCE_ENGINE==SE_EPISODEONE || SOURCE_ENGINE==SE_DARKMESSIAH
   223 class CCommandArgs : public ICommandArgs
   224 {
   225 public:
   226 	CCommandArgs(const CCommand& _cmd)
   227 	{
   228 	}
   229 	const char *Arg(int n) const
   230 	{
   231 		return engine->Cmd_Argv(n);
   232 	}
   233 	int ArgC() const
   234 	{
   235 		return engine->Cmd_Argc();
   236 	}
   237 	const char *ArgS() const
   238 	{
   239 		return engine->Cmd_Args();
   240 	}
   241 };
   242 #else
   243 class CCommandArgs : public ICommandArgs
   244 {
   245 	const CCommand *cmd;
   246 public:
   247 	CCommandArgs(const CCommand& _cmd) : cmd(&_cmd)
   248 	{
   249 	}
   250 	const char *Arg(int n) const
   251 	{
   252 		return cmd->Arg(n);
   253 	}
   254 	int ArgC() const
   255 	{
   256 		return cmd->ArgC();
   257 	}
   258 	const char *ArgS() const
   259 	{
   260 		return cmd->ArgS();
   261 	}
   262 };
   263 #endif
   264 
   265 void RootConsoleMenu::GotRootCmd(const CCommand &cmd)
   266 {
   267 	unsigned int argnum = cmd.ArgC();
   268 
   269 	if (argnum >= 2)
   270 	{
   271 		const char *cmdname = cmd.Arg(1);
   272 		if (strcmp(cmdname, "internal") == 0)
   273 		{
   274 			if (argnum >= 3)
   275 			{
   276 				const char *arg = cmd.Arg(2);
   277 				if (strcmp(arg, "1") == 0)
   278 				{
   279 					SM_ConfigsExecuted_Global();
   280 				}
   281 				else if (strcmp(arg, "2") == 0)
   282 				{
   283 					if (argnum >= 4)
   284 					{
   285 						SM_ConfigsExecuted_Plugin(atoi(cmd.Arg(3)));
   286 					}
   287 				}
   288 			}
   289 			return;
   290 		}
   291 
   292 		CCommandArgs ocmd(cmd);
   293 
   294 		ConsoleEntry *entry;
   295 		if (sm_trie_retrieve(m_pCommands, cmdname, (void **)&entry))
   296 		{
   297 			if (entry->version2)
   298 			{
   299 				entry->cmd->OnRootConsoleCommand2(cmdname, &ocmd);
   300 			}
   301 			else
   302 			{
   303 				entry->cmd->OnRootConsoleCommand(cmdname, cmd);
   304 			}
   305 			return;
   306 		}
   307 	}
   308 
   309 	ConsolePrint("SourceMod Menu:");
   310 	ConsolePrint("Usage: sm <command> [arguments]");
   311 
   312 	List<ConsoleEntry *>::iterator iter;
   313 	ConsoleEntry *pEntry;
   314 	for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
   315 	{
   316 		pEntry = (*iter);
   317 		DrawGenericOption(pEntry->command.c_str(), pEntry->description.c_str());
   318 	}
   319 }
   320 
   321 void RootConsoleMenu::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
   322 {
   323 	if (strcmp(cmdname, "credits") == 0)
   324 	{
   325 		ConsolePrint(" SourceMod was developed by AlliedModders, LLC.");
   326 		ConsolePrint(" Development would not have been possible without the following people:");
   327 		ConsolePrint("  David \"BAILOPAN\" Anderson");
   328 		ConsolePrint("  Matt \"pRED\" Woodrow");
   329 		ConsolePrint("  Scott \"DS\" Ehlert");
   330 		ConsolePrint("  Fyren");
   331 		ConsolePrint("  Nicholas \"psychonic\" Hastings");
   332 		ConsolePrint("  Borja \"faluco\" Ferrer");
   333 		ConsolePrint("  Pavol \"PM OnoTo\" Marko");
   334 		ConsolePrint(" Special thanks to asherkin, 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_FULL_VERSION);
   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 		g_HandleSys.Dump(write_handles_to_log);
   409 		g_pHndlLog = NULL;
   410 
   411 		fclose(fp);
   412 	}
   413 	else
   414 	{
   415 		g_HandleSys.Dump(write_handles_to_game);
   416 	}
   417 }
   418