1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/CoreConfig.cpp Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,524 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include <ITextParsers.h>
1.36 +#include "CoreConfig.h"
1.37 +#include "sourcemod.h"
1.38 +#include "sourcemm_api.h"
1.39 +#include "sm_srvcmds.h"
1.40 +#include "sm_version.h"
1.41 +#include "sm_stringutil.h"
1.42 +#include "LibrarySys.h"
1.43 +#include "Logger.h"
1.44 +#include "PluginSys.h"
1.45 +#include "ForwardSys.h"
1.46 +
1.47 +#ifdef PLATFORM_WINDOWS
1.48 +ConVar sm_corecfgfile("sm_corecfgfile", "addons\\sourcemod\\configs\\core.cfg", 0, "SourceMod core configuration file");
1.49 +#elif defined PLATFORM_LINUX
1.50 +ConVar sm_corecfgfile("sm_corecfgfile", "addons/sourcemod/configs/core.cfg", 0, "SourceMod core configuration file");
1.51 +#endif
1.52 +
1.53 +IForward *g_pOnServerCfg = NULL;
1.54 +IForward *g_pOnConfigsExecuted = NULL;
1.55 +IForward *g_pOnAutoConfigsBuffered = NULL;
1.56 +CoreConfig g_CoreConfig;
1.57 +bool g_bConfigsExecd = false;
1.58 +bool g_bServerExecd = false;
1.59 +bool g_bGotServerStart = false;
1.60 +bool g_bGotTrigger = false;
1.61 +ConCommand *g_pExecPtr = NULL;
1.62 +ConVar *g_ServerCfgFile = NULL;
1.63 +
1.64 +void CheckAndFinalizeConfigs();
1.65 +
1.66 +#if defined ORANGEBOX_BUILD
1.67 +SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
1.68 +void Hook_ExecDispatchPre(const CCommand &cmd)
1.69 +#else
1.70 +extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
1.71 +extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
1.72 +void Hook_ExecDispatchPre()
1.73 +#endif
1.74 +{
1.75 +#if !defined ORANGEBOX_BUILD
1.76 + CCommand cmd;
1.77 +#endif
1.78 +
1.79 + const char *arg = cmd.Arg(1);
1.80 +
1.81 + if (!g_bServerExecd
1.82 + && arg != NULL
1.83 + && strcmp(arg, g_ServerCfgFile->GetString()) == 0)
1.84 + {
1.85 + g_bGotTrigger = true;
1.86 + }
1.87 +}
1.88 +
1.89 +#if defined ORANGEBOX_BUILD
1.90 +void Hook_ExecDispatchPost(const CCommand &cmd)
1.91 +#else
1.92 +void Hook_ExecDispatchPost()
1.93 +#endif
1.94 +{
1.95 + if (g_bGotTrigger)
1.96 + {
1.97 + g_bGotTrigger = false;
1.98 + g_bServerExecd = true;
1.99 + CheckAndFinalizeConfigs();
1.100 + }
1.101 +}
1.102 +
1.103 +void CheckAndFinalizeConfigs()
1.104 +{
1.105 + if ((g_bServerExecd || g_ServerCfgFile == NULL)
1.106 + && g_bGotServerStart)
1.107 + {
1.108 + /* Order is important here. We need to buffer things before we send the command out. */
1.109 + g_pOnAutoConfigsBuffered->Execute(NULL);
1.110 + engine->ServerCommand("sm internal 1\n");
1.111 + }
1.112 +}
1.113 +
1.114 +void CoreConfig::OnSourceModAllInitialized()
1.115 +{
1.116 + g_RootMenu.AddRootConsoleCommand("config", "Set core configuration options", this);
1.117 + g_pOnServerCfg = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
1.118 + g_pOnConfigsExecuted = g_Forwards.CreateForward("OnConfigsExecuted", ET_Ignore, 0, NULL);
1.119 + g_pOnAutoConfigsBuffered = g_Forwards.CreateForward("OnAutoConfigsBuffered", ET_Ignore, 0, NULL);
1.120 +}
1.121 +
1.122 +void CoreConfig::OnSourceModShutdown()
1.123 +{
1.124 + g_RootMenu.RemoveRootConsoleCommand("config", this);
1.125 + g_Forwards.ReleaseForward(g_pOnServerCfg);
1.126 + g_Forwards.ReleaseForward(g_pOnConfigsExecuted);
1.127 + g_Forwards.ReleaseForward(g_pOnAutoConfigsBuffered);
1.128 +
1.129 + if (g_pExecPtr != NULL)
1.130 + {
1.131 + SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
1.132 + SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
1.133 + g_pExecPtr = NULL;
1.134 + }
1.135 +}
1.136 +
1.137 +void CoreConfig::OnSourceModLevelChange(const char *mapName)
1.138 +{
1.139 + static bool already_checked = false;
1.140 +
1.141 + if (!already_checked)
1.142 + {
1.143 + g_ServerCfgFile = icvar->FindVar("servercfgfile");
1.144 + if (g_ServerCfgFile != NULL)
1.145 + {
1.146 + ConCommandBase *pBase = icvar->GetCommands();
1.147 + while (pBase != NULL)
1.148 + {
1.149 + if (pBase->IsCommand() && strcmp(pBase->GetName(), "exec") == 0)
1.150 + {
1.151 + break;
1.152 + }
1.153 + pBase = const_cast<ConCommandBase *>(pBase->GetNext());
1.154 + }
1.155 +
1.156 + g_pExecPtr = (ConCommand *)pBase;
1.157 + if (g_pExecPtr != NULL)
1.158 + {
1.159 + SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
1.160 + SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
1.161 + }
1.162 + else
1.163 + {
1.164 + g_ServerCfgFile = NULL;
1.165 + }
1.166 + }
1.167 + already_checked = true;
1.168 + }
1.169 +
1.170 + g_bConfigsExecd = false;
1.171 + g_bServerExecd = false;
1.172 + g_bGotServerStart = false;
1.173 + g_bGotTrigger = false;
1.174 +}
1.175 +
1.176 +void CoreConfig::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
1.177 +{
1.178 + int argcount = command.ArgC();
1.179 + if (argcount >= 4)
1.180 + {
1.181 + const char *option = command.Arg(2);
1.182 + const char *value = command.Arg(3);
1.183 +
1.184 + char error[255];
1.185 +
1.186 + ConfigResult res = SetConfigOption(option, value, ConfigSource_Console, error, sizeof(error));
1.187 +
1.188 + if (res == ConfigResult_Reject)
1.189 + {
1.190 + g_RootMenu.ConsolePrint("[SM] Could not set config option \"%s\" to \"%s\" (%s)", option, value, error);
1.191 + } else if (res == ConfigResult_Ignore) {
1.192 + g_RootMenu.ConsolePrint("[SM] No such config option \"%s\" exists.", option);
1.193 + } else {
1.194 + g_RootMenu.ConsolePrint("Config option \"%s\" successfully set to \"%s.\"", option, value);
1.195 + }
1.196 +
1.197 + return;
1.198 + }
1.199 +
1.200 + g_RootMenu.ConsolePrint("[SM] Usage: sm config <option> <value>");
1.201 +}
1.202 +
1.203 +void CoreConfig::Initialize()
1.204 +{
1.205 + SMCError err;
1.206 + char filePath[PLATFORM_MAX_PATH];
1.207 +
1.208 + /* Try to get command line value of core config convar */
1.209 + const char *corecfg = icvar->GetCommandLineValue("sm_corecfgfile");
1.210 +
1.211 + /* If sm_corecfgfile not specified on command line, use default value */
1.212 + if (!corecfg)
1.213 + {
1.214 + corecfg = sm_corecfgfile.GetDefault();
1.215 + }
1.216 +
1.217 + /* Format path to config file */
1.218 + g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
1.219 +
1.220 + /* Parse config file */
1.221 + if ((err=textparsers->ParseFile_SMC(filePath, this, NULL)) != SMCError_Okay)
1.222 + {
1.223 + /* :TODO: This won't actually log or print anything :( - So fix that somehow */
1.224 + const char *error = textparsers->GetSMCErrorString(err);
1.225 + g_Logger.LogFatal("[SM] Error encountered parsing core config file: %s", error ? error : "");
1.226 + }
1.227 +}
1.228 +
1.229 +SMCResult CoreConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
1.230 +{
1.231 + char error[255];
1.232 + ConfigResult err = SetConfigOption(key, value, ConfigSource_File, error, sizeof(error));
1.233 +
1.234 + if (err == ConfigResult_Reject)
1.235 + {
1.236 + /* This is a fatal error */
1.237 + g_Logger.LogFatal("Config error (key: %s) (value: %s) %s", key, value, error);
1.238 + }
1.239 +
1.240 + return SMCResult_Continue;
1.241 +}
1.242 +
1.243 +ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength)
1.244 +{
1.245 + ConfigResult result;
1.246 +
1.247 + /* Notify! */
1.248 + SMGlobalClass *pBase = SMGlobalClass::head;
1.249 + while (pBase)
1.250 + {
1.251 + if ((result = pBase->OnSourceModConfigChanged(option, value, source, error, maxlength)) != ConfigResult_Ignore)
1.252 + {
1.253 + return result;
1.254 + }
1.255 + pBase = pBase->m_pGlobalClassNext;
1.256 + }
1.257 +
1.258 + return ConfigResult_Ignore;
1.259 +}
1.260 +
1.261 +bool SM_AreConfigsExecuted()
1.262 +{
1.263 + return g_bConfigsExecd;
1.264 +}
1.265 +
1.266 +inline bool IsPathSepChar(char c)
1.267 +{
1.268 +#if defined PLATFORM_WINDOWS
1.269 + return (c == '\\' || c == '/');
1.270 +#elif defined PLATFORM_LINUX
1.271 + return (c == '/');
1.272 +#endif
1.273 +}
1.274 +
1.275 +bool SM_ExecuteConfig(CPlugin *pl, AutoConfig *cfg, bool can_create)
1.276 +{
1.277 + bool will_create = false;
1.278 +
1.279 + /* See if we should be creating */
1.280 + if (can_create && cfg->create)
1.281 + {
1.282 + will_create = true;
1.283 +
1.284 + /* If the folder does not exist, attempt to create it.
1.285 + * We're awfully nice.
1.286 + */
1.287 + const char *folder = cfg->folder.c_str();
1.288 + char path[PLATFORM_MAX_PATH];
1.289 + char build[PLATFORM_MAX_PATH];
1.290 +
1.291 + g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "cfg/%s", folder);
1.292 +
1.293 + if (!g_LibSys.IsPathDirectory(path))
1.294 + {
1.295 + char *cur_ptr = path;
1.296 + size_t len;
1.297 +
1.298 + g_LibSys.PathFormat(path, sizeof(path), "%s", folder);
1.299 + len = g_SourceMod.BuildPath(Path_Game, build, sizeof(build), "cfg");
1.300 +
1.301 + do
1.302 + {
1.303 + /* Find next suitable path */
1.304 + char *next_ptr = cur_ptr;
1.305 + while (*next_ptr != '\0')
1.306 + {
1.307 + if (IsPathSepChar(*next_ptr))
1.308 + {
1.309 + *next_ptr = '\0';
1.310 + next_ptr++;
1.311 + break;
1.312 + }
1.313 + next_ptr++;
1.314 + }
1.315 + if (*next_ptr == '\0')
1.316 + {
1.317 + next_ptr = NULL;
1.318 + }
1.319 + len += g_LibSys.PathFormat(&build[len],
1.320 + sizeof(build)-len,
1.321 + "/%s",
1.322 + cur_ptr);
1.323 + if (!g_LibSys.CreateFolder(build))
1.324 + {
1.325 + break;
1.326 + }
1.327 + cur_ptr = next_ptr;
1.328 + } while (cur_ptr);
1.329 + }
1.330 + }
1.331 +
1.332 + /* Check if the file exists. */
1.333 + char file[PLATFORM_MAX_PATH];
1.334 + char local[PLATFORM_MAX_PATH];
1.335 +
1.336 + if (cfg->folder.size())
1.337 + {
1.338 + g_LibSys.PathFormat(local,
1.339 + sizeof(local),
1.340 + "%s/%s.cfg",
1.341 + cfg->folder.c_str(),
1.342 + cfg->autocfg.c_str());
1.343 + } else {
1.344 + g_LibSys.PathFormat(local,
1.345 + sizeof(local),
1.346 + "%s.cfg",
1.347 + cfg->autocfg.c_str());
1.348 + }
1.349 +
1.350 + g_SourceMod.BuildPath(Path_Game, file, sizeof(file), "cfg/%s", local);
1.351 +
1.352 + bool file_exists = g_LibSys.IsPathFile(file);
1.353 + if (!file_exists && will_create)
1.354 + {
1.355 + List<const ConVar *> *convars = NULL;
1.356 + if (pl->GetProperty("ConVarList", (void **)&convars, false) && convars)
1.357 + {
1.358 + /* Attempt to create it */
1.359 + FILE *fp = fopen(file, "wt");
1.360 + if (fp)
1.361 + {
1.362 + fprintf(fp, "// This file was auto-generated by SourceMod (v%s)\n", SVN_FULL_VERSION);
1.363 + fprintf(fp, "// ConVars for plugin \"%s\"\n", pl->GetFilename());
1.364 + fprintf(fp, "\n\n");
1.365 +
1.366 + List<const ConVar *>::iterator iter;
1.367 + float x;
1.368 + for (iter = convars->begin(); iter != convars->end(); iter++)
1.369 + {
1.370 + const ConVar *cvar = (*iter);
1.371 +
1.372 + if ((cvar->GetFlags() & FCVAR_DONTRECORD) == FCVAR_DONTRECORD)
1.373 + {
1.374 + continue;
1.375 + }
1.376 +
1.377 + char descr[255];
1.378 + char *dptr = descr;
1.379 +
1.380 + /* Print comments until there is no more */
1.381 + strncopy(descr, cvar->GetHelpText(), sizeof(descr));
1.382 + while (*dptr != '\0')
1.383 + {
1.384 + /* Find the next line */
1.385 + char *next_ptr = dptr;
1.386 + while (*next_ptr != '\0')
1.387 + {
1.388 + if (*next_ptr == '\n')
1.389 + {
1.390 + *next_ptr = '\0';
1.391 + next_ptr++;
1.392 + break;
1.393 + }
1.394 + next_ptr++;
1.395 + }
1.396 + fprintf(fp, "// %s\n", dptr);
1.397 + dptr = next_ptr;
1.398 + }
1.399 +
1.400 + fprintf(fp, "// -\n");
1.401 + fprintf(fp, "// Default: \"%s\"\n", cvar->GetDefault());
1.402 + if (cvar->GetMin(x))
1.403 + {
1.404 + fprintf(fp, "// Minimum: \"%02f\"\n", x);
1.405 + }
1.406 + if (cvar->GetMax(x))
1.407 + {
1.408 + fprintf(fp, "// Maximum: \"%02f\"\n", x);
1.409 + }
1.410 + fprintf(fp, "%s \"%s\"\n", cvar->GetName(), cvar->GetDefault());
1.411 + fprintf(fp, "\n");
1.412 + }
1.413 + fprintf(fp, "\n");
1.414 +
1.415 + file_exists = true;
1.416 + can_create = false;
1.417 + fclose(fp);
1.418 + }
1.419 + }
1.420 + }
1.421 +
1.422 + if (file_exists)
1.423 + {
1.424 + char cmd[255];
1.425 + UTIL_Format(cmd, sizeof(cmd), "exec %s\n", local);
1.426 + engine->ServerCommand(cmd);
1.427 + }
1.428 +
1.429 + return can_create;
1.430 +}
1.431 +
1.432 +void SM_DoSingleExecFwds(IPluginContext *ctx)
1.433 +{
1.434 + IPluginFunction *pf;
1.435 +
1.436 + if ((pf = ctx->GetFunctionByName("OnServerCfg")) != NULL)
1.437 + {
1.438 + pf->Execute(NULL);
1.439 + }
1.440 +
1.441 + if ((pf = ctx->GetFunctionByName("OnConfigsExecuted")) != NULL)
1.442 + {
1.443 + pf->Execute(NULL);
1.444 + }
1.445 +}
1.446 +
1.447 +void SM_ConfigsExecuted_Plugin(unsigned int serial)
1.448 +{
1.449 + IPluginIterator *iter = g_PluginSys.GetPluginIterator();
1.450 + while (iter->MorePlugins())
1.451 + {
1.452 + CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
1.453 + if (plugin->GetSerial() == serial)
1.454 + {
1.455 + SM_DoSingleExecFwds(plugin->GetBaseContext());
1.456 + break;
1.457 + }
1.458 + iter->NextPlugin();
1.459 + }
1.460 + iter->Release();
1.461 +}
1.462 +
1.463 +void SM_ExecuteForPlugin(IPluginContext *ctx)
1.464 +{
1.465 + CPlugin *plugin = (CPlugin *)g_PluginSys.GetPluginByCtx(ctx->GetContext());
1.466 +
1.467 + unsigned int num = plugin->GetConfigCount();
1.468 + if (!num)
1.469 + {
1.470 + SM_DoSingleExecFwds(ctx);
1.471 + }
1.472 + else
1.473 + {
1.474 + bool can_create = true;
1.475 + for (unsigned int i=0; i<num; i++)
1.476 + {
1.477 + can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
1.478 + }
1.479 + char cmd[255];
1.480 + UTIL_Format(cmd, sizeof(cmd), "sm internal 2 %d\n", plugin->GetSerial());
1.481 + engine->ServerCommand(cmd);
1.482 + }
1.483 +}
1.484 +
1.485 +void SM_ExecuteAllConfigs()
1.486 +{
1.487 + if (g_bGotServerStart)
1.488 + {
1.489 + return;
1.490 + }
1.491 +
1.492 + engine->ServerCommand("exec sourcemod/sourcemod.cfg\n");
1.493 +
1.494 + IPluginIterator *iter = g_PluginSys.GetPluginIterator();
1.495 + while (iter->MorePlugins())
1.496 + {
1.497 + CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
1.498 + unsigned int num = plugin->GetConfigCount();
1.499 + bool can_create = true;
1.500 + for (unsigned int i=0; i<num; i++)
1.501 + {
1.502 + can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
1.503 + }
1.504 + iter->NextPlugin();
1.505 + }
1.506 + iter->Release();
1.507 +
1.508 +#if defined ORANGEBOX_BUILD
1.509 + engine->ServerExecute();
1.510 +#endif
1.511 +
1.512 + g_bGotServerStart = true;
1.513 + CheckAndFinalizeConfigs();
1.514 +}
1.515 +
1.516 +void SM_ConfigsExecuted_Global()
1.517 +{
1.518 + if (g_bConfigsExecd)
1.519 + {
1.520 + return;
1.521 + }
1.522 +
1.523 + g_bConfigsExecd = true;
1.524 +
1.525 + g_pOnServerCfg->Execute(NULL);
1.526 + g_pOnConfigsExecuted->Execute(NULL);
1.527 +}