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