core/CoreConfig.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 15 21:22:24 2014 -0400 (2014-05-15)
changeset 4362 8a15e967d983
parent 4188 89782d5fc91b
permissions -rw-r--r--
Update gamedata for CS:S update.
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 <ITextParsers.h>
    33 #include "CoreConfig.h"
    34 #include "sourcemod.h"
    35 #include "sourcemm_api.h"
    36 #include "sm_srvcmds.h"
    37 #include "sm_stringutil.h"
    38 #include "LibrarySys.h"
    39 #include "Logger.h"
    40 #include "frame_hooks.h"
    41 #include "logic_bridge.h"
    42 #include <sourcemod_version.h>
    43 
    44 #ifdef PLATFORM_WINDOWS
    45 ConVar sm_corecfgfile("sm_corecfgfile", "addons\\sourcemod\\configs\\core.cfg", 0, "SourceMod core configuration file");
    46 #elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
    47 ConVar sm_corecfgfile("sm_corecfgfile", "addons/sourcemod/configs/core.cfg", 0, "SourceMod core configuration file");
    48 #endif
    49 
    50 IForward *g_pOnServerCfg = NULL;
    51 IForward *g_pOnConfigsExecuted = NULL;
    52 IForward *g_pOnAutoConfigsBuffered = NULL;
    53 CoreConfig g_CoreConfig;
    54 bool g_bConfigsExecd = false;
    55 bool g_bServerExecd = false;
    56 bool g_bGotServerStart = false;
    57 bool g_bGotTrigger = false;
    58 ConCommand *g_pExecPtr = NULL;
    59 ConVar *g_ServerCfgFile = NULL;
    60 
    61 void CheckAndFinalizeConfigs();
    62 
    63 #if SOURCE_ENGINE == SE_DOTA
    64 SH_DECL_EXTERN2_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommandContext &, const CCommand &);
    65 void Hook_ExecDispatchPre(const CCommandContext &context, const CCommand &cmd)
    66 #elif SOURCE_ENGINE >= SE_ORANGEBOX
    67 SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
    68 void Hook_ExecDispatchPre(const CCommand &cmd)
    69 #elif SOURCE_ENGINE == SE_DARKMESSIAH
    70 SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
    71 void Hook_ExecDispatchPre()
    72 #else
    73 # if SH_IMPL_VERSION >= 4
    74  extern int __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
    75 # else
    76  extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
    77 # endif
    78 extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
    79 void Hook_ExecDispatchPre()
    80 #endif
    81 {
    82 #if SOURCE_ENGINE <= SE_DARKMESSIAH
    83 	CCommand cmd;
    84 #endif
    85 
    86 	const char *arg = cmd.Arg(1);
    87 
    88 	if (!g_bServerExecd && arg != NULL && strcmp(arg, g_ServerCfgFile->GetString()) == 0)
    89 	{
    90 		g_bGotTrigger = true;
    91 	}
    92 }
    93 
    94 #if SOURCE_ENGINE == SE_DOTA
    95 void Hook_ExecDispatchPost(const CCommandContext &context, const CCommand &cmd)
    96 #elif SOURCE_ENGINE >= SE_ORANGEBOX
    97 void Hook_ExecDispatchPost(const CCommand &cmd)
    98 #else
    99 void Hook_ExecDispatchPost()
   100 #endif
   101 {
   102 	if (g_bGotTrigger)
   103 	{
   104 		g_bGotTrigger = false;
   105 		g_bServerExecd = true;
   106 		CheckAndFinalizeConfigs();
   107 	}
   108 }
   109 
   110 void CheckAndFinalizeConfigs()
   111 {
   112 	if ((g_bServerExecd || g_ServerCfgFile == NULL) && g_bGotServerStart)
   113 	{
   114         g_PendingInternalPush = true;
   115 	}
   116 }
   117 
   118 void CoreConfig::OnSourceModAllInitialized()
   119 {
   120 	g_RootMenu.AddRootConsoleCommand("config", "Set core configuration options", this);
   121 	g_pOnServerCfg = forwardsys->CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
   122 	g_pOnConfigsExecuted = forwardsys->CreateForward("OnConfigsExecuted", ET_Ignore, 0, NULL);
   123 	g_pOnAutoConfigsBuffered = forwardsys->CreateForward("OnAutoConfigsBuffered", ET_Ignore, 0, NULL);
   124 }
   125 
   126 CoreConfig::CoreConfig()
   127 {
   128 }
   129 
   130 CoreConfig::~CoreConfig()
   131 {
   132 }
   133 
   134 void CoreConfig::OnSourceModShutdown()
   135 {
   136 	g_RootMenu.RemoveRootConsoleCommand("config", this);
   137 	forwardsys->ReleaseForward(g_pOnServerCfg);
   138 	forwardsys->ReleaseForward(g_pOnConfigsExecuted);
   139 	forwardsys->ReleaseForward(g_pOnAutoConfigsBuffered);
   140 
   141 	if (g_pExecPtr != NULL)
   142 	{
   143 		SH_REMOVE_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPre), false);
   144 		SH_REMOVE_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPost), true);
   145 		g_pExecPtr = NULL;
   146 	}
   147 }
   148 
   149 void CoreConfig::OnSourceModLevelChange(const char *mapName)
   150 {
   151 	static bool already_checked = false;
   152 
   153 	if (!already_checked)
   154 	{
   155 		if (engine->IsDedicatedServer())
   156 		{
   157 			g_ServerCfgFile = icvar->FindVar("servercfgfile");
   158 		}
   159 		else
   160 		{
   161 			g_ServerCfgFile = icvar->FindVar("lservercfgfile");
   162 		}
   163 
   164 		if (g_ServerCfgFile != NULL)
   165 		{
   166 			g_pExecPtr = FindCommand("exec");
   167 			if (g_pExecPtr != NULL)
   168 			{
   169 				SH_ADD_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPre), false);
   170 				SH_ADD_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPost), true);
   171 			}
   172 			else
   173 			{
   174 				g_ServerCfgFile = NULL;
   175 			}
   176 		}
   177 		already_checked = true;
   178 	}
   179 
   180 	g_bConfigsExecd = false;
   181 	g_bServerExecd = false;
   182 	g_bGotServerStart = false;
   183 	g_bGotTrigger = false;
   184 }
   185 
   186 void CoreConfig::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
   187 {
   188 	int argcount = command.ArgC();
   189 	if (argcount >= 4)
   190 	{
   191 		const char *option = command.Arg(2);
   192 		const char *value = command.Arg(3);
   193 
   194 		char error[255];
   195 
   196 		ConfigResult res = SetConfigOption(option, value, ConfigSource_Console, error, sizeof(error));
   197 
   198 		if (res == ConfigResult_Reject)
   199 		{
   200 			g_RootMenu.ConsolePrint("[SM] Could not set config option \"%s\" to \"%s\". (%s)", option, value, error);
   201 		} else if (res == ConfigResult_Ignore) {
   202 			g_RootMenu.ConsolePrint("[SM] No such config option \"%s\" exists.", option);
   203 		} else {
   204 			g_RootMenu.ConsolePrint("[SM] Config option \"%s\" successfully set to \"%s\".", option, value);
   205 		}
   206 
   207 		return;
   208 	} else if (argcount >= 3) {
   209 		const char *option = command.Arg(2);
   210 		
   211 		const char *value = GetCoreConfigValue(option);
   212 		
   213 		if (value == NULL)
   214 		{
   215 			g_RootMenu.ConsolePrint("[SM] No such config option \"%s\" exists.", option);
   216 		} else {
   217 			g_RootMenu.ConsolePrint("[SM] Config option \"%s\" is set to \"%s\".", option, value);
   218 		}
   219 		
   220 		return;
   221 	}
   222 
   223 	g_RootMenu.ConsolePrint("[SM] Usage: sm config <option> [value]");
   224 }
   225 
   226 void CoreConfig::Initialize()
   227 {
   228 	SMCError err;
   229 	char filePath[PLATFORM_MAX_PATH];
   230 
   231 	/* Try to get command line value of core config convar */
   232 	const char *corecfg = icvar->GetCommandLineValue("sm_corecfgfile");
   233 
   234 	/* If sm_corecfgfile is on the command line, use that
   235 	 * If sm_corecfgfile isn't there, check sm_basepath on the command line and build the path off that
   236 	 * If sm_basepath isn't there, just use the default path for the cfg
   237 	 */
   238 	if (corecfg)
   239 	{
   240 		g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
   241 	}
   242 	else
   243 	{
   244 		const char *basepath = icvar->GetCommandLineValue("sm_basepath");
   245 
   246 		/* Format path to config file */
   247 		if (basepath)
   248 		{
   249 			g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s/%s", g_SourceMod.GetGamePath(), basepath, "configs/core.cfg");
   250 		}
   251 		else
   252 		{
   253 			g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), sm_corecfgfile.GetDefault());
   254 		}
   255 	}
   256 
   257 	/* Reset cached key values */
   258 	m_KeyValues.clear();
   259 
   260 	/* Parse config file */
   261 	if ((err=textparsers->ParseFile_SMC(filePath, this, NULL)) != SMCError_Okay)
   262 	{
   263  		/* :TODO: This won't actually log or print anything :( - So fix that somehow */
   264 		const char *error = textparsers->GetSMCErrorString(err);
   265 		g_Logger.LogFatal("[SM] Error encountered parsing core config file: %s", error ? error : "");
   266 	}
   267 }
   268 
   269 SMCResult CoreConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   270 {
   271 	char error[255];
   272 	ConfigResult err = SetConfigOption(key, value, ConfigSource_File, error, sizeof(error));
   273 
   274 	if (err == ConfigResult_Reject)
   275 	{
   276 		/* This is a fatal error */
   277 		g_Logger.LogFatal("Config error (key: %s) (value: %s) %s", key, value, error);
   278 	}
   279 
   280 	return SMCResult_Continue;
   281 }
   282 
   283 ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength)
   284 {
   285 	ConfigResult result;
   286 
   287 	/* Notify! */
   288 	SMGlobalClass *pBase = SMGlobalClass::head;
   289 	while (pBase)
   290 	{
   291 		if ((result = pBase->OnSourceModConfigChanged(option, value, source, error, maxlength)) != ConfigResult_Ignore)
   292 		{
   293 			return result;
   294 		}
   295 		pBase = pBase->m_pGlobalClassNext;
   296 	}
   297 
   298 	ke::AString vstr(value);
   299 	m_KeyValues.replace(option, ke::Move(vstr));
   300 
   301 	return ConfigResult_Ignore;
   302 }
   303 
   304 const char *CoreConfig::GetCoreConfigValue(const char *key)
   305 {
   306 	StringHashMap<ke::AString>::Result r = m_KeyValues.find(key);
   307 	if (!r.found())
   308 		return NULL;
   309 	return r->value.chars();
   310 }
   311 
   312 bool SM_AreConfigsExecuted()
   313 {
   314 	return g_bConfigsExecd;
   315 }
   316 
   317 inline bool IsPathSepChar(char c)
   318 {
   319 #if defined PLATFORM_WINDOWS
   320 	return (c == '\\' || c == '/');
   321 #elif defined PLATFORM_LINUX || defined PLATFORM_POSIX
   322 	return (c == '/');
   323 #endif
   324 }
   325 
   326 bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
   327 {
   328 	bool will_create = false;
   329 	
   330 	/* See if we should be creating */
   331 	if (can_create && cfg->create)
   332 	{
   333 		will_create = true;
   334 		
   335 		/* If the folder does not exist, attempt to create it.  
   336 		 * We're awfully nice.
   337 		 */
   338 		const char *folder = cfg->folder.c_str();
   339 		char path[PLATFORM_MAX_PATH];
   340 		char build[PLATFORM_MAX_PATH];
   341 
   342 		g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "cfg/%s", folder);
   343 
   344 		if (!g_LibSys.IsPathDirectory(path))
   345 		{
   346 			char *cur_ptr = path;
   347 			size_t len;
   348 			
   349 			g_LibSys.PathFormat(path, sizeof(path), "%s", folder);
   350 			len = g_SourceMod.BuildPath(Path_Game, build, sizeof(build), "cfg");
   351 
   352 			do
   353 			{
   354 				/* Find next suitable path */
   355 				char *next_ptr = cur_ptr;
   356 				while (*next_ptr != '\0')
   357 				{
   358 					if (IsPathSepChar(*next_ptr))
   359 					{
   360 						*next_ptr = '\0';
   361 						next_ptr++;
   362 						break;
   363 					}
   364 					next_ptr++;
   365 				}
   366 				if (*next_ptr == '\0')
   367 				{
   368 					next_ptr = NULL;
   369 				}
   370 				len += g_LibSys.PathFormat(&build[len], 
   371 					sizeof(build)-len,
   372 					"/%s",
   373 					cur_ptr);
   374 				if (!g_LibSys.CreateFolder(build))
   375 				{
   376 					break;
   377 				}
   378 				cur_ptr = next_ptr;
   379 			} while (cur_ptr);
   380 		}
   381 	}
   382 
   383 	/* Check if the file exists. */
   384 	char file[PLATFORM_MAX_PATH];
   385 	char local[PLATFORM_MAX_PATH];
   386 
   387 	if (cfg->folder.size())
   388 	{
   389 		g_LibSys.PathFormat(local, 
   390 			sizeof(local), 
   391 			"%s/%s.cfg",
   392 			cfg->folder.c_str(),
   393 			cfg->autocfg.c_str());
   394 	} else {
   395 		g_LibSys.PathFormat(local,
   396 			sizeof(local),
   397 			"%s.cfg",
   398 			cfg->autocfg.c_str());
   399 	}
   400 	
   401 	g_SourceMod.BuildPath(Path_Game, file, sizeof(file), "cfg/%s", local);
   402 
   403 	bool file_exists = g_LibSys.IsPathFile(file);
   404 	if (!file_exists && will_create)
   405 	{
   406 		List<const ConVar *> *convars = NULL;
   407 		if (pl->GetProperty("ConVarList", (void **)&convars, false) && convars)
   408 		{
   409 			/* Attempt to create it */
   410 			FILE *fp = fopen(file, "wt");
   411 			if (fp)
   412 			{
   413 				fprintf(fp, "// This file was auto-generated by SourceMod (v%s)\n", SOURCEMOD_VERSION);
   414 				fprintf(fp, "// ConVars for plugin \"%s\"\n", pl->GetFilename());
   415 				fprintf(fp, "\n\n");
   416 
   417 				List<const ConVar *>::iterator iter;
   418 				float x;
   419 				for (iter = convars->begin(); iter != convars->end(); iter++)
   420 				{
   421 					const ConVar *cvar = (*iter);
   422 
   423 					if ((cvar->GetFlags() & FCVAR_DONTRECORD) == FCVAR_DONTRECORD)
   424 					{
   425 						continue;
   426 					}
   427 
   428 					char descr[255];
   429 					char *dptr = descr;
   430 
   431 					/* Print comments until there is no more */
   432 					strncopy(descr, cvar->GetHelpText(), sizeof(descr));
   433 					while (*dptr != '\0')
   434 					{
   435 						/* Find the next line */
   436 						char *next_ptr = dptr;
   437 						while (*next_ptr != '\0')
   438 						{
   439 							if (*next_ptr == '\n')
   440 							{
   441 								*next_ptr = '\0';
   442 								next_ptr++;
   443 								break;
   444 							}
   445 							next_ptr++;
   446 						}
   447 						fprintf(fp, "// %s\n", dptr);
   448 						dptr = next_ptr;
   449 					}
   450 
   451 					fprintf(fp, "// -\n");
   452 					fprintf(fp, "// Default: \"%s\"\n", cvar->GetDefault());
   453 					if (cvar->GetMin(x))
   454 					{
   455 						fprintf(fp, "// Minimum: \"%02f\"\n", x);
   456 					}
   457 					if (cvar->GetMax(x))
   458 					{
   459 						fprintf(fp, "// Maximum: \"%02f\"\n", x);
   460 					}
   461 					fprintf(fp, "%s \"%s\"\n", cvar->GetName(), cvar->GetDefault());
   462 					fprintf(fp, "\n");
   463 				}
   464 				fprintf(fp, "\n");
   465 
   466 				file_exists = true;
   467 				can_create = false;
   468 				fclose(fp);
   469 			}
   470 			else
   471 			{
   472 				g_Logger.LogError("Failed to auto generate config for %s, make sure the directory has write permission.", pl->GetFilename());
   473 				return can_create;
   474 			}
   475 		}
   476 	}
   477 
   478 	if (file_exists)
   479 	{
   480 		char cmd[255];
   481 		UTIL_Format(cmd, sizeof(cmd), "exec %s\n", local);
   482 		engine->ServerCommand(cmd);
   483 	}
   484 
   485 	return can_create;
   486 }
   487 
   488 void SM_DoSingleExecFwds(IPluginContext *ctx)
   489 {
   490 	IPluginFunction *pf;
   491 
   492 	if ((pf = ctx->GetFunctionByName("OnServerCfg")) != NULL)
   493 	{
   494 		pf->Execute(NULL);
   495 	}
   496 
   497 	if ((pf = ctx->GetFunctionByName("OnConfigsExecuted")) != NULL)
   498 	{
   499 		pf->Execute(NULL);
   500 	}
   501 }
   502 
   503 void SM_ConfigsExecuted_Plugin(unsigned int serial)
   504 {
   505 	IPluginIterator *iter = scripts->GetPluginIterator();
   506 	while (iter->MorePlugins())
   507 	{
   508 		IPlugin *plugin = iter->GetPlugin();
   509 		if (plugin->GetSerial() == serial)
   510 		{
   511 			SM_DoSingleExecFwds(plugin->GetBaseContext());
   512 			break;
   513 		}
   514 		iter->NextPlugin();
   515 	}
   516 	iter->Release();
   517 }
   518 
   519 void SM_ExecuteForPlugin(IPluginContext *ctx)
   520 {
   521 	SMPlugin *plugin = scripts->FindPluginByContext(ctx->GetContext());
   522 
   523 	unsigned int num = plugin->GetConfigCount();
   524 	if (!num)
   525 	{
   526 		SM_DoSingleExecFwds(ctx);
   527 	}
   528 	else
   529 	{
   530 		bool can_create = true;
   531 		for (unsigned int i=0; i<num; i++)
   532 		{
   533 			can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
   534 		}
   535 		char cmd[255];
   536 		UTIL_Format(cmd, sizeof(cmd), "sm internal 2 %d\n", plugin->GetSerial());
   537 		engine->ServerCommand(cmd);
   538 	}
   539 }
   540 
   541 void SM_ExecuteAllConfigs()
   542 {
   543 	if (g_bGotServerStart)
   544 	{
   545 		return;
   546 	}
   547 
   548 	engine->ServerCommand("exec sourcemod/sourcemod.cfg\n");
   549 
   550 	AutoPluginList plugins(scripts);
   551 	for (size_t i = 0; i < plugins->size(); i++)
   552 	{
   553 		SMPlugin *plugin = plugins->at(i);
   554 		unsigned int num = plugin->GetConfigCount();
   555 		bool can_create = true;
   556 		for (unsigned int i=0; i<num; i++)
   557 		{
   558 			can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
   559 		}
   560 	}
   561 
   562 	g_bGotServerStart = true;
   563 	CheckAndFinalizeConfigs();
   564 }
   565 
   566 void SM_ConfigsExecuted_Global()
   567 {
   568 	if (g_bConfigsExecd)
   569 	{
   570 		return;
   571 	}
   572 
   573 	g_bConfigsExecd = true;
   574 
   575 	g_pOnServerCfg->Execute(NULL);
   576 	g_pOnConfigsExecuted->Execute(NULL);
   577 }
   578 
   579 void SM_InternalCmdTrigger()
   580 {
   581 	/* Order is important here.  We need to buffer things before we send the command out. */
   582 	g_pOnAutoConfigsBuffered->Execute(NULL);
   583 	engine->ServerCommand("sm internal 1\n");
   584     g_PendingInternalPush = false;
   585 }
   586