core/CoreConfig.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jul 06 04:49:55 2008 +0000 (2008-07-06)
branchrefac-jit
changeset 2168 c24a6fa957db
permissions -rw-r--r--
jit refactoring branch
     1 /**
     2  * vim: set ts=4 :
     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_version.h"
    38 #include "sm_stringutil.h"
    39 #include "LibrarySys.h"
    40 #include "Logger.h"
    41 #include "PluginSys.h"
    42 #include "ForwardSys.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
    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 defined ORANGEBOX_BUILD
    64 SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
    65 void Hook_ExecDispatchPre(const CCommand &cmd)
    66 #else
    67 extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
    68 extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
    69 void Hook_ExecDispatchPre()
    70 #endif
    71 {
    72 #if !defined ORANGEBOX_BUILD
    73 	CCommand cmd;
    74 #endif
    75 
    76 	const char *arg = cmd.Arg(1);
    77 
    78 	if (!g_bServerExecd 
    79 		&& arg != NULL 
    80 		&& strcmp(arg, g_ServerCfgFile->GetString()) == 0)
    81 	{
    82 		g_bGotTrigger = true;
    83 	}
    84 }
    85 
    86 #if defined ORANGEBOX_BUILD
    87 void Hook_ExecDispatchPost(const CCommand &cmd)
    88 #else
    89 void Hook_ExecDispatchPost()
    90 #endif
    91 {
    92 	if (g_bGotTrigger)
    93 	{
    94 		g_bGotTrigger = false;
    95 		g_bServerExecd = true;
    96 		CheckAndFinalizeConfigs();
    97 	}
    98 }
    99 
   100 void CheckAndFinalizeConfigs()
   101 {
   102 	if ((g_bServerExecd || g_ServerCfgFile == NULL) 
   103 		&& g_bGotServerStart)
   104 	{
   105 		/* Order is important here.  We need to buffer things before we send the command out. */
   106 		g_pOnAutoConfigsBuffered->Execute(NULL);
   107 		engine->ServerCommand("sm internal 1\n");
   108 	}
   109 }
   110 
   111 void CoreConfig::OnSourceModAllInitialized()
   112 {
   113 	g_RootMenu.AddRootConsoleCommand("config", "Set core configuration options", this);
   114 	g_pOnServerCfg = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
   115 	g_pOnConfigsExecuted = g_Forwards.CreateForward("OnConfigsExecuted", ET_Ignore, 0, NULL);
   116 	g_pOnAutoConfigsBuffered = g_Forwards.CreateForward("OnAutoConfigsBuffered", ET_Ignore, 0, NULL);
   117 }
   118 
   119 void CoreConfig::OnSourceModShutdown()
   120 {
   121 	g_RootMenu.RemoveRootConsoleCommand("config", this);
   122 	g_Forwards.ReleaseForward(g_pOnServerCfg);
   123 	g_Forwards.ReleaseForward(g_pOnConfigsExecuted);
   124 	g_Forwards.ReleaseForward(g_pOnAutoConfigsBuffered);
   125 
   126 	if (g_pExecPtr != NULL)
   127 	{
   128 		SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
   129 		SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
   130 		g_pExecPtr = NULL;
   131 	}
   132 }
   133 
   134 void CoreConfig::OnSourceModLevelChange(const char *mapName)
   135 {
   136 	static bool already_checked = false;
   137 
   138 	if (!already_checked)
   139 	{
   140 		g_ServerCfgFile = icvar->FindVar("servercfgfile");
   141 		if (g_ServerCfgFile != NULL)
   142 		{
   143 			ConCommandBase *pBase = icvar->GetCommands();
   144 			while (pBase != NULL)
   145 			{
   146 				if (pBase->IsCommand() && strcmp(pBase->GetName(), "exec") == 0)
   147 				{
   148 					break;
   149 				}
   150 				pBase = const_cast<ConCommandBase *>(pBase->GetNext());
   151 			}
   152 
   153 			g_pExecPtr = (ConCommand *)pBase;
   154 			if (g_pExecPtr != NULL)
   155 			{
   156 				SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
   157 				SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
   158 			}
   159 			else
   160 			{
   161 				g_ServerCfgFile = NULL;
   162 			}
   163 		}
   164 		already_checked = true;
   165 	}
   166 
   167 	g_bConfigsExecd = false;
   168 	g_bServerExecd = false;
   169 	g_bGotServerStart = false;
   170 	g_bGotTrigger = false;
   171 }
   172 
   173 void CoreConfig::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
   174 {
   175 	int argcount = command.ArgC();
   176 	if (argcount >= 4)
   177 	{
   178 		const char *option = command.Arg(2);
   179 		const char *value = command.Arg(3);
   180 
   181 		char error[255];
   182 
   183 		ConfigResult res = SetConfigOption(option, value, ConfigSource_Console, error, sizeof(error));
   184 
   185 		if (res == ConfigResult_Reject)
   186 		{
   187 			g_RootMenu.ConsolePrint("[SM] Could not set config option \"%s\" to \"%s\" (%s)", option, value, error);
   188 		} else if (res == ConfigResult_Ignore) {
   189 			g_RootMenu.ConsolePrint("[SM] No such config option \"%s\" exists.", option);
   190 		} else {
   191 			g_RootMenu.ConsolePrint("Config option \"%s\" successfully set to \"%s.\"", option, value);
   192 		}
   193 
   194 		return;
   195 	}
   196 
   197 	g_RootMenu.ConsolePrint("[SM] Usage: sm config <option> <value>");
   198 }
   199 
   200 void CoreConfig::Initialize()
   201 {
   202 	SMCError err;
   203 	char filePath[PLATFORM_MAX_PATH];
   204 
   205 	/* Try to get command line value of core config convar */
   206 	const char *corecfg = icvar->GetCommandLineValue("sm_corecfgfile");
   207 
   208 	/* If sm_corecfgfile not specified on command line, use default value */
   209 	if (!corecfg)
   210 	{
   211 		corecfg = sm_corecfgfile.GetDefault();
   212 	}
   213 
   214 	/* Format path to config file */
   215 	g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
   216 
   217 	/* Parse config file */
   218 	if ((err=textparsers->ParseFile_SMC(filePath, this, NULL)) != SMCError_Okay)
   219 	{
   220  		/* :TODO: This won't actually log or print anything :( - So fix that somehow */
   221 		const char *error = textparsers->GetSMCErrorString(err);
   222 		g_Logger.LogFatal("[SM] Error encountered parsing core config file: %s", error ? error : "");
   223 	}
   224 }
   225 
   226 SMCResult CoreConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   227 {
   228 	char error[255];
   229 	ConfigResult err = SetConfigOption(key, value, ConfigSource_File, error, sizeof(error));
   230 
   231 	if (err == ConfigResult_Reject)
   232 	{
   233 		/* This is a fatal error */
   234 		g_Logger.LogFatal("Config error (key: %s) (value: %s) %s", key, value, error);
   235 	}
   236 
   237 	return SMCResult_Continue;
   238 }
   239 
   240 ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength)
   241 {
   242 	ConfigResult result;
   243 
   244 	/* Notify! */
   245 	SMGlobalClass *pBase = SMGlobalClass::head;
   246 	while (pBase)
   247 	{
   248 		if ((result = pBase->OnSourceModConfigChanged(option, value, source, error, maxlength)) != ConfigResult_Ignore)
   249 		{
   250 			return result;
   251 		}
   252 		pBase = pBase->m_pGlobalClassNext;
   253 	}
   254 
   255 	return ConfigResult_Ignore;
   256 }
   257 
   258 bool SM_AreConfigsExecuted()
   259 {
   260 	return g_bConfigsExecd;
   261 }
   262 
   263 inline bool IsPathSepChar(char c)
   264 {
   265 #if defined PLATFORM_WINDOWS
   266 	return (c == '\\' || c == '/');
   267 #elif defined PLATFORM_LINUX
   268 	return (c == '/');
   269 #endif
   270 }
   271 
   272 bool SM_ExecuteConfig(CPlugin *pl, AutoConfig *cfg, bool can_create)
   273 {
   274 	bool will_create = false;
   275 	
   276 	/* See if we should be creating */
   277 	if (can_create && cfg->create)
   278 	{
   279 		will_create = true;
   280 		
   281 		/* If the folder does not exist, attempt to create it.  
   282 		 * We're awfully nice.
   283 		 */
   284 		const char *folder = cfg->folder.c_str();
   285 		char path[PLATFORM_MAX_PATH];
   286 		char build[PLATFORM_MAX_PATH];
   287 
   288 		g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "cfg/%s", folder);
   289 
   290 		if (!g_LibSys.IsPathDirectory(path))
   291 		{
   292 			char *cur_ptr = path;
   293 			size_t len;
   294 			
   295 			g_LibSys.PathFormat(path, sizeof(path), "%s", folder);
   296 			len = g_SourceMod.BuildPath(Path_Game, build, sizeof(build), "cfg");
   297 
   298 			do
   299 			{
   300 				/* Find next suitable path */
   301 				char *next_ptr = cur_ptr;
   302 				while (*next_ptr != '\0')
   303 				{
   304 					if (IsPathSepChar(*next_ptr))
   305 					{
   306 						*next_ptr = '\0';
   307 						next_ptr++;
   308 						break;
   309 					}
   310 					next_ptr++;
   311 				}
   312 				if (*next_ptr == '\0')
   313 				{
   314 					next_ptr = NULL;
   315 				}
   316 				len += g_LibSys.PathFormat(&build[len], 
   317 					sizeof(build)-len,
   318 					"/%s",
   319 					cur_ptr);
   320 				if (!g_LibSys.CreateFolder(build))
   321 				{
   322 					break;
   323 				}
   324 				cur_ptr = next_ptr;
   325 			} while (cur_ptr);
   326 		}
   327 	}
   328 
   329 	/* Check if the file exists. */
   330 	char file[PLATFORM_MAX_PATH];
   331 	char local[PLATFORM_MAX_PATH];
   332 
   333 	if (cfg->folder.size())
   334 	{
   335 		g_LibSys.PathFormat(local, 
   336 			sizeof(local), 
   337 			"%s/%s.cfg",
   338 			cfg->folder.c_str(),
   339 			cfg->autocfg.c_str());
   340 	} else {
   341 		g_LibSys.PathFormat(local,
   342 			sizeof(local),
   343 			"%s.cfg",
   344 			cfg->autocfg.c_str());
   345 	}
   346 	
   347 	g_SourceMod.BuildPath(Path_Game, file, sizeof(file), "cfg/%s", local);
   348 
   349 	bool file_exists = g_LibSys.IsPathFile(file);
   350 	if (!file_exists && will_create)
   351 	{
   352 		List<const ConVar *> *convars = NULL;
   353 		if (pl->GetProperty("ConVarList", (void **)&convars, false) && convars)
   354 		{
   355 			/* Attempt to create it */
   356 			FILE *fp = fopen(file, "wt");
   357 			if (fp)
   358 			{
   359 				fprintf(fp, "// This file was auto-generated by SourceMod (v%s)\n", SVN_FULL_VERSION);
   360 				fprintf(fp, "// ConVars for plugin \"%s\"\n", pl->GetFilename());
   361 				fprintf(fp, "\n\n");
   362 
   363 				List<const ConVar *>::iterator iter;
   364 				float x;
   365 				for (iter = convars->begin(); iter != convars->end(); iter++)
   366 				{
   367 					const ConVar *cvar = (*iter);
   368 
   369 					if ((cvar->GetFlags() & FCVAR_DONTRECORD) == FCVAR_DONTRECORD)
   370 					{
   371 						continue;
   372 					}
   373 
   374 					char descr[255];
   375 					char *dptr = descr;
   376 
   377 					/* Print comments until there is no more */
   378 					strncopy(descr, cvar->GetHelpText(), sizeof(descr));
   379 					while (*dptr != '\0')
   380 					{
   381 						/* Find the next line */
   382 						char *next_ptr = dptr;
   383 						while (*next_ptr != '\0')
   384 						{
   385 							if (*next_ptr == '\n')
   386 							{
   387 								*next_ptr = '\0';
   388 								next_ptr++;
   389 								break;
   390 							}
   391 							next_ptr++;
   392 						}
   393 						fprintf(fp, "// %s\n", dptr);
   394 						dptr = next_ptr;
   395 					}
   396 
   397 					fprintf(fp, "// -\n");
   398 					fprintf(fp, "// Default: \"%s\"\n", cvar->GetDefault());
   399 					if (cvar->GetMin(x))
   400 					{
   401 						fprintf(fp, "// Minimum: \"%02f\"\n", x);
   402 					}
   403 					if (cvar->GetMax(x))
   404 					{
   405 						fprintf(fp, "// Maximum: \"%02f\"\n", x);
   406 					}
   407 					fprintf(fp, "%s \"%s\"\n", cvar->GetName(), cvar->GetDefault());
   408 					fprintf(fp, "\n");
   409 				}
   410 				fprintf(fp, "\n");
   411 
   412 				file_exists = true;
   413 				can_create = false;
   414 				fclose(fp);
   415 			}
   416 		}
   417 	}
   418 
   419 	if (file_exists)
   420 	{
   421 		char cmd[255];
   422 		UTIL_Format(cmd, sizeof(cmd), "exec %s\n", local);
   423 		engine->ServerCommand(cmd);
   424 	}
   425 
   426 	return can_create;
   427 }
   428 
   429 void SM_DoSingleExecFwds(IPluginContext *ctx)
   430 {
   431 	IPluginFunction *pf;
   432 
   433 	if ((pf = ctx->GetFunctionByName("OnServerCfg")) != NULL)
   434 	{
   435 		pf->Execute(NULL);
   436 	}
   437 
   438 	if ((pf = ctx->GetFunctionByName("OnConfigsExecuted")) != NULL)
   439 	{
   440 		pf->Execute(NULL);
   441 	}
   442 }
   443 
   444 void SM_ConfigsExecuted_Plugin(unsigned int serial)
   445 {
   446 	IPluginIterator *iter = g_PluginSys.GetPluginIterator();
   447 	while (iter->MorePlugins())
   448 	{
   449 		CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
   450 		if (plugin->GetSerial() == serial)
   451 		{
   452 			SM_DoSingleExecFwds(plugin->GetBaseContext());
   453 			break;
   454 		}
   455 		iter->NextPlugin();
   456 	}
   457 	iter->Release();
   458 }
   459 
   460 void SM_ExecuteForPlugin(IPluginContext *ctx)
   461 {
   462 	CPlugin *plugin = (CPlugin *)g_PluginSys.GetPluginByCtx(ctx->GetContext());
   463 
   464 	unsigned int num = plugin->GetConfigCount();
   465 	if (!num)
   466 	{
   467 		SM_DoSingleExecFwds(ctx);
   468 	}
   469 	else
   470 	{
   471 		bool can_create = true;
   472 		for (unsigned int i=0; i<num; i++)
   473 		{
   474 			can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
   475 		}
   476 		char cmd[255];
   477 		UTIL_Format(cmd, sizeof(cmd), "sm internal 2 %d\n", plugin->GetSerial());
   478 		engine->ServerCommand(cmd);
   479 	}
   480 }
   481 
   482 void SM_ExecuteAllConfigs()
   483 {
   484 	if (g_bGotServerStart)
   485 	{
   486 		return;
   487 	}
   488 
   489 	engine->ServerCommand("exec sourcemod/sourcemod.cfg\n");
   490 
   491 	IPluginIterator *iter = g_PluginSys.GetPluginIterator();
   492 	while (iter->MorePlugins())
   493 	{
   494 		CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
   495 		unsigned int num = plugin->GetConfigCount();
   496 		bool can_create = true;
   497 		for (unsigned int i=0; i<num; i++)
   498 		{
   499 			can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
   500 		}
   501 		iter->NextPlugin();
   502 	}
   503 	iter->Release();
   504 
   505 #if defined ORANGEBOX_BUILD
   506 	engine->ServerExecute();
   507 #endif
   508 
   509 	g_bGotServerStart = true;
   510 	CheckAndFinalizeConfigs();
   511 }
   512 
   513 void SM_ConfigsExecuted_Global()
   514 {
   515 	if (g_bConfigsExecd)
   516 	{
   517 		return;
   518 	}
   519 
   520 	g_bConfigsExecd = true;
   521 
   522 	g_pOnServerCfg->Execute(NULL);
   523 	g_pOnConfigsExecuted->Execute(NULL);
   524 }