1.1 --- a/core/CConVarManager.cpp Sun Feb 11 00:17:54 2007 +0000
1.2 +++ b/core/CConVarManager.cpp Sun Feb 11 11:09:38 2007 +0000
1.3 @@ -15,43 +15,86 @@
1.4 #include "CLogger.h"
1.5 #include "PluginSys.h"
1.6 #include "sm_srvcmds.h"
1.7 +#include "sm_stringutil.h"
1.8
1.9 CConVarManager g_ConVarManager;
1.10
1.11 -CConVarManager::CConVarManager()
1.12 +CConVarManager::CConVarManager() : m_ConVarType(0)
1.13 {
1.14 - m_ConVarType = 0;
1.15 - m_CvarCache = sm_trie_create();
1.16 + // Create a convar lookup trie
1.17 + m_ConVarCache = sm_trie_create();
1.18 }
1.19
1.20 CConVarManager::~CConVarManager()
1.21 {
1.22 - sm_trie_destroy(m_CvarCache);
1.23 + List<ConVarInfo *>::iterator i;
1.24 +
1.25 + // Destroy our convar lookup trie
1.26 + sm_trie_destroy(m_ConVarCache);
1.27 +
1.28 + // Destroy all the ConVarInfo structures
1.29 + for (i = m_ConVars.begin(); i != m_ConVars.end(); i++)
1.30 + {
1.31 + delete (*i);
1.32 + }
1.33 +
1.34 + m_ConVars.clear();
1.35 }
1.36
1.37 void CConVarManager::OnSourceModAllInitialized()
1.38 {
1.39 HandleAccess sec;
1.40
1.41 + // Set up access rights for the 'ConVar' handle type
1.42 sec.access[HandleAccess_Read] = 0;
1.43 - sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
1.44 - sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
1.45 + sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.46 + sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.47
1.48 + // Create the 'ConVar' handle type
1.49 m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
1.50
1.51 - g_RootMenu.AddRootConsoleCommand("cvars", "View convars associated with a plugin", this);
1.52 + // Add the 'cvars' option to the 'sm' console command
1.53 + g_RootMenu.AddRootConsoleCommand("cvars", "View convars created by a plugin", this);
1.54 }
1.55
1.56 void CConVarManager::OnSourceModShutdown()
1.57 {
1.58 + IChangeableForward *fwd;
1.59 + List<ConVarInfo *>::iterator i;
1.60 +
1.61 + // Iterate list of ConVarInfo structures
1.62 + for (i = m_ConVars.begin(); i != m_ConVars.end(); i++)
1.63 + {
1.64 + fwd = (*i)->changeForward;
1.65 +
1.66 + // Free any convar-change forwards that still exist
1.67 + if (fwd)
1.68 + {
1.69 + g_Forwards.ReleaseForward(fwd);
1.70 + }
1.71 + }
1.72 +
1.73 + // Remove the 'cvars' option from the 'sm' console command
1.74 g_RootMenu.RemoveRootConsoleCommand("cvars", this);
1.75
1.76 + // Remove the 'ConVar' handle type
1.77 g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
1.78 }
1.79
1.80 void CConVarManager::OnHandleDestroy(HandleType_t type, void *object)
1.81 {
1.82 - g_SMAPI->UnregisterConCmdBase(g_PLAPI, static_cast<ConCommandBase *>(object));
1.83 + ConVarInfo *info;
1.84 + ConCommandBase *cvar = static_cast<ConCommandBase *>(object);
1.85 +
1.86 + // Find convar in lookup trie
1.87 + sm_trie_retrieve(m_ConVarCache, cvar->GetName(), reinterpret_cast<void **>(&info));
1.88 +
1.89 + // If convar was created by SourceMod plugin...
1.90 + if (info->sourceMod)
1.91 + {
1.92 + // Then unregister it
1.93 + g_SMAPI->UnregisterConCmdBase(g_PLAPI, cvar);
1.94 + }
1.95 }
1.96
1.97 void CConVarManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
1.98 @@ -60,15 +103,20 @@
1.99 {
1.100 int id = 1;
1.101
1.102 + // Get plugin index that was passed
1.103 int num = atoi(g_RootMenu.GetArgument(2));
1.104
1.105 + // If invalid plugin index...
1.106 if (num < 1 || num > (int)g_PluginSys.GetPluginCount())
1.107 {
1.108 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
1.109 return;
1.110 }
1.111
1.112 + // Get plugin object
1.113 CPlugin *pl = g_PluginSys.GetPluginByOrder(num);
1.114 +
1.115 + // Get number of convars created by plugin
1.116 int convarnum = pl->GetConVarCount();
1.117
1.118 if (convarnum == 0)
1.119 @@ -79,6 +127,7 @@
1.120
1.121 g_RootMenu.ConsolePrint("[SM] Displaying convars for \"%s\":", pl->GetPublicInfo()->name);
1.122
1.123 + // Iterate convar list and display each one
1.124 for (int i = 0; i < convarnum; i++, id++)
1.125 {
1.126 ConVar *cvar = pl->GetConVarByIndex(i);
1.127 @@ -88,13 +137,14 @@
1.128 return;
1.129 }
1.130
1.131 + // Display usage of subcommand
1.132 g_RootMenu.ConsolePrint("[SM] Usage: sm cvars <#>");
1.133 }
1.134
1.135 Handle_t CConVarManager::CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal, const char *helpText, int flags, bool hasMin, float min, bool hasMax, float max)
1.136 {
1.137 ConVar *cvar = NULL;
1.138 - void *temp = NULL;
1.139 + ConVarInfo *info = NULL;
1.140 Handle_t hndl = 0;
1.141
1.142 // Find out if the convar exists already
1.143 @@ -103,33 +153,59 @@
1.144 // If the convar already exists...
1.145 if (cvar != NULL)
1.146 {
1.147 - IPlugin *pl = g_PluginSys.FindPluginByContext(pContext->GetContext());
1.148 -
1.149 - // This isn't a fatal error because we can handle it, but user should be warned anyways
1.150 - g_Logger.LogError("[SM] Warning: Plugin \"%s\" has attempted to create already existing convar \"%s\"", pl->GetFilename(), name);
1.151 -
1.152 // First check if we already have a handle to it
1.153 - if (sm_trie_retrieve(m_CvarCache, name, &temp))
1.154 + if (sm_trie_retrieve(m_ConVarCache, name, reinterpret_cast<void **>(&info)))
1.155 {
1.156 // If we do, then return that handle
1.157 - return reinterpret_cast<Handle_t>(temp);
1.158 + return info->handle;
1.159 } else {
1.160 - // If we don't, then create a new handle from the convar and return it
1.161 - return g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
1.162 + // If we don't, then create a new handle from the convar
1.163 + hndl = g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
1.164 +
1.165 + info = new ConVarInfo;
1.166 + info->handle = hndl;
1.167 + info->sourceMod = false;
1.168 + info->changeForward = NULL;
1.169 + info->origCallback = cvar->GetCallback();
1.170 +
1.171 + m_ConVars.push_back(info);
1.172 +
1.173 + return hndl;
1.174 }
1.175 }
1.176
1.177 - // Since we didn't find an existing convar, now we can create it
1.178 + // To prevent creating a convar that has the same name as a console command... ugh
1.179 + ConCommandBase *pBase = icvar->GetCommands();
1.180 +
1.181 + while (pBase)
1.182 + {
1.183 + if (strcmp(pBase->GetName(), name) == 0)
1.184 + {
1.185 + return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same name already exists.", name);
1.186 + }
1.187 +
1.188 + pBase = const_cast<ConCommandBase *>(pBase->GetNext());
1.189 + }
1.190 +
1.191 + // Since we didn't find an existing convar (or concmd with the same name), now we can finally create it!
1.192 cvar = new ConVar(name, defaultVal, flags, helpText, hasMin, min, hasMax, max);
1.193
1.194 // Add new convar to plugin's list
1.195 g_PluginSys.GetPluginByCtx(pContext->GetContext())->AddConVar(cvar);
1.196
1.197 - // Create a new handle from the convar
1.198 + // Create a handle from the new convar
1.199 hndl = g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
1.200
1.201 - // Insert the handle into our cache
1.202 - sm_trie_insert(m_CvarCache, name, reinterpret_cast<void *>(hndl));
1.203 + info = new ConVarInfo;
1.204 + info->handle = hndl;
1.205 + info->sourceMod = true;
1.206 + info->changeForward = NULL;
1.207 + info->origCallback = NULL;
1.208 +
1.209 + m_ConVars.push_back(info);
1.210 +
1.211 + // Insert the handle into our lookup trie
1.212 + sm_trie_insert(m_ConVarCache, name, info);
1.213
1.214 return hndl;
1.215 }
1.216 @@ -137,7 +213,8 @@
1.217 Handle_t CConVarManager::FindConVar(const char *name)
1.218 {
1.219 ConVar *cvar = NULL;
1.220 - void *temp = NULL;
1.221 + ConVarInfo *info = NULL;
1.222 + Handle_t hndl = 0;
1.223
1.224 // Search for convar
1.225 cvar = icvar->FindVar(name);
1.226 @@ -149,12 +226,139 @@
1.227 }
1.228
1.229 // At this point, convar exists, so find out if we already have a handle for it
1.230 - if (sm_trie_retrieve(m_CvarCache, name, &temp))
1.231 + if (sm_trie_retrieve(m_ConVarCache, name, reinterpret_cast<void **>(&info)))
1.232 {
1.233 // If we do, then return that handle
1.234 - return reinterpret_cast<Handle_t>(temp);
1.235 + return info->handle;
1.236 + }
1.237 +
1.238 + // If we don't, then create a new handle from the convar
1.239 + hndl = g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
1.240 +
1.241 + info = new ConVarInfo;
1.242 + info->handle = hndl;
1.243 + info->sourceMod = false;
1.244 + info->changeForward = NULL;
1.245 + info->origCallback = cvar->GetCallback();
1.246 +
1.247 + m_ConVars.push_back(info);
1.248 +
1.249 + // Insert the handle into our cache
1.250 + sm_trie_insert(m_ConVarCache, name, info);
1.251 +
1.252 + return hndl;
1.253 +}
1.254 +
1.255 +void CConVarManager::HookConVarChange(IPluginContext *pContext, ConVar *cvar, funcid_t funcid)
1.256 +{
1.257 + IPluginFunction *func = pContext->GetFunctionById(funcid);
1.258 + IChangeableForward *fwd = NULL;
1.259 + char fwdName[64];
1.260 + ConVarInfo *info = NULL;
1.261 +
1.262 + // This shouldn't happen...
1.263 + if (func == NULL)
1.264 + {
1.265 + pContext->ThrowNativeError("Invalid function: %d", funcid);
1.266 + return;
1.267 + }
1.268 +
1.269 + // Create a forward name
1.270 + UTIL_Format(fwdName, sizeof(fwdName), "ConVar.%s", cvar->GetName());
1.271 +
1.272 + // First find out if the forward already exists
1.273 + g_Forwards.FindForward(fwdName, &fwd);
1.274 +
1.275 + // If the forward doesn't exist...
1.276 + if (fwd == NULL)
1.277 + {
1.278 + // This is the forward's parameter type list
1.279 + ParamType p[] = {Param_Cell, Param_String, Param_String};
1.280 +
1.281 + // Create the forward
1.282 + fwd = g_Forwards.CreateForwardEx(fwdName, ET_Ignore, 3, p);
1.283 +
1.284 + // Find the convar in the lookup trie
1.285 + if (sm_trie_retrieve(m_ConVarCache, cvar->GetName(), reinterpret_cast<void **>(&info)))
1.286 + {
1.287 + // Set the convar's forward to the newly created one
1.288 + info->changeForward = fwd;
1.289 +
1.290 + // Set the convar's callback to our static one
1.291 + cvar->InstallChangeCallback(OnConVarChanged);
1.292 + }
1.293 }
1.294
1.295 - // If we don't, then create a new handle from the convar and return it
1.296 - return g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
1.297 + // Add the function to the forward's list
1.298 + fwd->AddFunction(func);
1.299 +}
1.300 +
1.301 +void CConVarManager::UnhookConVarChange(IPluginContext *pContext, ConVar *cvar, funcid_t funcid)
1.302 +{
1.303 + IPluginFunction *func = pContext->GetFunctionById(funcid);
1.304 + IChangeableForward *fwd = NULL;
1.305 + ConVarInfo *info = NULL;
1.306 +
1.307 + // This shouldn't happen...
1.308 + if (func == NULL)
1.309 + {
1.310 + pContext->ThrowNativeError("Invalid function: %d", funcid);
1.311 + return;
1.312 + }
1.313 +
1.314 + // Find the convar in the lookup trie
1.315 + if (sm_trie_retrieve(m_ConVarCache, cvar->GetName(), reinterpret_cast<void **>(&info)))
1.316 + {
1.317 + // Get the forward
1.318 + fwd = info->changeForward;
1.319 +
1.320 + // If the forward doesn't exist, we can't unhook anything
1.321 + if (fwd == NULL)
1.322 + {
1.323 + pContext->ThrowNativeError("Convar \"%s\" has no active hook.", cvar->GetName());
1.324 + return;
1.325 + }
1.326 +
1.327 + // Remove the function from the forward's list
1.328 + if (!fwd->RemoveFunction(func))
1.329 + {
1.330 + pContext->ThrowNativeError("Function %d is not a valid hook callback for convar \"%s\"", funcid, cvar->GetName());
1.331 + return;
1.332 + }
1.333 +
1.334 + // If the forward now has 0 functions in it...
1.335 + if (fwd->GetFunctionCount() == 0)
1.336 + {
1.337 + // Free this forward
1.338 + g_Forwards.ReleaseForward(fwd);
1.339 + info->changeForward = NULL;
1.340 +
1.341 + // Put the back the original convar callback
1.342 + cvar->InstallChangeCallback(info->origCallback);
1.343 + }
1.344 + }
1.345 +}
1.346 +
1.347 +void CConVarManager::OnConVarChanged(ConVar *cvar, const char *oldValue)
1.348 +{
1.349 + Trie *cache = g_ConVarManager.GetConVarCache();
1.350 + ConVarInfo *info;
1.351 +
1.352 + // Find the convar in the lookup trie
1.353 + sm_trie_retrieve(cache, cvar->GetName(), reinterpret_cast<void **>(&info));
1.354 +
1.355 + FnChangeCallback origCallback = info->origCallback;
1.356 + IChangeableForward *fwd = info->changeForward;
1.357 +
1.358 + // If there was a change callback installed previously, call it
1.359 + if (origCallback)
1.360 + {
1.361 + origCallback(cvar, oldValue);
1.362 + }
1.363 +
1.364 + // Now call forwards in plugins that have hooked this
1.365 + fwd->PushCell(info->handle);
1.366 + fwd->PushString(cvar->GetString());
1.367 + fwd->PushString(oldValue);
1.368 + fwd->Execute(NULL);
1.369 }
1.370 \ No newline at end of file