1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/ConVarManager.cpp Sun Mar 30 07:00:22 2008 +0000
1.3 @@ -0,0 +1,707 @@
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 "ConVarManager.h"
1.36 +#include "HalfLife2.h"
1.37 +#include "PluginSys.h"
1.38 +#include "ForwardSys.h"
1.39 +#include "HandleSys.h"
1.40 +#include "sm_srvcmds.h"
1.41 +#include "sm_stringutil.h"
1.42 +#include <sh_vector.h>
1.43 +#include <sm_trie_tpl.h>
1.44 +
1.45 +ConVarManager g_ConVarManager;
1.46 +
1.47 +#if !defined ORANGEBOX_BUILD
1.48 +#define CallGlobalChangeCallbacks CallGlobalChangeCallback
1.49 +#endif
1.50 +
1.51 +#if defined ORANGEBOX_BUILD
1.52 +SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *, float);
1.53 +#else
1.54 +SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *);
1.55 +#endif
1.56 +
1.57 +SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
1.58 +SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
1.59 +
1.60 +const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
1.61 +typedef List<const ConVar *> ConVarList;
1.62 +KTrie<ConVarInfo *> convar_cache;
1.63 +
1.64 +ConVarManager::ConVarManager() : m_ConVarType(0), m_bIsDLLQueryHooked(false), m_bIsVSPQueryHooked(false)
1.65 +{
1.66 +}
1.67 +
1.68 +ConVarManager::~ConVarManager()
1.69 +{
1.70 +}
1.71 +
1.72 +void ConVarManager::OnSourceModStartup(bool late)
1.73 +{
1.74 + HandleAccess sec;
1.75 +
1.76 + /* Set up access rights for the 'ConVar' handle type */
1.77 + sec.access[HandleAccess_Read] = 0;
1.78 + sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.79 + sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.80 +
1.81 + /* Create the 'ConVar' handle type */
1.82 + m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
1.83 +}
1.84 +
1.85 +void ConVarManager::OnSourceModAllInitialized()
1.86 +{
1.87 + /**
1.88 + * Episode 2 has this function by default, but the older versions do not.
1.89 + */
1.90 +#if !defined ORANGEBOX_BUILD
1.91 + if (g_SMAPI->GetGameDLLVersion() >= 6)
1.92 + {
1.93 + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
1.94 + m_bIsDLLQueryHooked = true;
1.95 + }
1.96 +#endif
1.97 +
1.98 + SH_ADD_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
1.99 +
1.100 + /* Add the 'convars' option to the 'sm' console command */
1.101 + g_RootMenu.AddRootConsoleCommand("cvars", "View convars created by a plugin", this);
1.102 +}
1.103 +
1.104 +void ConVarManager::OnSourceModShutdown()
1.105 +{
1.106 + List<ConVarInfo *>::iterator iter = m_ConVars.begin();
1.107 + HandleSecurity sec(NULL, g_pCoreIdent);
1.108 +
1.109 + /* Iterate list of ConVarInfo structures, remove every one of them */
1.110 + while (iter != m_ConVars.end())
1.111 + {
1.112 + ConVarInfo *pInfo = (*iter);
1.113 +
1.114 + iter = m_ConVars.erase(iter);
1.115 +
1.116 + g_HandleSys.FreeHandle(pInfo->handle, &sec);
1.117 + if (pInfo->pChangeForward != NULL)
1.118 + {
1.119 + g_Forwards.ReleaseForward(pInfo->pChangeForward);
1.120 + }
1.121 + if (pInfo->sourceMod)
1.122 + {
1.123 + /* If we created it, we won't be tracking it, therefore it is
1.124 + * safe to remove everything in one go.
1.125 + */
1.126 + META_UNREGCVAR(pInfo->pVar);
1.127 + delete [] pInfo->pVar->GetName();
1.128 + delete [] pInfo->pVar->GetHelpText();
1.129 + delete [] pInfo->pVar->GetDefault();
1.130 + delete pInfo->pVar;
1.131 + }
1.132 + else
1.133 + {
1.134 + /* If we didn't create it, we might be tracking it. Also,
1.135 + * it could be unreadable.
1.136 + */
1.137 + UntrackConCommandBase(pInfo->pVar, this);
1.138 + }
1.139 +
1.140 + /* It's not safe to read the name here, so we simply delete the
1.141 + * the info struct and clear the lookup cache at the end.
1.142 + */
1.143 + delete pInfo;
1.144 + }
1.145 + convar_cache.clear();
1.146 +
1.147 + /* Unhook things */
1.148 + if (m_bIsDLLQueryHooked)
1.149 + {
1.150 + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
1.151 + m_bIsDLLQueryHooked = false;
1.152 + }
1.153 + else if (m_bIsVSPQueryHooked)
1.154 + {
1.155 + SH_REMOVE_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
1.156 + m_bIsVSPQueryHooked = false;
1.157 + }
1.158 +
1.159 + SH_REMOVE_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
1.160 +
1.161 + /* Remove the 'convars' option from the 'sm' console command */
1.162 + g_RootMenu.RemoveRootConsoleCommand("cvars", this);
1.163 +
1.164 + /* Remove the 'ConVar' handle type */
1.165 + g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
1.166 +}
1.167 +
1.168 +/**
1.169 + * Orange Box will never use this.
1.170 + */
1.171 +void ConVarManager::OnSourceModVSPReceived()
1.172 +{
1.173 + /**
1.174 + * Don't bother if the DLL is already hooked.
1.175 + */
1.176 + if (m_bIsDLLQueryHooked)
1.177 + {
1.178 + return;
1.179 + }
1.180 +
1.181 + /* For later MM:S versions, use the updated API, since it's cleaner. */
1.182 +#if defined METAMOD_PLAPI_VERSION
1.183 + int engine = g_SMAPI->GetSourceEngineBuild();
1.184 + if (engine == SOURCE_ENGINE_ORIGINAL || vsp_version < 2)
1.185 + {
1.186 + return;
1.187 + }
1.188 +#else
1.189 + if (g_HL2.IsOriginalEngine() || vsp_version < 2)
1.190 + {
1.191 + return;
1.192 + }
1.193 +#endif
1.194 +
1.195 + SH_ADD_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
1.196 + m_bIsVSPQueryHooked = true;
1.197 +}
1.198 +
1.199 +bool convar_cache_lookup(const char *name, ConVarInfo **pVar)
1.200 +{
1.201 + ConVarInfo **pLookup = convar_cache.retrieve(name);
1.202 + if (pLookup != NULL)
1.203 + {
1.204 + *pVar = *pLookup;
1.205 + return true;
1.206 + }
1.207 + else
1.208 + {
1.209 + return false;
1.210 + }
1.211 +}
1.212 +
1.213 +void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
1.214 +{
1.215 + /* Only check convars that have not been created by SourceMod's core */
1.216 + ConVarInfo *pInfo;
1.217 + if (!convar_cache_lookup(name, &pInfo))
1.218 + {
1.219 + return;
1.220 + }
1.221 +
1.222 + HandleSecurity sec(NULL, g_pCoreIdent);
1.223 +
1.224 + /* Remove it from our cache */
1.225 + m_ConVars.remove(pInfo);
1.226 + convar_cache.remove(name);
1.227 +
1.228 + /* Now make sure no plugins are referring to this pointer */
1.229 + IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
1.230 + while (pl_iter->MorePlugins())
1.231 + {
1.232 + IPlugin *pl = pl_iter->GetPlugin();
1.233 +
1.234 + ConVarList *pConVarList;
1.235 + if (pl->GetProperty("ConVarList", (void **)&pConVarList, true)
1.236 + && pConVarList != NULL)
1.237 + {
1.238 + pConVarList->remove(pInfo->pVar);
1.239 + }
1.240 +
1.241 + pl_iter->NextPlugin();
1.242 + }
1.243 +
1.244 + /* Free resources */
1.245 + g_HandleSys.FreeHandle(pInfo->handle, &sec);
1.246 + delete pInfo;
1.247 +}
1.248 +
1.249 +void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
1.250 +{
1.251 + ConVarList *pConVarList;
1.252 +
1.253 + /* If plugin has a convar list, free its memory */
1.254 + if (plugin->GetProperty("ConVarList", (void **)&pConVarList, true))
1.255 + {
1.256 + delete pConVarList;
1.257 + }
1.258 +}
1.259 +
1.260 +void ConVarManager::OnHandleDestroy(HandleType_t type, void *object)
1.261 +{
1.262 +}
1.263 +
1.264 +bool ConVarManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1.265 +{
1.266 + *pSize = sizeof(ConVar) + sizeof(ConVarInfo);
1.267 + return true;
1.268 +}
1.269 +
1.270 +void ConVarManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
1.271 +{
1.272 + int argcount = command.ArgC();
1.273 + if (argcount >= 3)
1.274 + {
1.275 + /* Get plugin index that was passed */
1.276 + const char *arg = command.Arg(2);
1.277 +
1.278 + /* Get plugin object */
1.279 + CPlugin *plugin = g_PluginSys.FindPluginByConsoleArg(arg);
1.280 +
1.281 + if (!plugin)
1.282 + {
1.283 + g_RootMenu.ConsolePrint("[SM] Plugin \"%s\" was not found.", arg);
1.284 + return;
1.285 + }
1.286 +
1.287 + /* Get plugin name */
1.288 + const sm_plugininfo_t *plinfo = plugin->GetPublicInfo();
1.289 + const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : plugin->GetFilename();
1.290 +
1.291 + ConVarList *pConVarList;
1.292 + ConVarList::iterator iter;
1.293 +
1.294 + /* If no convar list... */
1.295 + if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
1.296 + {
1.297 + g_RootMenu.ConsolePrint("[SM] No convars found for: %s", plname);
1.298 + return;
1.299 + }
1.300 +
1.301 + g_RootMenu.ConsolePrint("[SM] Listing %d convars for: %s", pConVarList->size(), plname);
1.302 + g_RootMenu.ConsolePrint(" %-32.31s %s", "[Name]", "[Value]");
1.303 +
1.304 + /* Iterate convar list and display each one */
1.305 + for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
1.306 + {
1.307 + const ConVar *pConVar = (*iter);
1.308 + g_RootMenu.ConsolePrint(" %-32.31s %s", pConVar->GetName(), pConVar->GetString());
1.309 + }
1.310 +
1.311 + return;
1.312 + }
1.313 +
1.314 + /* Display usage of subcommand */
1.315 + g_RootMenu.ConsolePrint("[SM] Usage: sm convars <plugin #>");
1.316 +}
1.317 +
1.318 +Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal, const char *description, int flags, bool hasMin, float min, bool hasMax, float max)
1.319 +{
1.320 + ConVar *pConVar = NULL;
1.321 + ConVarInfo *pInfo = NULL;
1.322 + Handle_t hndl = 0;
1.323 +
1.324 + /* Find out if the convar exists already */
1.325 + pConVar = icvar->FindVar(name);
1.326 +
1.327 + /* If the convar already exists... */
1.328 + if (pConVar)
1.329 + {
1.330 + /* Add convar to plugin's list */
1.331 + AddConVarToPluginList(pContext, pConVar);
1.332 +
1.333 + /* First find out if we already have a handle to it */
1.334 + if (convar_cache_lookup(name, &pInfo))
1.335 + {
1.336 + return pInfo->handle;
1.337 + }
1.338 + else
1.339 + {
1.340 + /* Create and initialize ConVarInfo structure */
1.341 + pInfo = new ConVarInfo();
1.342 + pInfo->sourceMod = false;
1.343 + pInfo->pChangeForward = NULL;
1.344 + pInfo->pVar = pConVar;
1.345 +
1.346 + /* If we don't, then create a new handle from the convar */
1.347 + hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
1.348 + if (hndl == BAD_HANDLE)
1.349 + {
1.350 + delete pInfo;
1.351 + return BAD_HANDLE;
1.352 + }
1.353 +
1.354 + pInfo->handle = hndl;
1.355 +
1.356 + /* Insert struct into caches */
1.357 + m_ConVars.push_back(pInfo);
1.358 + convar_cache.insert(name, pInfo);
1.359 + TrackConCommandBase(pConVar, this);
1.360 +
1.361 + return hndl;
1.362 + }
1.363 + }
1.364 +
1.365 + /* To prevent creating a convar that has the same name as a console command... ugh */
1.366 + ConCommandBase *pBase = icvar->GetCommands();
1.367 +
1.368 + while (pBase)
1.369 + {
1.370 + if (pBase->IsCommand() && strcmp(pBase->GetName(), name) == 0)
1.371 + {
1.372 + return BAD_HANDLE;
1.373 + }
1.374 +
1.375 + pBase = const_cast<ConCommandBase *>(pBase->GetNext());
1.376 + }
1.377 +
1.378 + /* Create and initialize ConVarInfo structure */
1.379 + pInfo = new ConVarInfo();
1.380 + pInfo->handle = hndl;
1.381 + pInfo->sourceMod = true;
1.382 + pInfo->pChangeForward = NULL;
1.383 +
1.384 + /* Create a handle from the new convar */
1.385 + hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
1.386 + if (hndl == BAD_HANDLE)
1.387 + {
1.388 + delete pInfo;
1.389 + return BAD_HANDLE;
1.390 + }
1.391 +
1.392 + pInfo->handle = hndl;
1.393 +
1.394 + /* Since an existing convar (or concmd with the same name) was not found , now we can finally create it */
1.395 + pConVar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(description), hasMin, min, hasMax, max);
1.396 + pInfo->pVar = pConVar;
1.397 +
1.398 + /* Add convar to plugin's list */
1.399 + AddConVarToPluginList(pContext, pConVar);
1.400 +
1.401 + /* Insert struct into caches */
1.402 + m_ConVars.push_back(pInfo);
1.403 + convar_cache.insert(name, pInfo);
1.404 +
1.405 + return hndl;
1.406 +}
1.407 +
1.408 +Handle_t ConVarManager::FindConVar(const char *name)
1.409 +{
1.410 + ConVar *pConVar = NULL;
1.411 + ConVarInfo *pInfo;
1.412 + Handle_t hndl;
1.413 +
1.414 + /* Search for convar */
1.415 + pConVar = icvar->FindVar(name);
1.416 +
1.417 + /* If it doesn't exist, then return an invalid handle */
1.418 + if (!pConVar)
1.419 + {
1.420 + return BAD_HANDLE;
1.421 + }
1.422 +
1.423 + /* At this point, the convar exists. So, find out if we already have a handle */
1.424 + if (convar_cache_lookup(name, &pInfo))
1.425 + {
1.426 + return pInfo->handle;
1.427 + }
1.428 +
1.429 + /* Create and initialize ConVarInfo structure */
1.430 + pInfo = new ConVarInfo();
1.431 + pInfo->sourceMod = false;
1.432 + pInfo->pChangeForward = NULL;
1.433 + pInfo->pVar = pConVar;
1.434 +
1.435 + /* If we don't have a handle, then create a new one */
1.436 + hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
1.437 + if (hndl == BAD_HANDLE)
1.438 + {
1.439 + delete pInfo;
1.440 + return BAD_HANDLE;
1.441 + }
1.442 +
1.443 + pInfo->handle = hndl;
1.444 +
1.445 + /* Insert struct into our caches */
1.446 + m_ConVars.push_back(pInfo);
1.447 + convar_cache.insert(name, pInfo);
1.448 + TrackConCommandBase(pConVar, this);
1.449 +
1.450 + return hndl;
1.451 +}
1.452 +
1.453 +void ConVarManager::AddConVarChangeListener(const char *name, IConVarChangeListener *pListener)
1.454 +{
1.455 + ConVarInfo *pInfo;
1.456 +
1.457 + if (FindConVar(name) == BAD_HANDLE)
1.458 + {
1.459 + return;
1.460 + }
1.461 +
1.462 + /* Find the convar in the lookup trie */
1.463 + if (convar_cache_lookup(name, &pInfo))
1.464 + {
1.465 + pInfo->changeListeners.push_back(pListener);
1.466 + }
1.467 +}
1.468 +
1.469 +void ConVarManager::RemoveConVarChangeListener(const char *name, IConVarChangeListener *pListener)
1.470 +{
1.471 + ConVarInfo *pInfo;
1.472 +
1.473 + /* Find the convar in the lookup trie */
1.474 + if (convar_cache_lookup(name, &pInfo))
1.475 + {
1.476 + pInfo->changeListeners.remove(pListener);
1.477 + }
1.478 +}
1.479 +
1.480 +void ConVarManager::HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
1.481 +{
1.482 + ConVarInfo *pInfo;
1.483 + IChangeableForward *pForward;
1.484 +
1.485 + /* Find the convar in the lookup trie */
1.486 + if (convar_cache_lookup(pConVar->GetName(), &pInfo))
1.487 + {
1.488 + /* Get the forward */
1.489 + pForward = pInfo->pChangeForward;
1.490 +
1.491 + /* If forward does not exist, create it */
1.492 + if (!pForward)
1.493 + {
1.494 + pForward = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS);
1.495 + pInfo->pChangeForward = pForward;
1.496 + }
1.497 +
1.498 + /* Add function to forward's list */
1.499 + pForward->AddFunction(pFunction);
1.500 + }
1.501 +}
1.502 +
1.503 +void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
1.504 +{
1.505 + ConVarInfo *pInfo;
1.506 + IChangeableForward *pForward;
1.507 + IPluginContext *pContext = pFunction->GetParentContext();
1.508 +
1.509 + /* Find the convar in the lookup trie */
1.510 + if (convar_cache_lookup(pConVar->GetName(), &pInfo))
1.511 + {
1.512 + /* Get the forward */
1.513 + pForward = pInfo->pChangeForward;
1.514 +
1.515 + /* If the forward doesn't exist, we can't unhook anything */
1.516 + if (!pForward)
1.517 + {
1.518 + pContext->ThrowNativeError("Convar \"%s\" has no active hook", pConVar->GetName());
1.519 + return;
1.520 + }
1.521 +
1.522 + /* Remove the function from the forward's list */
1.523 + if (!pForward->RemoveFunction(pFunction))
1.524 + {
1.525 + pContext->ThrowNativeError("Invalid hook callback specified for convar \"%s\"", pConVar->GetName());
1.526 + return;
1.527 + }
1.528 +
1.529 + /* If the forward now has 0 functions in it... */
1.530 + if (pForward->GetFunctionCount() == 0)
1.531 + {
1.532 + /* Free this forward */
1.533 + g_Forwards.ReleaseForward(pForward);
1.534 + pInfo->pChangeForward = NULL;
1.535 + }
1.536 + }
1.537 +}
1.538 +
1.539 +QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl)
1.540 +{
1.541 + QueryCvarCookie_t cookie;
1.542 +
1.543 + /* Call StartQueryCvarValue() in either the IVEngineServer or IServerPluginHelpers depending on situation */
1.544 + if (m_bIsDLLQueryHooked)
1.545 + {
1.546 + cookie = engine->StartQueryCvarValue(pPlayer, name);
1.547 + }
1.548 + else if (m_bIsVSPQueryHooked)
1.549 + {
1.550 + cookie = serverpluginhelpers->StartQueryCvarValue(pPlayer, name);
1.551 + }
1.552 + else
1.553 + {
1.554 + return InvalidQueryCvarCookie;
1.555 + }
1.556 +
1.557 + ConVarQuery query = {cookie, pCallback, hndl};
1.558 + m_ConVarQueries.push_back(query);
1.559 +
1.560 + return cookie;
1.561 +}
1.562 +
1.563 +void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
1.564 +{
1.565 + ConVarList *pConVarList;
1.566 + ConVarList::iterator iter;
1.567 + bool inserted = false;
1.568 + const char *orig = pConVar->GetName();
1.569 +
1.570 + IPlugin *plugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
1.571 +
1.572 + /* Check plugin for an existing convar list */
1.573 + if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
1.574 + {
1.575 + pConVarList = new ConVarList();
1.576 + plugin->SetProperty("ConVarList", pConVarList);
1.577 + }
1.578 + else if (pConVarList->find(pConVar) != pConVarList->end())
1.579 + {
1.580 + /* If convar is already in list, then don't add it */
1.581 + return;
1.582 + }
1.583 +
1.584 + /* Insert convar into list which is sorted alphabetically */
1.585 + for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
1.586 + {
1.587 + if (strcmp(orig, (*iter)->GetName()) < 0)
1.588 + {
1.589 + pConVarList->insert(iter, pConVar);
1.590 + inserted = true;
1.591 + break;
1.592 + }
1.593 + }
1.594 +
1.595 + if (!inserted)
1.596 + {
1.597 + pConVarList->push_back(pConVar);
1.598 + }
1.599 +}
1.600 +
1.601 +#if defined ORANGEBOX_BUILD
1.602 +void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
1.603 +#else
1.604 +void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
1.605 +#endif
1.606 +{
1.607 + /* If the values are the same, exit early in order to not trigger callbacks */
1.608 + if (strcmp(pConVar->GetString(), oldValue) == 0)
1.609 + {
1.610 + return;
1.611 + }
1.612 +
1.613 + ConVarInfo *pInfo;
1.614 +
1.615 + /* Find the convar in the lookup trie */
1.616 + if (!convar_cache_lookup(pConVar->GetName(), &pInfo))
1.617 + {
1.618 + return;
1.619 + }
1.620 +
1.621 + IChangeableForward *pForward = pInfo->pChangeForward;
1.622 +
1.623 + if (pInfo->changeListeners.size() != 0)
1.624 + {
1.625 + for (List<IConVarChangeListener *>::iterator i = pInfo->changeListeners.begin();
1.626 + i != pInfo->changeListeners.end();
1.627 + i++)
1.628 + {
1.629 +#if defined ORANGEBOX_BUILD
1.630 + (*i)->OnConVarChanged(pConVar, oldValue, flOldValue);
1.631 +#else
1.632 + (*i)->OnConVarChanged(pConVar, oldValue, atof(oldValue));
1.633 +#endif
1.634 + }
1.635 + }
1.636 +
1.637 + if (pForward != NULL)
1.638 + {
1.639 + /* Now call forwards in plugins that have hooked this */
1.640 + pForward->PushCell(pInfo->handle);
1.641 + pForward->PushString(oldValue);
1.642 + pForward->PushString(pConVar->GetString());
1.643 + pForward->Execute(NULL);
1.644 + }
1.645 +}
1.646 +
1.647 +bool ConVarManager::IsQueryingSupported()
1.648 +{
1.649 + return (m_bIsDLLQueryHooked || m_bIsVSPQueryHooked);
1.650 +}
1.651 +
1.652 +void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
1.653 +{
1.654 + IPluginFunction *pCallback = NULL;
1.655 + cell_t value = 0;
1.656 + List<ConVarQuery>::iterator iter;
1.657 +
1.658 + for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end(); iter++)
1.659 + {
1.660 + ConVarQuery &query = (*iter);
1.661 + if (query.cookie == cookie)
1.662 + {
1.663 + pCallback = query.pCallback;
1.664 + value = query.value;
1.665 + break;
1.666 + }
1.667 + }
1.668 +
1.669 + if (pCallback)
1.670 + {
1.671 + cell_t ret;
1.672 +
1.673 + pCallback->PushCell(cookie);
1.674 + pCallback->PushCell(engine->IndexOfEdict(pPlayer));
1.675 + pCallback->PushCell(result);
1.676 + pCallback->PushString(cvarName);
1.677 +
1.678 + if (result == eQueryCvarValueStatus_ValueIntact)
1.679 + {
1.680 + pCallback->PushString(cvarValue);
1.681 + }
1.682 + else
1.683 + {
1.684 + pCallback->PushString("\0");
1.685 + }
1.686 +
1.687 + pCallback->PushCell(value);
1.688 + pCallback->Execute(&ret);
1.689 +
1.690 + m_ConVarQueries.erase(iter);
1.691 + }
1.692 +}
1.693 +
1.694 +HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
1.695 +{
1.696 + ConVarInfo *pInfo;
1.697 + HandleError error;
1.698 +
1.699 + if ((error = g_HandleSys.ReadHandle(hndl, m_ConVarType, NULL, (void **)&pInfo)) != HandleError_None)
1.700 + {
1.701 + return error;
1.702 + }
1.703 +
1.704 + if (pVar)
1.705 + {
1.706 + *pVar = pInfo->pVar;
1.707 + }
1.708 +
1.709 + return error;
1.710 +}