core/CoreConfig.cpp
author David Anderson <dvander@alliedmods.net>
Tue Sep 11 15:10:18 2007 +0000 (2007-09-11)
changeset 1406 f5c07ec3c052
parent 1330 ab94316bb52b
child 1541 e46ad208b9b4
permissions -rw-r--r--
header movement #1 - reduced usage of TextParsers.h
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 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 CoreConfig g_CoreConfig;
    53 bool g_bConfigsExecd = false;
    54 
    55 void CoreConfig::OnSourceModAllInitialized()
    56 {
    57 	g_RootMenu.AddRootConsoleCommand("config", "Set core configuration options", this);
    58 	g_pOnServerCfg = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
    59 	g_pOnConfigsExecuted = g_Forwards.CreateForward("OnConfigsExecuted", ET_Ignore, 0, NULL);
    60 }
    61 
    62 void CoreConfig::OnSourceModShutdown()
    63 {
    64 	g_RootMenu.RemoveRootConsoleCommand("config", this);
    65 	g_Forwards.ReleaseForward(g_pOnServerCfg);
    66 	g_Forwards.ReleaseForward(g_pOnConfigsExecuted);
    67 }
    68 
    69 void CoreConfig::OnSourceModLevelChange(const char *mapName)
    70 {
    71 	g_bConfigsExecd = false;
    72 }
    73 
    74 void CoreConfig::OnRootConsoleCommand(const char *command, unsigned int argcount)
    75 {
    76 	if (argcount >= 4)
    77 	{
    78 		const char *option = engine->Cmd_Argv(2);
    79 		const char *value = engine->Cmd_Argv(3);
    80 
    81 		char error[255];
    82 
    83 		ConfigResult res = SetConfigOption(option, value, ConfigSource_Console, error, sizeof(error));
    84 
    85 		if (res == ConfigResult_Reject)
    86 		{
    87 			g_RootMenu.ConsolePrint("[SM] Could not set config option \"%s\" to \"%s\" (%s)", option, value, error);
    88 		} else if (res == ConfigResult_Ignore) {
    89 			g_RootMenu.ConsolePrint("[SM] No such config option \"%s\" exists.", option);
    90 		} else {
    91 			g_RootMenu.ConsolePrint("Config option \"%s\" successfully set to \"%s.\"", option, value);
    92 		}
    93 
    94 		return;
    95 	}
    96 
    97 	g_RootMenu.ConsolePrint("[SM] Usage: sm config <option> <value>");
    98 }
    99 
   100 void CoreConfig::Initialize()
   101 {
   102 	SMCParseError err;
   103 	char filePath[PLATFORM_MAX_PATH];
   104 
   105 	/* Try to get command line value of core config convar */
   106 	const char *corecfg = icvar->GetCommandLineValue("sm_corecfgfile");
   107 
   108 	/* If sm_corecfgfile not specified on command line, use default value */
   109 	if (!corecfg)
   110 	{
   111 		corecfg = sm_corecfgfile.GetDefault();
   112 	}
   113 
   114 	/* Format path to config file */
   115 	g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
   116 
   117 	/* Parse config file */
   118 	if ((err=textparsers->ParseFile_SMC(filePath, this, NULL, NULL)) != SMCParse_Okay)
   119 	{
   120  		/* :TODO: This won't actually log or print anything :( - So fix that somehow */
   121 		const char *error = textparsers->GetSMCErrorString(err);
   122 		g_Logger.LogFatal("[SM] Error encountered parsing core config file: %s", error ? error : "");
   123 	}
   124 }
   125 
   126 SMCParseResult CoreConfig::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
   127 {
   128 	char error[255];
   129 	ConfigResult err = SetConfigOption(key, value, ConfigSource_File, error, sizeof(error));
   130 
   131 	if (err == ConfigResult_Reject)
   132 	{
   133 		/* This is a fatal error */
   134 		g_Logger.LogFatal("Config error (key: %s) (value: %s) %s", key, value, error);
   135 	}
   136 
   137 	return SMCParse_Continue;
   138 }
   139 
   140 ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength)
   141 {
   142 	ConfigResult result;
   143 
   144 	/* Notify! */
   145 	SMGlobalClass *pBase = SMGlobalClass::head;
   146 	while (pBase)
   147 	{
   148 		if ((result = pBase->OnSourceModConfigChanged(option, value, source, error, maxlength)) != ConfigResult_Ignore)
   149 		{
   150 			return result;
   151 		}
   152 		pBase = pBase->m_pGlobalClassNext;
   153 	}
   154 
   155 	return ConfigResult_Ignore;
   156 }
   157 
   158 bool SM_AreConfigsExecuted()
   159 {
   160 	return g_bConfigsExecd;
   161 }
   162 
   163 inline bool IsPathSepChar(char c)
   164 {
   165 #if defined PLATFORM_WINDOWS
   166 	return (c == '\\' || c == '/');
   167 #elif defined PLATFORM_LINUX
   168 	return (c == '/');
   169 #endif
   170 }
   171 
   172 bool SM_ExecuteConfig(CPlugin *pl, AutoConfig *cfg, bool can_create)
   173 {
   174 	bool will_create = false;
   175 	
   176 	/* See if we should be creating */
   177 	if (can_create && cfg->create)
   178 	{
   179 		will_create = true;
   180 		
   181 		/* If the folder does not exist, attempt to create it.  
   182 		 * We're awfully nice.
   183 		 */
   184 		const char *folder = cfg->folder.c_str();
   185 		char path[PLATFORM_MAX_PATH];
   186 		char build[PLATFORM_MAX_PATH];
   187 
   188 		g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "cfg/%s", folder);
   189 
   190 		if (!g_LibSys.IsPathDirectory(path))
   191 		{
   192 			char *cur_ptr = path;
   193 			size_t len;
   194 			
   195 			g_LibSys.PathFormat(path, sizeof(path), "%s", folder);
   196 			len = g_SourceMod.BuildPath(Path_Game, build, sizeof(build), "cfg");
   197 
   198 			do
   199 			{
   200 				/* Find next suitable path */
   201 				char *next_ptr = cur_ptr;
   202 				while (*next_ptr != '\0')
   203 				{
   204 					if (IsPathSepChar(*next_ptr))
   205 					{
   206 						*next_ptr = '\0';
   207 						next_ptr++;
   208 						break;
   209 					}
   210 					next_ptr++;
   211 				}
   212 				if (*next_ptr == '\0')
   213 				{
   214 					next_ptr = NULL;
   215 				}
   216 				len += g_LibSys.PathFormat(&build[len], 
   217 					sizeof(build)-len,
   218 					"/%s",
   219 					cur_ptr);
   220 				if (!g_LibSys.CreateFolder(build))
   221 				{
   222 					break;
   223 				}
   224 				cur_ptr = next_ptr;
   225 			} while (cur_ptr);
   226 		}
   227 	}
   228 
   229 	/* Check if the file exists. */
   230 	char file[PLATFORM_MAX_PATH];
   231 	char local[PLATFORM_MAX_PATH];
   232 
   233 	if (cfg->folder.size())
   234 	{
   235 		g_LibSys.PathFormat(local, 
   236 			sizeof(local), 
   237 			"%s/%s.cfg",
   238 			cfg->folder.c_str(),
   239 			cfg->autocfg.c_str());
   240 	} else {
   241 		g_LibSys.PathFormat(local,
   242 			sizeof(local),
   243 			"%s.cfg",
   244 			cfg->autocfg.c_str());
   245 	}
   246 	
   247 	g_SourceMod.BuildPath(Path_Game, file, sizeof(file), "cfg/%s", local);
   248 
   249 	bool file_exists = g_LibSys.IsPathFile(file);
   250 	if (!file_exists && will_create)
   251 	{
   252 		List<const ConVar *> *convars = NULL;
   253 		if (pl->GetProperty("ConVarList", (void **)&convars, false) && convars)
   254 		{
   255 			/* Attempt to create it */
   256 			FILE *fp = fopen(file, "wt");
   257 			if (fp)
   258 			{
   259 				fprintf(fp, "// This file was auto-generated by SourceMod (v%s)\n", SVN_FULL_VERSION);
   260 				fprintf(fp, "// ConVars for plugin \"%s\"\n", pl->GetFilename());
   261 				fprintf(fp, "\n\n");
   262 
   263 				List<const ConVar *>::iterator iter;
   264 				float x;
   265 				for (iter = convars->begin(); iter != convars->end(); iter++)
   266 				{
   267 					const ConVar *cvar = (*iter);
   268 
   269 					if ((cvar->GetFlags() & FCVAR_DONTRECORD) == FCVAR_DONTRECORD)
   270 					{
   271 						continue;
   272 					}
   273 
   274 					char descr[255];
   275 					char *dptr = descr;
   276 
   277 					/* Print comments until there is no more */
   278 					strncopy(descr, cvar->GetHelpText(), sizeof(descr));
   279 					while (*dptr != '\0')
   280 					{
   281 						/* Find the next line */
   282 						char *next_ptr = dptr;
   283 						while (*next_ptr != '\0')
   284 						{
   285 							if (*next_ptr == '\n')
   286 							{
   287 								*next_ptr = '\0';
   288 								next_ptr++;
   289 								break;
   290 							}
   291 							next_ptr++;
   292 						}
   293 						fprintf(fp, "// %s\n", dptr);
   294 						dptr = next_ptr;
   295 					}
   296 
   297 					fprintf(fp, "// -\n");
   298 					fprintf(fp, "// Default: \"%s\"\n", cvar->GetDefault());
   299 					if (cvar->GetMin(x))
   300 					{
   301 						fprintf(fp, "// Minimum: \"%02f\"\n", x);
   302 					}
   303 					if (cvar->GetMax(x))
   304 					{
   305 						fprintf(fp, "// Maximum: \"%02f\"\n", x);
   306 					}
   307 					fprintf(fp, "%s \"%s\"\n", cvar->GetName(), cvar->GetDefault());
   308 					fprintf(fp, "\n");
   309 				}
   310 				fprintf(fp, "\n");
   311 
   312 				file_exists = true;
   313 				can_create = false;
   314 				fclose(fp);
   315 			}
   316 		}
   317 	}
   318 
   319 	if (file_exists)
   320 	{
   321 		char cmd[255];
   322 		UTIL_Format(cmd, sizeof(cmd), "exec %s\n", local);
   323 		engine->ServerCommand(cmd);
   324 	}
   325 
   326 	return can_create;
   327 }
   328 
   329 void SM_DoSingleExecFwds(IPluginContext *ctx)
   330 {
   331 	IPluginFunction *pf;
   332 
   333 	if ((pf = ctx->GetFunctionByName("OnServerCfg")) != NULL)
   334 	{
   335 		pf->Execute(NULL);
   336 	}
   337 
   338 	if ((pf = ctx->GetFunctionByName("OnConfigsExecuted")) != NULL)
   339 	{
   340 		pf->Execute(NULL);
   341 	}
   342 }
   343 
   344 void SM_ConfigsExecuted_Plugin(unsigned int serial)
   345 {
   346 	IPluginIterator *iter = g_PluginSys.GetPluginIterator();
   347 	while (iter->MorePlugins())
   348 	{
   349 		CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
   350 		if (plugin->GetSerial() == serial)
   351 		{
   352 			SM_DoSingleExecFwds(plugin->GetBaseContext());
   353 			break;
   354 		}
   355 		iter->NextPlugin();
   356 	}
   357 	iter->Release();
   358 }
   359 
   360 void SM_ExecuteForPlugin(IPluginContext *ctx)
   361 {
   362 	CPlugin *plugin = (CPlugin *)g_PluginSys.GetPluginByCtx(ctx->GetContext());
   363 
   364 	unsigned int num = plugin->GetConfigCount();
   365 	if (!num)
   366 	{
   367 		SM_DoSingleExecFwds(ctx);
   368 	} else {
   369 		bool can_create = true;
   370 		for (unsigned int i=0; i<num; i++)
   371 		{
   372 			can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
   373 		}
   374 		char cmd[255];
   375 		UTIL_Format(cmd, sizeof(cmd), "sm internal 2 %d\n", plugin->GetSerial());
   376 		engine->ServerCommand(cmd);
   377 	}
   378 }
   379 
   380 void SM_ExecuteAllConfigs()
   381 {
   382 	if (g_bConfigsExecd)
   383 	{
   384 		return;
   385 	}
   386 
   387 	engine->ServerCommand("exec sourcemod/sourcemod.cfg\n");
   388 
   389 	IPluginIterator *iter = g_PluginSys.GetPluginIterator();
   390 	while (iter->MorePlugins())
   391 	{
   392 		CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
   393 		unsigned int num = plugin->GetConfigCount();
   394 		bool can_create = true;
   395 		for (unsigned int i=0; i<num; i++)
   396 		{
   397 			can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
   398 		}
   399 		iter->NextPlugin();
   400 	}
   401 	iter->Release();
   402 
   403 	engine->ServerCommand("sm internal 1\n");
   404 }
   405 
   406 void SM_ConfigsExecuted_Global()
   407 {
   408 	if (g_bConfigsExecd)
   409 	{
   410 		return;
   411 	}
   412 
   413 	g_bConfigsExecd = true;
   414 
   415 	g_pOnServerCfg->Execute(NULL);
   416 	g_pOnConfigsExecuted->Execute(NULL);
   417 }