1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/ConVarManager.cpp Tue Mar 06 06:49:35 2007 +0000
1.3 @@ -0,0 +1,407 @@
1.4 +/**
1.5 +* ===============================================================
1.6 +* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
1.7 +* ===============================================================
1.8 +*
1.9 +* This file is not open source and may not be copied without explicit
1.10 +* written permission of AlliedModders LLC. This file may not be redistributed
1.11 +* in whole or significant part.
1.12 +* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
1.13 +*
1.14 +* Version: $Id$
1.15 +*/
1.16 +
1.17 +#include "ConVarManager.h"
1.18 +#include "PluginSys.h"
1.19 +#include "ForwardSys.h"
1.20 +#include "HandleSys.h"
1.21 +#include "sm_srvcmds.h"
1.22 +#include "sm_stringutil.h"
1.23 +#include <sh_vector.h>
1.24 +
1.25 +ConVarManager g_ConVarManager;
1.26 +
1.27 +ConVarManager::ConVarManager() : m_ConVarType(0)
1.28 +{
1.29 + // Create a convar lookup trie
1.30 + m_ConVarCache = sm_trie_create();
1.31 +}
1.32 +
1.33 +ConVarManager::~ConVarManager()
1.34 +{
1.35 + List<ConVarInfo *>::iterator iter;
1.36 +
1.37 + // Destroy our convar lookup trie
1.38 + sm_trie_destroy(m_ConVarCache);
1.39 +
1.40 + // Destroy all the ConVarInfo structures
1.41 + for (iter = m_ConVars.begin(); iter != m_ConVars.end(); iter++)
1.42 + {
1.43 + delete (*iter);
1.44 + }
1.45 +
1.46 + m_ConVars.clear();
1.47 +}
1.48 +
1.49 +void ConVarManager::OnSourceModAllInitialized()
1.50 +{
1.51 + HandleAccess sec;
1.52 +
1.53 + // Set up access rights for the 'ConVar' handle type
1.54 + sec.access[HandleAccess_Read] = 0;
1.55 + sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.56 + sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.57 +
1.58 + // Create the 'ConVar' handle type
1.59 + m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
1.60 +
1.61 + // Add the 'cvars' option to the 'sm' console command
1.62 + g_RootMenu.AddRootConsoleCommand("convars", "View convars created by a plugin", this);
1.63 +}
1.64 +
1.65 +void ConVarManager::OnSourceModShutdown()
1.66 +{
1.67 + IChangeableForward *fwd;
1.68 + List<ConVarInfo *>::iterator i;
1.69 +
1.70 + // Iterate list of ConVarInfo structures
1.71 + for (i = m_ConVars.begin(); i != m_ConVars.end(); i++)
1.72 + {
1.73 + fwd = (*i)->changeForward;
1.74 +
1.75 + // Free any convar-change forwards that still exist
1.76 + if (fwd)
1.77 + {
1.78 + g_Forwards.ReleaseForward(fwd);
1.79 + }
1.80 + }
1.81 +
1.82 + // Remove the 'cvars' option from the 'sm' console command
1.83 + g_RootMenu.RemoveRootConsoleCommand("convars", this);
1.84 +
1.85 + // Remove the 'ConVar' handle type
1.86 + g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
1.87 +}
1.88 +
1.89 +void ConVarManager::OnPluginDestroyed(IPlugin *plugin)
1.90 +{
1.91 + CVector<ConVar *> *cvarList;
1.92 +
1.93 + // If plugin has a convar list, free its memory
1.94 + if (plugin->GetProperty("ConVarList", reinterpret_cast<void **>(&cvarList), true))
1.95 + {
1.96 + delete cvarList;
1.97 + }
1.98 +}
1.99 +
1.100 +void ConVarManager::OnHandleDestroy(HandleType_t type, void *object)
1.101 +{
1.102 + ConVarInfo *info;
1.103 + ConVar *pConVar = static_cast<ConVar *>(object);
1.104 +
1.105 + // Find convar in lookup trie
1.106 + sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), reinterpret_cast<void **>(&info));
1.107 +
1.108 + // If convar was created by SourceMod plugin...
1.109 + if (info->sourceMod)
1.110 + {
1.111 + // Delete string allocations
1.112 + delete [] pConVar->GetName();
1.113 + delete [] pConVar->GetDefault();
1.114 + delete [] pConVar->GetHelpText();
1.115 +
1.116 + // Then unlink it from SourceMM
1.117 + g_SMAPI->UnregisterConCmdBase(g_PLAPI, pConVar);
1.118 + }
1.119 +}
1.120 +
1.121 +void ConVarManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
1.122 +{
1.123 + if (argcount >= 3)
1.124 + {
1.125 + int id = 1;
1.126 +
1.127 + // Get plugin index that was passed
1.128 + int num = atoi(g_RootMenu.GetArgument(2));
1.129 +
1.130 + // If invalid plugin index...
1.131 + if (num < 1 || num > (int)g_PluginSys.GetPluginCount())
1.132 + {
1.133 + g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
1.134 + return;
1.135 + }
1.136 +
1.137 + // Get plugin object
1.138 + CPlugin *pl = g_PluginSys.GetPluginByOrder(num);
1.139 +
1.140 + CVector<ConVar *> *cvarList = NULL;
1.141 +
1.142 + // Get convar list from 'ConVar' property
1.143 + pl->GetProperty("ConVar", reinterpret_cast<void **>(&cvarList));
1.144 +
1.145 + // If no cvar list...
1.146 + if (cvarList == NULL)
1.147 + {
1.148 + g_RootMenu.ConsolePrint("[SM] No convars for \"%s\"", pl->GetPublicInfo()->name);
1.149 + return;
1.150 + }
1.151 +
1.152 + g_RootMenu.ConsolePrint("[SM] Displaying convars for \"%s\"", pl->GetPublicInfo()->name);
1.153 +
1.154 + // Iterate convar list and display each one
1.155 + for (size_t i = 0; i < cvarList->size(); i++, id++)
1.156 + {
1.157 + ConVar *pConVar = (*cvarList)[i];
1.158 + g_RootMenu.ConsolePrint(" %02d \"%s\" = \"%s\"", id, pConVar->GetName(), pConVar->GetString());
1.159 + }
1.160 +
1.161 + return;
1.162 + }
1.163 +
1.164 + // Display usage of subcommand
1.165 + g_RootMenu.ConsolePrint("[SM] Usage: sm convars <plugin #>");
1.166 +}
1.167 +
1.168 +Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal, const char *helpText, int flags, bool hasMin, float min, bool hasMax, float max)
1.169 +{
1.170 + ConVar *pConVar = NULL;
1.171 + ConVarInfo *info = NULL;
1.172 + CVector<ConVar *> *cvarList = NULL;
1.173 + Handle_t hndl = 0;
1.174 +
1.175 + // Find out if the convar exists already
1.176 + pConVar = icvar->FindVar(name);
1.177 +
1.178 + // If the convar already exists...
1.179 + if (pConVar != NULL)
1.180 + {
1.181 + // First check if we already have a handle to it
1.182 + if (sm_trie_retrieve(m_ConVarCache, name, reinterpret_cast<void **>(&info)))
1.183 + {
1.184 + // If we do, then return that handle
1.185 + return info->handle;
1.186 + } else {
1.187 + // If we don't, then create a new handle from the convar
1.188 + hndl = g_HandleSys.CreateHandle(m_ConVarType, pConVar, NULL, g_pCoreIdent, NULL);
1.189 +
1.190 + info = new ConVarInfo;
1.191 + info->handle = hndl;
1.192 + info->sourceMod = false;
1.193 + info->changeForward = NULL;
1.194 + info->origCallback = pConVar->GetCallback();
1.195 +
1.196 + m_ConVars.push_back(info);
1.197 +
1.198 + return hndl;
1.199 + }
1.200 + }
1.201 +
1.202 + // To prevent creating a convar that has the same name as a console command... ugh
1.203 + ConCommandBase *pBase = icvar->GetCommands();
1.204 +
1.205 + while (pBase)
1.206 + {
1.207 + if (strcmp(pBase->GetName(), name) == 0)
1.208 + {
1.209 + return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same name already exists.", name);
1.210 + }
1.211 +
1.212 + pBase = const_cast<ConCommandBase *>(pBase->GetNext());
1.213 + }
1.214 +
1.215 + // Since we didn't find an existing convar (or concmd with the same name), now we can finally create it!
1.216 + pConVar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(helpText), hasMin, min, hasMax, max);
1.217 +
1.218 + // Find plugin creating convar
1.219 + IPlugin *pl = g_PluginSys.FindPluginByContext(pContext->GetContext());
1.220 +
1.221 + // Get convar list from 'ConVar' property of plugin
1.222 + pl->GetProperty("ConVarList", reinterpret_cast<void **>(&cvarList));
1.223 +
1.224 + // If 'ConVar' property doesn't exist...
1.225 + if (cvarList == NULL)
1.226 + {
1.227 + // Then create it
1.228 + cvarList = new CVector<ConVar *>;
1.229 + pl->SetProperty("ConVarList", cvarList);
1.230 + }
1.231 +
1.232 + // Add new convar to plugin's list
1.233 + cvarList->push_back(pConVar);
1.234 +
1.235 + // Create a handle from the new convar
1.236 + hndl = g_HandleSys.CreateHandle(m_ConVarType, pConVar, NULL, g_pCoreIdent, NULL);
1.237 +
1.238 + info = new ConVarInfo;
1.239 + info->handle = hndl;
1.240 + info->sourceMod = true;
1.241 + info->changeForward = NULL;
1.242 + info->origCallback = NULL;
1.243 +
1.244 + m_ConVars.push_back(info);
1.245 +
1.246 + // Insert the handle into our lookup trie
1.247 + sm_trie_insert(m_ConVarCache, name, info);
1.248 +
1.249 + return hndl;
1.250 +}
1.251 +
1.252 +Handle_t ConVarManager::FindConVar(const char *name)
1.253 +{
1.254 + ConVar *pConVar = NULL;
1.255 + ConVarInfo *info = NULL;
1.256 + Handle_t hndl = 0;
1.257 +
1.258 + // Search for convar
1.259 + pConVar = icvar->FindVar(name);
1.260 +
1.261 + // If it doesn't exist, then return an invalid handle
1.262 + if (pConVar == NULL)
1.263 + {
1.264 + return BAD_HANDLE;
1.265 + }
1.266 +
1.267 + // At this point, convar exists, so find out if we already have a handle for it
1.268 + if (sm_trie_retrieve(m_ConVarCache, name, reinterpret_cast<void **>(&info)))
1.269 + {
1.270 + // If we do, then return that handle
1.271 + return info->handle;
1.272 + }
1.273 +
1.274 + // If we don't, then create a new handle from the convar
1.275 + hndl = g_HandleSys.CreateHandle(m_ConVarType, pConVar, NULL, g_pCoreIdent, NULL);
1.276 +
1.277 + info = new ConVarInfo;
1.278 + info->handle = hndl;
1.279 + info->sourceMod = false;
1.280 + info->changeForward = NULL;
1.281 + info->origCallback = pConVar->GetCallback();
1.282 +
1.283 + m_ConVars.push_back(info);
1.284 +
1.285 + // Insert the handle into our cache
1.286 + sm_trie_insert(m_ConVarCache, name, info);
1.287 +
1.288 + return hndl;
1.289 +}
1.290 +
1.291 +void ConVarManager::HookConVarChange(IPluginContext *pContext, ConVar *pConVar, funcid_t funcid)
1.292 +{
1.293 + IPluginFunction *func = pContext->GetFunctionById(funcid);
1.294 + IChangeableForward *fwd = NULL;
1.295 + char fwdName[64];
1.296 + ConVarInfo *info = NULL;
1.297 +
1.298 + // This shouldn't happen...
1.299 + if (func == NULL)
1.300 + {
1.301 + pContext->ThrowNativeError("Invalid function specified");
1.302 + return;
1.303 + }
1.304 +
1.305 + // Create a forward name
1.306 + UTIL_Format(fwdName, sizeof(fwdName), "ConVar.%s", pConVar->GetName());
1.307 +
1.308 + // First find out if the forward already exists
1.309 + g_Forwards.FindForward(fwdName, &fwd);
1.310 +
1.311 + // If the forward doesn't exist...
1.312 + if (fwd == NULL)
1.313 + {
1.314 + // This is the forward's parameter type list
1.315 + ParamType p[] = {Param_Cell, Param_String, Param_String};
1.316 +
1.317 + // Create the forward
1.318 + fwd = g_Forwards.CreateForwardEx(fwdName, ET_Ignore, 3, p);
1.319 +
1.320 + // Find the convar in the lookup trie
1.321 + if (sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), reinterpret_cast<void **>(&info)))
1.322 + {
1.323 + // Set the convar's forward to the newly created one
1.324 + info->changeForward = fwd;
1.325 +
1.326 + // Set the convar's callback to our static one
1.327 + pConVar->InstallChangeCallback(OnConVarChanged);
1.328 + }
1.329 + }
1.330 +
1.331 + // Add the function to the forward's list
1.332 + fwd->AddFunction(func);
1.333 +}
1.334 +
1.335 +void ConVarManager::UnhookConVarChange(IPluginContext *pContext, ConVar *pConVar, funcid_t funcid)
1.336 +{
1.337 + IPluginFunction *func = pContext->GetFunctionById(funcid);
1.338 + IChangeableForward *fwd = NULL;
1.339 + ConVarInfo *info = NULL;
1.340 +
1.341 + // This shouldn't happen...
1.342 + if (func == NULL)
1.343 + {
1.344 + pContext->ThrowNativeError("Invalid function specified");
1.345 + return;
1.346 + }
1.347 +
1.348 + // Find the convar in the lookup trie
1.349 + if (sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), reinterpret_cast<void **>(&info)))
1.350 + {
1.351 + // Get the forward
1.352 + fwd = info->changeForward;
1.353 +
1.354 + // If the forward doesn't exist, we can't unhook anything
1.355 + if (fwd == NULL)
1.356 + {
1.357 + pContext->ThrowNativeError("Convar \"%s\" has no active hook", pConVar->GetName());
1.358 + return;
1.359 + }
1.360 +
1.361 + // Remove the function from the forward's list
1.362 + if (!fwd->RemoveFunction(func))
1.363 + {
1.364 + pContext->ThrowNativeError("Invalid hook callback specified for convar \"%s\"", pConVar->GetName());
1.365 + return;
1.366 + }
1.367 +
1.368 + // If the forward now has 0 functions in it...
1.369 + if (fwd->GetFunctionCount() == 0)
1.370 + {
1.371 + // Free this forward
1.372 + g_Forwards.ReleaseForward(fwd);
1.373 + info->changeForward = NULL;
1.374 +
1.375 + // Put the back the original convar callback
1.376 + pConVar->InstallChangeCallback(info->origCallback);
1.377 + }
1.378 + }
1.379 +}
1.380 +
1.381 +void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
1.382 +{
1.383 + // If the values are the same...
1.384 + if (strcmp(pConVar->GetString(), oldValue) == 0)
1.385 + {
1.386 + // Exit early in order to not trigger callbacks
1.387 + return;
1.388 + }
1.389 +
1.390 + Trie *cache = g_ConVarManager.GetConVarCache();
1.391 + ConVarInfo *info;
1.392 +
1.393 + // Find the convar in the lookup trie
1.394 + sm_trie_retrieve(cache, pConVar->GetName(), reinterpret_cast<void **>(&info));
1.395 +
1.396 + FnChangeCallback origCallback = info->origCallback;
1.397 + IChangeableForward *fwd = info->changeForward;
1.398 +
1.399 + // If there was a change callback installed previously, call it
1.400 + if (origCallback)
1.401 + {
1.402 + origCallback(pConVar, oldValue);
1.403 + }
1.404 +
1.405 + // Now call forwards in plugins that have hooked this
1.406 + fwd->PushCell(info->handle);
1.407 + fwd->PushString(pConVar->GetString());
1.408 + fwd->PushString(oldValue);
1.409 + fwd->Execute(NULL);
1.410 +}