ds@1952: /**
ds@1952: * vim: set ts=4 :
ds@1952: * =============================================================================
ds@1952: * SourceMod
ds@1952: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
ds@1952: * =============================================================================
ds@1952: *
ds@1952: * This program is free software; you can redistribute it and/or modify it under
ds@1952: * the terms of the GNU General Public License, version 3.0, as published by the
ds@1952: * Free Software Foundation.
ds@1952: *
ds@1952: * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1952: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1952: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
ds@1952: * details.
ds@1952: *
ds@1952: * You should have received a copy of the GNU General Public License along with
ds@1952: * this program. If not, see .
ds@1952: *
ds@1952: * As a special exception, AlliedModders LLC gives you permission to link the
ds@1952: * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1952: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1952: * by the Valve Corporation. You must obey the GNU General Public License in
ds@1952: * all respects for all other code used. Additionally, AlliedModders LLC grants
ds@1952: * this exception to all derivative works. AlliedModders LLC defines further
ds@1952: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1952: * or .
ds@1952: *
ds@1952: * Version: $Id$
ds@1952: */
ds@1952:
ds@1952: #include "ConVarManager.h"
ds@1952: #include "HalfLife2.h"
ds@1952: #include "PluginSys.h"
ds@1952: #include "ForwardSys.h"
ds@1952: #include "HandleSys.h"
ds@1952: #include "sm_srvcmds.h"
ds@1952: #include "sm_stringutil.h"
ds@1952: #include
ds@1952: #include
ds@1952:
ds@1952: ConVarManager g_ConVarManager;
ds@1952:
ds@1952: #if !defined ORANGEBOX_BUILD
ds@1952: #define CallGlobalChangeCallbacks CallGlobalChangeCallback
ds@1952: #endif
ds@1952:
ds@1952: #if defined ORANGEBOX_BUILD
ds@1952: SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *, float);
ds@1952: #else
ds@1952: SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *);
ds@1952: #endif
ds@1952:
ds@1952: SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
ds@1952: SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
ds@1952:
ds@1952: const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
ds@1952: typedef List ConVarList;
ds@1952: KTrie convar_cache;
ds@1952:
ds@1952: ConVarManager::ConVarManager() : m_ConVarType(0), m_bIsDLLQueryHooked(false), m_bIsVSPQueryHooked(false)
ds@1952: {
ds@1952: }
ds@1952:
ds@1952: ConVarManager::~ConVarManager()
ds@1952: {
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnSourceModStartup(bool late)
ds@1952: {
ds@1952: HandleAccess sec;
ds@1952:
ds@1952: /* Set up access rights for the 'ConVar' handle type */
ds@1952: sec.access[HandleAccess_Read] = 0;
ds@1952: sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
ds@1952: sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
ds@1952:
ds@1952: /* Create the 'ConVar' handle type */
ds@1952: m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnSourceModAllInitialized()
ds@1952: {
ds@1952: /**
ds@1952: * Episode 2 has this function by default, but the older versions do not.
ds@1952: */
ds@1952: #if !defined ORANGEBOX_BUILD
ds@1952: if (g_SMAPI->GetGameDLLVersion() >= 6)
ds@1952: {
ds@1952: SH_ADD_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
ds@1952: m_bIsDLLQueryHooked = true;
ds@1952: }
ds@1952: #endif
ds@1952:
ds@1952: SH_ADD_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
ds@1952:
ds@1952: /* Add the 'convars' option to the 'sm' console command */
ds@1952: g_RootMenu.AddRootConsoleCommand("cvars", "View convars created by a plugin", this);
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnSourceModShutdown()
ds@1952: {
ds@1952: List::iterator iter = m_ConVars.begin();
ds@1952: HandleSecurity sec(NULL, g_pCoreIdent);
ds@1952:
ds@1952: /* Iterate list of ConVarInfo structures, remove every one of them */
ds@1952: while (iter != m_ConVars.end())
ds@1952: {
ds@1952: ConVarInfo *pInfo = (*iter);
ds@1952:
ds@1952: iter = m_ConVars.erase(iter);
ds@1952:
ds@1952: g_HandleSys.FreeHandle(pInfo->handle, &sec);
ds@1952: if (pInfo->pChangeForward != NULL)
ds@1952: {
ds@1952: g_Forwards.ReleaseForward(pInfo->pChangeForward);
ds@1952: }
ds@1952: if (pInfo->sourceMod)
ds@1952: {
ds@1952: /* If we created it, we won't be tracking it, therefore it is
ds@1952: * safe to remove everything in one go.
ds@1952: */
ds@1952: META_UNREGCVAR(pInfo->pVar);
ds@1952: delete [] pInfo->pVar->GetName();
ds@1952: delete [] pInfo->pVar->GetHelpText();
ds@1952: delete [] pInfo->pVar->GetDefault();
ds@1952: delete pInfo->pVar;
ds@1952: }
ds@1952: else
ds@1952: {
ds@1952: /* If we didn't create it, we might be tracking it. Also,
ds@1952: * it could be unreadable.
ds@1952: */
ds@1952: UntrackConCommandBase(pInfo->pVar, this);
ds@1952: }
ds@1952:
ds@1952: /* It's not safe to read the name here, so we simply delete the
ds@1952: * the info struct and clear the lookup cache at the end.
ds@1952: */
ds@1952: delete pInfo;
ds@1952: }
ds@1952: convar_cache.clear();
ds@1952:
ds@1952: /* Unhook things */
ds@1952: if (m_bIsDLLQueryHooked)
ds@1952: {
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
ds@1952: m_bIsDLLQueryHooked = false;
ds@1952: }
ds@1952: else if (m_bIsVSPQueryHooked)
ds@1952: {
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
ds@1952: m_bIsVSPQueryHooked = false;
ds@1952: }
ds@1952:
ds@1952: SH_REMOVE_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
ds@1952:
ds@1952: /* Remove the 'convars' option from the 'sm' console command */
ds@1952: g_RootMenu.RemoveRootConsoleCommand("cvars", this);
ds@1952:
ds@1952: /* Remove the 'ConVar' handle type */
ds@1952: g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
ds@1952: }
ds@1952:
ds@1952: /**
ds@1952: * Orange Box will never use this.
ds@1952: */
ds@1952: void ConVarManager::OnSourceModVSPReceived()
ds@1952: {
ds@1952: /**
ds@1952: * Don't bother if the DLL is already hooked.
ds@1952: */
ds@1952: if (m_bIsDLLQueryHooked)
ds@1952: {
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: /* For later MM:S versions, use the updated API, since it's cleaner. */
ds@1952: #if defined METAMOD_PLAPI_VERSION
ds@1952: int engine = g_SMAPI->GetSourceEngineBuild();
ds@1952: if (engine == SOURCE_ENGINE_ORIGINAL || vsp_version < 2)
ds@1952: {
ds@1952: return;
ds@1952: }
ds@1952: #else
ds@1952: if (g_HL2.IsOriginalEngine() || vsp_version < 2)
ds@1952: {
ds@1952: return;
ds@1952: }
ds@1952: #endif
ds@1952:
ds@1952: SH_ADD_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
ds@1952: m_bIsVSPQueryHooked = true;
ds@1952: }
ds@1952:
ds@1952: bool convar_cache_lookup(const char *name, ConVarInfo **pVar)
ds@1952: {
ds@1952: ConVarInfo **pLookup = convar_cache.retrieve(name);
ds@1952: if (pLookup != NULL)
ds@1952: {
ds@1952: *pVar = *pLookup;
ds@1952: return true;
ds@1952: }
ds@1952: else
ds@1952: {
ds@1952: return false;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
ds@1952: {
ds@1952: /* Only check convars that have not been created by SourceMod's core */
ds@1952: ConVarInfo *pInfo;
ds@1952: if (!convar_cache_lookup(name, &pInfo))
ds@1952: {
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: HandleSecurity sec(NULL, g_pCoreIdent);
ds@1952:
ds@1952: /* Remove it from our cache */
ds@1952: m_ConVars.remove(pInfo);
ds@1952: convar_cache.remove(name);
ds@1952:
ds@1952: /* Now make sure no plugins are referring to this pointer */
ds@1952: IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
ds@1952: while (pl_iter->MorePlugins())
ds@1952: {
ds@1952: IPlugin *pl = pl_iter->GetPlugin();
ds@1952:
ds@1952: ConVarList *pConVarList;
ds@1952: if (pl->GetProperty("ConVarList", (void **)&pConVarList, true)
ds@1952: && pConVarList != NULL)
ds@1952: {
ds@1952: pConVarList->remove(pInfo->pVar);
ds@1952: }
ds@1952:
ds@1952: pl_iter->NextPlugin();
ds@1952: }
ds@1952:
ds@1952: /* Free resources */
ds@1952: g_HandleSys.FreeHandle(pInfo->handle, &sec);
ds@1952: delete pInfo;
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
ds@1952: {
ds@1952: ConVarList *pConVarList;
ds@1952:
ds@1952: /* If plugin has a convar list, free its memory */
ds@1952: if (plugin->GetProperty("ConVarList", (void **)&pConVarList, true))
ds@1952: {
ds@1952: delete pConVarList;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnHandleDestroy(HandleType_t type, void *object)
ds@1952: {
ds@1952: }
ds@1952:
ds@1952: bool ConVarManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
ds@1952: {
ds@1952: *pSize = sizeof(ConVar) + sizeof(ConVarInfo);
ds@1952: return true;
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
ds@1952: {
ds@1952: int argcount = command.ArgC();
ds@1952: if (argcount >= 3)
ds@1952: {
ds@1952: /* Get plugin index that was passed */
ds@1952: const char *arg = command.Arg(2);
ds@1952:
ds@1952: /* Get plugin object */
ds@1952: CPlugin *plugin = g_PluginSys.FindPluginByConsoleArg(arg);
ds@1952:
ds@1952: if (!plugin)
ds@1952: {
ds@1952: g_RootMenu.ConsolePrint("[SM] Plugin \"%s\" was not found.", arg);
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: /* Get plugin name */
ds@1952: const sm_plugininfo_t *plinfo = plugin->GetPublicInfo();
ds@1952: const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : plugin->GetFilename();
ds@1952:
ds@1952: ConVarList *pConVarList;
ds@1952: ConVarList::iterator iter;
ds@1952:
ds@1952: /* If no convar list... */
ds@1952: if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
ds@1952: {
ds@1952: g_RootMenu.ConsolePrint("[SM] No convars found for: %s", plname);
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: g_RootMenu.ConsolePrint("[SM] Listing %d convars for: %s", pConVarList->size(), plname);
ds@1952: g_RootMenu.ConsolePrint(" %-32.31s %s", "[Name]", "[Value]");
ds@1952:
ds@1952: /* Iterate convar list and display each one */
ds@1952: for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
ds@1952: {
ds@1952: const ConVar *pConVar = (*iter);
ds@1952: g_RootMenu.ConsolePrint(" %-32.31s %s", pConVar->GetName(), pConVar->GetString());
ds@1952: }
ds@1952:
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: /* Display usage of subcommand */
ds@1952: g_RootMenu.ConsolePrint("[SM] Usage: sm convars ");
ds@1952: }
ds@1952:
ds@1952: 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)
ds@1952: {
ds@1952: ConVar *pConVar = NULL;
ds@1952: ConVarInfo *pInfo = NULL;
ds@1952: Handle_t hndl = 0;
ds@1952:
ds@1952: /* Find out if the convar exists already */
ds@1952: pConVar = icvar->FindVar(name);
ds@1952:
ds@1952: /* If the convar already exists... */
ds@1952: if (pConVar)
ds@1952: {
ds@1952: /* Add convar to plugin's list */
ds@1952: AddConVarToPluginList(pContext, pConVar);
ds@1952:
ds@1952: /* First find out if we already have a handle to it */
ds@1952: if (convar_cache_lookup(name, &pInfo))
ds@1952: {
ds@1952: return pInfo->handle;
ds@1952: }
ds@1952: else
ds@1952: {
ds@1952: /* Create and initialize ConVarInfo structure */
ds@1952: pInfo = new ConVarInfo();
ds@1952: pInfo->sourceMod = false;
ds@1952: pInfo->pChangeForward = NULL;
ds@1952: pInfo->pVar = pConVar;
ds@1952:
ds@1952: /* If we don't, then create a new handle from the convar */
ds@1952: hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
ds@1952: if (hndl == BAD_HANDLE)
ds@1952: {
ds@1952: delete pInfo;
ds@1952: return BAD_HANDLE;
ds@1952: }
ds@1952:
ds@1952: pInfo->handle = hndl;
ds@1952:
ds@1952: /* Insert struct into caches */
ds@1952: m_ConVars.push_back(pInfo);
ds@1952: convar_cache.insert(name, pInfo);
ds@1952: TrackConCommandBase(pConVar, this);
ds@1952:
ds@1952: return hndl;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: /* To prevent creating a convar that has the same name as a console command... ugh */
ds@1952: ConCommandBase *pBase = icvar->GetCommands();
ds@1952:
ds@1952: while (pBase)
ds@1952: {
ds@1952: if (pBase->IsCommand() && strcmp(pBase->GetName(), name) == 0)
ds@1952: {
ds@1952: return BAD_HANDLE;
ds@1952: }
ds@1952:
ds@1952: pBase = const_cast(pBase->GetNext());
ds@1952: }
ds@1952:
ds@1952: /* Create and initialize ConVarInfo structure */
ds@1952: pInfo = new ConVarInfo();
ds@1952: pInfo->handle = hndl;
ds@1952: pInfo->sourceMod = true;
ds@1952: pInfo->pChangeForward = NULL;
ds@1952:
ds@1952: /* Create a handle from the new convar */
ds@1952: hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
ds@1952: if (hndl == BAD_HANDLE)
ds@1952: {
ds@1952: delete pInfo;
ds@1952: return BAD_HANDLE;
ds@1952: }
ds@1952:
ds@1952: pInfo->handle = hndl;
ds@1952:
ds@1952: /* Since an existing convar (or concmd with the same name) was not found , now we can finally create it */
ds@1952: pConVar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(description), hasMin, min, hasMax, max);
ds@1952: pInfo->pVar = pConVar;
ds@1952:
ds@1952: /* Add convar to plugin's list */
ds@1952: AddConVarToPluginList(pContext, pConVar);
ds@1952:
ds@1952: /* Insert struct into caches */
ds@1952: m_ConVars.push_back(pInfo);
ds@1952: convar_cache.insert(name, pInfo);
ds@1952:
ds@1952: return hndl;
ds@1952: }
ds@1952:
ds@1952: Handle_t ConVarManager::FindConVar(const char *name)
ds@1952: {
ds@1952: ConVar *pConVar = NULL;
ds@1952: ConVarInfo *pInfo;
ds@1952: Handle_t hndl;
ds@1952:
ds@1952: /* Search for convar */
ds@1952: pConVar = icvar->FindVar(name);
ds@1952:
ds@1952: /* If it doesn't exist, then return an invalid handle */
ds@1952: if (!pConVar)
ds@1952: {
ds@1952: return BAD_HANDLE;
ds@1952: }
ds@1952:
ds@1952: /* At this point, the convar exists. So, find out if we already have a handle */
ds@1952: if (convar_cache_lookup(name, &pInfo))
ds@1952: {
ds@1952: return pInfo->handle;
ds@1952: }
ds@1952:
ds@1952: /* Create and initialize ConVarInfo structure */
ds@1952: pInfo = new ConVarInfo();
ds@1952: pInfo->sourceMod = false;
ds@1952: pInfo->pChangeForward = NULL;
ds@1952: pInfo->pVar = pConVar;
ds@1952:
ds@1952: /* If we don't have a handle, then create a new one */
ds@1952: hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
ds@1952: if (hndl == BAD_HANDLE)
ds@1952: {
ds@1952: delete pInfo;
ds@1952: return BAD_HANDLE;
ds@1952: }
ds@1952:
ds@1952: pInfo->handle = hndl;
ds@1952:
ds@1952: /* Insert struct into our caches */
ds@1952: m_ConVars.push_back(pInfo);
ds@1952: convar_cache.insert(name, pInfo);
ds@1952: TrackConCommandBase(pConVar, this);
ds@1952:
ds@1952: return hndl;
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::AddConVarChangeListener(const char *name, IConVarChangeListener *pListener)
ds@1952: {
ds@1952: ConVarInfo *pInfo;
ds@1952:
ds@1952: if (FindConVar(name) == BAD_HANDLE)
ds@1952: {
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: /* Find the convar in the lookup trie */
ds@1952: if (convar_cache_lookup(name, &pInfo))
ds@1952: {
ds@1952: pInfo->changeListeners.push_back(pListener);
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::RemoveConVarChangeListener(const char *name, IConVarChangeListener *pListener)
ds@1952: {
ds@1952: ConVarInfo *pInfo;
ds@1952:
ds@1952: /* Find the convar in the lookup trie */
ds@1952: if (convar_cache_lookup(name, &pInfo))
ds@1952: {
ds@1952: pInfo->changeListeners.remove(pListener);
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
ds@1952: {
ds@1952: ConVarInfo *pInfo;
ds@1952: IChangeableForward *pForward;
ds@1952:
ds@1952: /* Find the convar in the lookup trie */
ds@1952: if (convar_cache_lookup(pConVar->GetName(), &pInfo))
ds@1952: {
ds@1952: /* Get the forward */
ds@1952: pForward = pInfo->pChangeForward;
ds@1952:
ds@1952: /* If forward does not exist, create it */
ds@1952: if (!pForward)
ds@1952: {
ds@1952: pForward = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS);
ds@1952: pInfo->pChangeForward = pForward;
ds@1952: }
ds@1952:
ds@1952: /* Add function to forward's list */
ds@1952: pForward->AddFunction(pFunction);
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
ds@1952: {
ds@1952: ConVarInfo *pInfo;
ds@1952: IChangeableForward *pForward;
ds@1952: IPluginContext *pContext = pFunction->GetParentContext();
ds@1952:
ds@1952: /* Find the convar in the lookup trie */
ds@1952: if (convar_cache_lookup(pConVar->GetName(), &pInfo))
ds@1952: {
ds@1952: /* Get the forward */
ds@1952: pForward = pInfo->pChangeForward;
ds@1952:
ds@1952: /* If the forward doesn't exist, we can't unhook anything */
ds@1952: if (!pForward)
ds@1952: {
ds@1952: pContext->ThrowNativeError("Convar \"%s\" has no active hook", pConVar->GetName());
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: /* Remove the function from the forward's list */
ds@1952: if (!pForward->RemoveFunction(pFunction))
ds@1952: {
ds@1952: pContext->ThrowNativeError("Invalid hook callback specified for convar \"%s\"", pConVar->GetName());
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: /* If the forward now has 0 functions in it... */
ds@1952: if (pForward->GetFunctionCount() == 0)
ds@1952: {
ds@1952: /* Free this forward */
ds@1952: g_Forwards.ReleaseForward(pForward);
ds@1952: pInfo->pChangeForward = NULL;
ds@1952: }
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl)
ds@1952: {
ds@1952: QueryCvarCookie_t cookie;
ds@1952:
ds@1952: /* Call StartQueryCvarValue() in either the IVEngineServer or IServerPluginHelpers depending on situation */
ds@1952: if (m_bIsDLLQueryHooked)
ds@1952: {
ds@1952: cookie = engine->StartQueryCvarValue(pPlayer, name);
ds@1952: }
ds@1952: else if (m_bIsVSPQueryHooked)
ds@1952: {
ds@1952: cookie = serverpluginhelpers->StartQueryCvarValue(pPlayer, name);
ds@1952: }
ds@1952: else
ds@1952: {
ds@1952: return InvalidQueryCvarCookie;
ds@1952: }
ds@1952:
ds@1952: ConVarQuery query = {cookie, pCallback, hndl};
ds@1952: m_ConVarQueries.push_back(query);
ds@1952:
ds@1952: return cookie;
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
ds@1952: {
ds@1952: ConVarList *pConVarList;
ds@1952: ConVarList::iterator iter;
ds@1952: bool inserted = false;
ds@1952: const char *orig = pConVar->GetName();
ds@1952:
ds@1952: IPlugin *plugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
ds@1952:
ds@1952: /* Check plugin for an existing convar list */
ds@1952: if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
ds@1952: {
ds@1952: pConVarList = new ConVarList();
ds@1952: plugin->SetProperty("ConVarList", pConVarList);
ds@1952: }
ds@1952: else if (pConVarList->find(pConVar) != pConVarList->end())
ds@1952: {
ds@1952: /* If convar is already in list, then don't add it */
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: /* Insert convar into list which is sorted alphabetically */
ds@1952: for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
ds@1952: {
ds@1952: if (strcmp(orig, (*iter)->GetName()) < 0)
ds@1952: {
ds@1952: pConVarList->insert(iter, pConVar);
ds@1952: inserted = true;
ds@1952: break;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: if (!inserted)
ds@1952: {
ds@1952: pConVarList->push_back(pConVar);
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: #if defined ORANGEBOX_BUILD
ds@1952: void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
ds@1952: #else
ds@1952: void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
ds@1952: #endif
ds@1952: {
ds@1952: /* If the values are the same, exit early in order to not trigger callbacks */
ds@1952: if (strcmp(pConVar->GetString(), oldValue) == 0)
ds@1952: {
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: ConVarInfo *pInfo;
ds@1952:
ds@1952: /* Find the convar in the lookup trie */
ds@1952: if (!convar_cache_lookup(pConVar->GetName(), &pInfo))
ds@1952: {
ds@1952: return;
ds@1952: }
ds@1952:
ds@1952: IChangeableForward *pForward = pInfo->pChangeForward;
ds@1952:
ds@1952: if (pInfo->changeListeners.size() != 0)
ds@1952: {
ds@1952: for (List::iterator i = pInfo->changeListeners.begin();
ds@1952: i != pInfo->changeListeners.end();
ds@1952: i++)
ds@1952: {
ds@1952: #if defined ORANGEBOX_BUILD
ds@1952: (*i)->OnConVarChanged(pConVar, oldValue, flOldValue);
ds@1952: #else
ds@1952: (*i)->OnConVarChanged(pConVar, oldValue, atof(oldValue));
ds@1952: #endif
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: if (pForward != NULL)
ds@1952: {
ds@1952: /* Now call forwards in plugins that have hooked this */
ds@1952: pForward->PushCell(pInfo->handle);
ds@1952: pForward->PushString(oldValue);
ds@1952: pForward->PushString(pConVar->GetString());
ds@1952: pForward->Execute(NULL);
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: bool ConVarManager::IsQueryingSupported()
ds@1952: {
ds@1952: return (m_bIsDLLQueryHooked || m_bIsVSPQueryHooked);
ds@1952: }
ds@1952:
ds@1952: void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
ds@1952: {
ds@1952: IPluginFunction *pCallback = NULL;
ds@1952: cell_t value = 0;
ds@1952: List::iterator iter;
ds@1952:
ds@1952: for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end(); iter++)
ds@1952: {
ds@1952: ConVarQuery &query = (*iter);
ds@1952: if (query.cookie == cookie)
ds@1952: {
ds@1952: pCallback = query.pCallback;
ds@1952: value = query.value;
ds@1952: break;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: if (pCallback)
ds@1952: {
ds@1952: cell_t ret;
ds@1952:
ds@1952: pCallback->PushCell(cookie);
ds@1952: pCallback->PushCell(engine->IndexOfEdict(pPlayer));
ds@1952: pCallback->PushCell(result);
ds@1952: pCallback->PushString(cvarName);
ds@1952:
ds@1952: if (result == eQueryCvarValueStatus_ValueIntact)
ds@1952: {
ds@1952: pCallback->PushString(cvarValue);
ds@1952: }
ds@1952: else
ds@1952: {
ds@1952: pCallback->PushString("\0");
ds@1952: }
ds@1952:
ds@1952: pCallback->PushCell(value);
ds@1952: pCallback->Execute(&ret);
ds@1952:
ds@1952: m_ConVarQueries.erase(iter);
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
ds@1952: {
ds@1952: ConVarInfo *pInfo;
ds@1952: HandleError error;
ds@1952:
ds@1952: if ((error = g_HandleSys.ReadHandle(hndl, m_ConVarType, NULL, (void **)&pInfo)) != HandleError_None)
ds@1952: {
ds@1952: return error;
ds@1952: }
ds@1952:
ds@1952: if (pVar)
ds@1952: {
ds@1952: *pVar = pInfo->pVar;
ds@1952: }
ds@1952:
ds@1952: return error;
ds@1952: }