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: }