core/CConVarManager.cpp
author David Anderson <dvander@alliedmods.net>
Mon Feb 26 09:21:08 2007 +0000 (2007-02-26)
changeset 539 fa7e8a232a18
parent 511 c29f8796bcf4
child 576 50696154c0b8
permissions -rw-r--r--
fixed stupid export
     1 /**
     2 * ===============================================================
     3 * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4 * ===============================================================
     5 *
     6 * This file is not open source and may not be copied without explicit
     7 * written permission of AlliedModders LLC.  This file may not be redistributed 
     8 * in whole or significant part.
     9 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    10 *
    11 * Version: $Id$
    12 */
    13 
    14 #include "CConVarManager.h"
    15 #include "PluginSys.h"
    16 #include "ForwardSys.h"
    17 #include "HandleSys.h"
    18 #include "sm_srvcmds.h"
    19 #include "sm_stringutil.h"
    20 #include <sh_vector.h>
    21 
    22 CConVarManager g_ConVarManager;
    23 
    24 CConVarManager::CConVarManager() : m_ConVarType(0)
    25 {
    26 	// Create a convar lookup trie
    27 	m_ConVarCache = sm_trie_create();
    28 }
    29 
    30 CConVarManager::~CConVarManager()
    31 {
    32 	List<ConVarInfo *>::iterator i;
    33 
    34 	// Destroy our convar lookup trie
    35 	sm_trie_destroy(m_ConVarCache);
    36 
    37 	// Destroy all the ConVarInfo structures
    38 	for (i = m_ConVars.begin(); i != m_ConVars.end(); i++)
    39 	{
    40 		delete (*i);
    41 	}
    42 
    43 	m_ConVars.clear();
    44 }
    45 
    46 void CConVarManager::OnSourceModAllInitialized()
    47 {
    48 	HandleAccess sec;
    49 
    50 	// Set up access rights for the 'ConVar' handle type
    51 	sec.access[HandleAccess_Read] = 0;
    52 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
    53 	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
    54 
    55 	// Create the 'ConVar' handle type
    56 	m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    57 
    58 	// Add the 'cvars' option to the 'sm' console command
    59 	g_RootMenu.AddRootConsoleCommand("convars", "View convars created by a plugin", this);
    60 }
    61 
    62 void CConVarManager::OnSourceModShutdown()
    63 {
    64 	IChangeableForward *fwd;
    65 	List<ConVarInfo *>::iterator i;
    66 
    67 	// Iterate list of ConVarInfo structures
    68 	for (i = m_ConVars.begin(); i != m_ConVars.end(); i++)
    69 	{
    70 		fwd = (*i)->changeForward;
    71 
    72 		// Free any convar-change forwards that still exist
    73 		if (fwd)
    74 		{
    75 			g_Forwards.ReleaseForward(fwd);
    76 		}
    77 	}
    78 
    79 	// Remove the 'cvars' option from the 'sm' console command
    80 	g_RootMenu.RemoveRootConsoleCommand("convars", this);
    81 
    82 	// Remove the 'ConVar' handle type
    83 	g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
    84 }
    85 
    86 void CConVarManager::OnPluginDestroyed(IPlugin *plugin)
    87 {
    88 	CVector<ConVar *> *cvarList;
    89 
    90 	// If plugin has a convar list, free its memory
    91 	if (plugin->GetProperty("ConVar", reinterpret_cast<void **>(&cvarList), true))
    92 	{
    93 		delete cvarList;
    94 	}
    95 }
    96 
    97 void CConVarManager::OnHandleDestroy(HandleType_t type, void *object)
    98 {
    99 	ConVarInfo *info;
   100 	ConVar *cvar = static_cast<ConVar *>(object);
   101 
   102 	// Find convar in lookup trie
   103 	sm_trie_retrieve(m_ConVarCache, cvar->GetName(), reinterpret_cast<void **>(&info));
   104 
   105 	// If convar was created by SourceMod plugin...
   106 	if (info->sourceMod)
   107 	{
   108 		// Delete string allocations
   109 		delete [] cvar->GetName(); 
   110 		delete [] cvar->GetDefault();
   111 		delete [] cvar->GetHelpText();
   112 
   113 		// Then unregister it
   114 		g_SMAPI->UnregisterConCmdBase(g_PLAPI, cvar);
   115 	}
   116 }
   117 
   118 void CConVarManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
   119 {
   120 	if (argcount >= 3)
   121 	{
   122 		int id = 1;
   123 
   124 		// Get plugin index that was passed
   125 		int num = atoi(g_RootMenu.GetArgument(2));
   126 		
   127 		// If invalid plugin index...
   128 		if (num < 1 || num > (int)g_PluginSys.GetPluginCount())
   129 		{
   130 			g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
   131 			return;
   132 		}
   133 
   134 		// Get plugin object
   135 		CPlugin *pl = g_PluginSys.GetPluginByOrder(num);
   136 
   137 		CVector<ConVar *> *cvarList = NULL;
   138 
   139 		// Get convar list from 'ConVar' property
   140 		pl->GetProperty("ConVar", reinterpret_cast<void **>(&cvarList));
   141 
   142 		// If no cvar list...
   143 		if (cvarList == NULL)
   144 		{
   145 			g_RootMenu.ConsolePrint("[SM] No convars for \"%s\"", pl->GetPublicInfo()->name);
   146 			return;
   147 		}
   148 
   149 		g_RootMenu.ConsolePrint("[SM] Displaying convars for \"%s\"", pl->GetPublicInfo()->name);
   150 
   151 		// Iterate convar list and display each one
   152 		for (size_t i = 0; i < cvarList->size(); i++, id++)
   153 		{
   154 			ConVar *cvar = (*cvarList)[i];
   155 			g_RootMenu.ConsolePrint("  %02d \"%s\" = \"%s\"", id, cvar->GetName(), cvar->GetString()); 
   156 		}
   157 
   158 		return;
   159 	}
   160 
   161 	// Display usage of subcommand
   162 	g_RootMenu.ConsolePrint("[SM] Usage: sm convars <plugin #>");
   163 }
   164 
   165 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)
   166 {
   167 	ConVar *cvar = NULL;
   168 	ConVarInfo *info = NULL;
   169 	CVector<ConVar *> *cvarList = NULL;
   170 	Handle_t hndl = 0;
   171 
   172 	// Find out if the convar exists already
   173 	cvar = icvar->FindVar(name);
   174 
   175 	// If the convar already exists...
   176 	if (cvar != NULL)
   177 	{
   178 		// First check if we already have a handle to it
   179 		if (sm_trie_retrieve(m_ConVarCache, name, reinterpret_cast<void **>(&info)))
   180 		{
   181 			// If we do, then return that handle
   182 			return info->handle;
   183 		} else {
   184 			// If we don't, then create a new handle from the convar
   185 			hndl = g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
   186 
   187 			info = new ConVarInfo;
   188 			info->handle = hndl;
   189 			info->sourceMod = false;
   190 			info->changeForward = NULL;
   191 			info->origCallback = cvar->GetCallback();
   192 
   193 			m_ConVars.push_back(info);
   194 
   195 			return hndl;
   196 		}
   197 	}
   198 
   199 	// To prevent creating a convar that has the same name as a console command... ugh
   200 	ConCommandBase *pBase = icvar->GetCommands();
   201 
   202 	while (pBase)
   203 	{
   204 		if (strcmp(pBase->GetName(), name) == 0)
   205 		{
   206 			return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same name already exists.", name);
   207 		}
   208 
   209 		pBase = const_cast<ConCommandBase *>(pBase->GetNext());
   210 	}
   211 
   212 	// Since we didn't find an existing convar (or concmd with the same name), now we can finally create it!
   213 	cvar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(helpText), hasMin, min, hasMax, max);
   214 
   215 	// Find plugin creating convar
   216 	IPlugin *pl = g_PluginSys.FindPluginByContext(pContext->GetContext());
   217 
   218 	// Get convar list from 'ConVar' property of plugin
   219 	pl->GetProperty("ConVar", reinterpret_cast<void **>(&cvarList));
   220 
   221 	// If 'ConVar' property doesn't exist...
   222 	if (cvarList == NULL)
   223 	{
   224 		// Then create it
   225 		cvarList = new CVector<ConVar *>;
   226 		pl->SetProperty("ConVar", cvarList);
   227 	}
   228 
   229 	// Add new convar to plugin's list
   230 	cvarList->push_back(cvar);
   231 
   232 	// Create a handle from the new convar
   233 	hndl = g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
   234 
   235 	info = new ConVarInfo;
   236 	info->handle = hndl;
   237 	info->sourceMod = true;
   238 	info->changeForward = NULL;
   239 	info->origCallback = NULL;
   240 
   241 	m_ConVars.push_back(info);
   242 
   243 	// Insert the handle into our lookup trie
   244 	sm_trie_insert(m_ConVarCache, name, info);
   245 
   246 	return hndl;
   247 }
   248 
   249 Handle_t CConVarManager::FindConVar(const char *name)
   250 {
   251 	ConVar *cvar = NULL;
   252 	ConVarInfo *info = NULL;
   253 	Handle_t hndl = 0;
   254 
   255 	// Search for convar
   256 	cvar = icvar->FindVar(name);
   257 
   258 	// If it doesn't exist, then return an invalid handle
   259 	if (cvar == NULL)
   260 	{
   261 		return BAD_HANDLE;
   262 	}
   263 
   264 	// At this point, convar exists, so find out if we already have a handle for it
   265 	if (sm_trie_retrieve(m_ConVarCache, name, reinterpret_cast<void **>(&info)))
   266 	{
   267 		// If we do, then return that handle
   268 		return info->handle;
   269 	}
   270 
   271 	// If we don't, then create a new handle from the convar
   272 	hndl = g_HandleSys.CreateHandle(m_ConVarType, cvar, NULL, g_pCoreIdent, NULL);
   273 
   274 	info = new ConVarInfo;
   275 	info->handle = hndl;
   276 	info->sourceMod = false;
   277 	info->changeForward = NULL;
   278 	info->origCallback = cvar->GetCallback();
   279 
   280 	m_ConVars.push_back(info);
   281 
   282 	// Insert the handle into our cache
   283 	sm_trie_insert(m_ConVarCache, name, info);
   284 
   285 	return hndl;
   286 }
   287 
   288 void CConVarManager::HookConVarChange(IPluginContext *pContext, ConVar *cvar, funcid_t funcid)
   289 {
   290 	IPluginFunction *func = pContext->GetFunctionById(funcid);
   291 	IChangeableForward *fwd = NULL;
   292 	char fwdName[64];
   293 	ConVarInfo *info = NULL;
   294 
   295 	// This shouldn't happen...
   296 	if (func == NULL)
   297 	{
   298 		pContext->ThrowNativeError("Invalid function specified");
   299 		return;
   300 	}
   301 
   302 	// Create a forward name
   303 	UTIL_Format(fwdName, sizeof(fwdName), "ConVar.%s", cvar->GetName());
   304 
   305 	// First find out if the forward already exists
   306 	g_Forwards.FindForward(fwdName, &fwd);
   307 
   308 	// If the forward doesn't exist...
   309 	if (fwd == NULL)
   310 	{
   311 		// This is the forward's parameter type list
   312 		ParamType p[] = {Param_Cell, Param_String, Param_String};
   313 
   314 		// Create the forward
   315 		fwd = g_Forwards.CreateForwardEx(fwdName, ET_Ignore, 3, p);
   316 
   317 		// Find the convar in the lookup trie
   318 		if (sm_trie_retrieve(m_ConVarCache, cvar->GetName(), reinterpret_cast<void **>(&info)))
   319 		{
   320 			// Set the convar's forward to the newly created one
   321 			info->changeForward = fwd;
   322 
   323 			// Set the convar's callback to our static one
   324 			cvar->InstallChangeCallback(OnConVarChanged);
   325 		}
   326 	}
   327 
   328 	// Add the function to the forward's list
   329 	fwd->AddFunction(func);
   330 }
   331 
   332 void CConVarManager::UnhookConVarChange(IPluginContext *pContext, ConVar *cvar, funcid_t funcid)
   333 {
   334 	IPluginFunction *func = pContext->GetFunctionById(funcid);
   335 	IChangeableForward *fwd = NULL;
   336 	ConVarInfo *info = NULL;
   337 
   338 	// This shouldn't happen...
   339 	if (func == NULL)
   340 	{
   341 		pContext->ThrowNativeError("Invalid function specified");
   342 		return;
   343 	}
   344 
   345 	// Find the convar in the lookup trie
   346 	if (sm_trie_retrieve(m_ConVarCache, cvar->GetName(), reinterpret_cast<void **>(&info)))
   347 	{
   348 		// Get the forward
   349 		fwd = info->changeForward;
   350 
   351 		// If the forward doesn't exist, we can't unhook anything
   352 		if (fwd == NULL)
   353 		{
   354 			pContext->ThrowNativeError("Convar \"%s\" has no active hook", cvar->GetName());
   355 			return;
   356 		}
   357 
   358 		// Remove the function from the forward's list
   359 		if (!fwd->RemoveFunction(func))
   360 		{
   361 			pContext->ThrowNativeError("Invalid hook callback specified for convar \"%s\"", cvar->GetName());
   362 			return;
   363 		}
   364 
   365 		// If the forward now has 0 functions in it...
   366 		if (fwd->GetFunctionCount() == 0)
   367 		{
   368 			// Free this forward
   369 			g_Forwards.ReleaseForward(fwd);
   370 			info->changeForward = NULL;
   371 
   372 			// Put the back the original convar callback
   373 			cvar->InstallChangeCallback(info->origCallback);
   374 		}
   375 	}
   376 }
   377 
   378 void CConVarManager::OnConVarChanged(ConVar *cvar, const char *oldValue)
   379 {
   380 	// If the values are the same...
   381 	if (strcmp(cvar->GetString(), oldValue) == 0)
   382 	{
   383 		// Exit early in order to not trigger callbacks
   384 		return;
   385 	}
   386 
   387 	Trie *cache = g_ConVarManager.GetConVarCache();
   388 	ConVarInfo *info;
   389 
   390 	// Find the convar in the lookup trie
   391 	sm_trie_retrieve(cache, cvar->GetName(), reinterpret_cast<void **>(&info));
   392 
   393 	FnChangeCallback origCallback = info->origCallback;
   394 	IChangeableForward *fwd = info->changeForward;
   395 
   396 	// If there was a change callback installed previously, call it
   397 	if (origCallback)
   398 	{
   399 		origCallback(cvar, oldValue);
   400 	}
   401 
   402 	// Now call forwards in plugins that have hooked this
   403 	fwd->PushCell(info->handle);
   404 	fwd->PushString(cvar->GetString());
   405 	fwd->PushString(oldValue);
   406 	fwd->Execute(NULL);
   407 }