core/ConVarManager.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jul 11 06:51:21 2008 +0000 (2008-07-11)
branchrefac-jit
changeset 2192 7602d7fb60d9
parent 2168 c24a6fa957db
child 2289 44045ead61cd
permissions -rw-r--r--
fixed a compat regression, forgot msvc does funky things with overloaded virtual calls
dvander@2168
     1
/**
dvander@2168
     2
 * vim: set ts=4 :
dvander@2168
     3
 * =============================================================================
dvander@2168
     4
 * SourceMod
dvander@2168
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@2168
     6
 * =============================================================================
dvander@2168
     7
 *
dvander@2168
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@2168
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@2168
    10
 * Free Software Foundation.
dvander@2168
    11
 * 
dvander@2168
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2168
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2168
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@2168
    15
 * details.
dvander@2168
    16
 *
dvander@2168
    17
 * You should have received a copy of the GNU General Public License along with
dvander@2168
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@2168
    19
 *
dvander@2168
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@2168
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2168
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2168
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@2168
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@2168
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@2168
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2168
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@2168
    28
 *
dvander@2168
    29
 * Version: $Id$
dvander@2168
    30
 */
dvander@2168
    31
dvander@2168
    32
#include "ConVarManager.h"
dvander@2168
    33
#include "HalfLife2.h"
dvander@2168
    34
#include "PluginSys.h"
dvander@2168
    35
#include "ForwardSys.h"
dvander@2168
    36
#include "HandleSys.h"
dvander@2168
    37
#include "sm_srvcmds.h"
dvander@2168
    38
#include "sm_stringutil.h"
dvander@2168
    39
#include <sh_vector.h>
dvander@2168
    40
#include <sm_trie_tpl.h>
dvander@2168
    41
dvander@2168
    42
ConVarManager g_ConVarManager;
dvander@2168
    43
dvander@2168
    44
#if !defined ORANGEBOX_BUILD
dvander@2168
    45
#define CallGlobalChangeCallbacks	CallGlobalChangeCallback
dvander@2168
    46
#endif
dvander@2168
    47
dvander@2168
    48
#if defined ORANGEBOX_BUILD
dvander@2168
    49
SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *, float);
dvander@2168
    50
#else
dvander@2168
    51
SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *);
dvander@2168
    52
#endif
dvander@2168
    53
dvander@2168
    54
SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
dvander@2168
    55
SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
dvander@2168
    56
dvander@2168
    57
const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
dvander@2168
    58
typedef List<const ConVar *> ConVarList;
dvander@2168
    59
KTrie<ConVarInfo *> convar_cache;
dvander@2168
    60
dvander@2168
    61
ConVarManager::ConVarManager() : m_ConVarType(0), m_bIsDLLQueryHooked(false), m_bIsVSPQueryHooked(false)
dvander@2168
    62
{
dvander@2168
    63
}
dvander@2168
    64
dvander@2168
    65
ConVarManager::~ConVarManager()
dvander@2168
    66
{
dvander@2168
    67
}
dvander@2168
    68
dvander@2168
    69
void ConVarManager::OnSourceModStartup(bool late)
dvander@2168
    70
{
dvander@2168
    71
	HandleAccess sec;
dvander@2168
    72
dvander@2168
    73
	/* Set up access rights for the 'ConVar' handle type */
dvander@2168
    74
	sec.access[HandleAccess_Read] = 0;
dvander@2168
    75
	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
dvander@2168
    76
	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
dvander@2168
    77
dvander@2168
    78
	/* Create the 'ConVar' handle type */
dvander@2168
    79
	m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
dvander@2168
    80
}
dvander@2168
    81
dvander@2168
    82
void ConVarManager::OnSourceModAllInitialized()
dvander@2168
    83
{
dvander@2168
    84
	/**
dvander@2168
    85
	 * Episode 2 has this function by default, but the older versions do not.
dvander@2168
    86
	 */
dvander@2168
    87
#if !defined ORANGEBOX_BUILD
dvander@2168
    88
	if (g_SMAPI->GetGameDLLVersion() >= 6)
dvander@2168
    89
	{
dvander@2168
    90
		SH_ADD_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
dvander@2168
    91
		m_bIsDLLQueryHooked = true;
dvander@2168
    92
	}
dvander@2168
    93
#endif
dvander@2168
    94
dvander@2168
    95
	SH_ADD_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
dvander@2168
    96
dvander@2168
    97
	/* Add the 'convars' option to the 'sm' console command */
dvander@2168
    98
	g_RootMenu.AddRootConsoleCommand("cvars", "View convars created by a plugin", this);
dvander@2168
    99
}
dvander@2168
   100
dvander@2168
   101
void ConVarManager::OnSourceModShutdown()
dvander@2168
   102
{
dvander@2168
   103
	List<ConVarInfo *>::iterator iter = m_ConVars.begin();
dvander@2168
   104
	HandleSecurity sec(NULL, g_pCoreIdent);
dvander@2168
   105
dvander@2168
   106
	/* Iterate list of ConVarInfo structures, remove every one of them */
dvander@2168
   107
	while (iter != m_ConVars.end())
dvander@2168
   108
	{
dvander@2168
   109
		ConVarInfo *pInfo = (*iter);
dvander@2168
   110
dvander@2168
   111
		iter = m_ConVars.erase(iter);
dvander@2168
   112
dvander@2168
   113
		g_HandleSys.FreeHandle(pInfo->handle, &sec);
dvander@2168
   114
		if (pInfo->pChangeForward != NULL)
dvander@2168
   115
		{
dvander@2168
   116
			g_Forwards.ReleaseForward(pInfo->pChangeForward);
dvander@2168
   117
		}
dvander@2168
   118
		if (pInfo->sourceMod)
dvander@2168
   119
		{
dvander@2168
   120
			/* If we created it, we won't be tracking it, therefore it is 
dvander@2168
   121
			 * safe to remove everything in one go.
dvander@2168
   122
			 */
dvander@2168
   123
			META_UNREGCVAR(pInfo->pVar);
dvander@2168
   124
			delete [] pInfo->pVar->GetName();
dvander@2168
   125
			delete [] pInfo->pVar->GetHelpText();
dvander@2168
   126
			delete [] pInfo->pVar->GetDefault();
dvander@2168
   127
			delete pInfo->pVar;
dvander@2168
   128
		}
dvander@2168
   129
		else
dvander@2168
   130
		{
dvander@2168
   131
			/* If we didn't create it, we might be tracking it.  Also, 
dvander@2168
   132
			 * it could be unreadable.
dvander@2168
   133
			 */
dvander@2168
   134
			UntrackConCommandBase(pInfo->pVar, this);
dvander@2168
   135
		}
dvander@2168
   136
dvander@2168
   137
		/* It's not safe to read the name here, so we simply delete the 
dvander@2168
   138
		 * the info struct and clear the lookup cache at the end.
dvander@2168
   139
		 */
dvander@2168
   140
		delete pInfo;
dvander@2168
   141
	}
dvander@2168
   142
	convar_cache.clear();
dvander@2168
   143
dvander@2168
   144
	/* Unhook things */
dvander@2168
   145
	if (m_bIsDLLQueryHooked)
dvander@2168
   146
	{
dvander@2168
   147
		SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
dvander@2168
   148
		m_bIsDLLQueryHooked = false;
dvander@2168
   149
	}
dvander@2168
   150
	else if (m_bIsVSPQueryHooked)
dvander@2168
   151
	{
dvander@2168
   152
		SH_REMOVE_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
dvander@2168
   153
		m_bIsVSPQueryHooked = false;
dvander@2168
   154
	}
dvander@2168
   155
dvander@2168
   156
	SH_REMOVE_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
dvander@2168
   157
dvander@2168
   158
	/* Remove the 'convars' option from the 'sm' console command */
dvander@2168
   159
	g_RootMenu.RemoveRootConsoleCommand("cvars", this);
dvander@2168
   160
dvander@2168
   161
	/* Remove the 'ConVar' handle type */
dvander@2168
   162
	g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
dvander@2168
   163
}
dvander@2168
   164
dvander@2168
   165
/**
dvander@2168
   166
 * Orange Box will never use this.
dvander@2168
   167
 */
dvander@2168
   168
void ConVarManager::OnSourceModVSPReceived()
dvander@2168
   169
{
dvander@2168
   170
	/**
dvander@2168
   171
	 * Don't bother if the DLL is already hooked.
dvander@2168
   172
	 */
dvander@2168
   173
	if (m_bIsDLLQueryHooked)
dvander@2168
   174
	{
dvander@2168
   175
		return;
dvander@2168
   176
	}
dvander@2168
   177
dvander@2168
   178
	/* For later MM:S versions, use the updated API, since it's cleaner. */
dvander@2168
   179
#if defined METAMOD_PLAPI_VERSION
dvander@2168
   180
	int engine = g_SMAPI->GetSourceEngineBuild();
dvander@2168
   181
	if (engine == SOURCE_ENGINE_ORIGINAL || vsp_version < 2)
dvander@2168
   182
	{
dvander@2168
   183
		return;
dvander@2168
   184
	}
dvander@2168
   185
#else
dvander@2168
   186
	if (g_HL2.IsOriginalEngine() || vsp_version < 2)
dvander@2168
   187
	{
dvander@2168
   188
		return;
dvander@2168
   189
	}
dvander@2168
   190
#endif
dvander@2168
   191
dvander@2168
   192
	SH_ADD_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
dvander@2168
   193
	m_bIsVSPQueryHooked = true;
dvander@2168
   194
}
dvander@2168
   195
dvander@2168
   196
bool convar_cache_lookup(const char *name, ConVarInfo **pVar)
dvander@2168
   197
{
dvander@2168
   198
	ConVarInfo **pLookup = convar_cache.retrieve(name);
dvander@2168
   199
	if (pLookup != NULL)
dvander@2168
   200
	{
dvander@2168
   201
		*pVar = *pLookup;
dvander@2168
   202
		return true;
dvander@2168
   203
	}
dvander@2168
   204
	else
dvander@2168
   205
	{
dvander@2168
   206
		return false;
dvander@2168
   207
	}
dvander@2168
   208
}
dvander@2168
   209
dvander@2168
   210
void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
dvander@2168
   211
{
dvander@2168
   212
	/* Only check convars that have not been created by SourceMod's core */
dvander@2168
   213
	ConVarInfo *pInfo;
dvander@2168
   214
	if (!convar_cache_lookup(name, &pInfo))
dvander@2168
   215
	{
dvander@2168
   216
		return;
dvander@2168
   217
	}
dvander@2168
   218
dvander@2168
   219
	HandleSecurity sec(NULL, g_pCoreIdent);
dvander@2168
   220
dvander@2168
   221
	/* Remove it from our cache */
dvander@2168
   222
	m_ConVars.remove(pInfo);
dvander@2168
   223
	convar_cache.remove(name);
dvander@2168
   224
dvander@2168
   225
	/* Now make sure no plugins are referring to this pointer */
dvander@2168
   226
	IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
dvander@2168
   227
	while (pl_iter->MorePlugins())
dvander@2168
   228
	{
dvander@2168
   229
		IPlugin *pl = pl_iter->GetPlugin();
dvander@2168
   230
dvander@2168
   231
		ConVarList *pConVarList;
dvander@2168
   232
		if (pl->GetProperty("ConVarList", (void **)&pConVarList, true) 
dvander@2168
   233
			&& pConVarList != NULL)
dvander@2168
   234
		{
dvander@2168
   235
			pConVarList->remove(pInfo->pVar);
dvander@2168
   236
		}
dvander@2168
   237
dvander@2168
   238
		pl_iter->NextPlugin();
dvander@2168
   239
	}
dvander@2168
   240
dvander@2168
   241
	/* Free resources */
dvander@2168
   242
	g_HandleSys.FreeHandle(pInfo->handle, &sec);
dvander@2168
   243
	delete pInfo;
dvander@2168
   244
}
dvander@2168
   245
dvander@2168
   246
void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
dvander@2168
   247
{
dvander@2168
   248
	ConVarList *pConVarList;
dvander@2168
   249
dvander@2168
   250
	/* If plugin has a convar list, free its memory */
dvander@2168
   251
	if (plugin->GetProperty("ConVarList", (void **)&pConVarList, true))
dvander@2168
   252
	{
dvander@2168
   253
		delete pConVarList;
dvander@2168
   254
	}
dvander@2168
   255
}
dvander@2168
   256
dvander@2168
   257
void ConVarManager::OnHandleDestroy(HandleType_t type, void *object)
dvander@2168
   258
{
dvander@2168
   259
}
dvander@2168
   260
dvander@2168
   261
bool ConVarManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
dvander@2168
   262
{
dvander@2168
   263
	*pSize = sizeof(ConVar) + sizeof(ConVarInfo);
dvander@2168
   264
	return true;
dvander@2168
   265
}
dvander@2168
   266
dvander@2168
   267
void ConVarManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
dvander@2168
   268
{
dvander@2168
   269
	int argcount = command.ArgC();
dvander@2168
   270
	if (argcount >= 3)
dvander@2168
   271
	{
dvander@2168
   272
		/* Get plugin index that was passed */
dvander@2168
   273
		const char *arg = command.Arg(2);
dvander@2168
   274
		
dvander@2168
   275
		/* Get plugin object */
dvander@2168
   276
		CPlugin *plugin = g_PluginSys.FindPluginByConsoleArg(arg);
dvander@2168
   277
dvander@2168
   278
		if (!plugin)
dvander@2168
   279
		{
dvander@2168
   280
			g_RootMenu.ConsolePrint("[SM] Plugin \"%s\" was not found.", arg);
dvander@2168
   281
			return;
dvander@2168
   282
		}
dvander@2168
   283
dvander@2168
   284
		/* Get plugin name */
dvander@2168
   285
		const sm_plugininfo_t *plinfo = plugin->GetPublicInfo();
dvander@2168
   286
		const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : plugin->GetFilename();
dvander@2168
   287
dvander@2168
   288
		ConVarList *pConVarList;
dvander@2168
   289
		ConVarList::iterator iter;
dvander@2168
   290
dvander@2168
   291
		/* If no convar list... */
dvander@2168
   292
		if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
dvander@2168
   293
		{
dvander@2168
   294
			g_RootMenu.ConsolePrint("[SM] No convars found for: %s", plname);
dvander@2168
   295
			return;
dvander@2168
   296
		}
dvander@2168
   297
dvander@2168
   298
		g_RootMenu.ConsolePrint("[SM] Listing %d convars for: %s", pConVarList->size(), plname);
dvander@2168
   299
		g_RootMenu.ConsolePrint("  %-32.31s %s", "[Name]", "[Value]");
dvander@2168
   300
dvander@2168
   301
		/* Iterate convar list and display each one */
dvander@2168
   302
		for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
dvander@2168
   303
		{
dvander@2168
   304
			const ConVar *pConVar = (*iter);
dvander@2168
   305
			g_RootMenu.ConsolePrint("  %-32.31s %s", pConVar->GetName(), pConVar->GetString()); 
dvander@2168
   306
		}
dvander@2168
   307
dvander@2168
   308
		return;
dvander@2168
   309
	}
dvander@2168
   310
dvander@2168
   311
	/* Display usage of subcommand */
dvander@2168
   312
	g_RootMenu.ConsolePrint("[SM] Usage: sm convars <plugin #>");
dvander@2168
   313
}
dvander@2168
   314
dvander@2168
   315
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)
dvander@2168
   316
{
dvander@2168
   317
	ConVar *pConVar = NULL;
dvander@2168
   318
	ConVarInfo *pInfo = NULL;
dvander@2168
   319
	Handle_t hndl = 0;
dvander@2168
   320
dvander@2168
   321
	/* Find out if the convar exists already */
dvander@2168
   322
	pConVar = icvar->FindVar(name);
dvander@2168
   323
dvander@2168
   324
	/* If the convar already exists... */
dvander@2168
   325
	if (pConVar)
dvander@2168
   326
	{
dvander@2168
   327
		/* Add convar to plugin's list */
dvander@2168
   328
		AddConVarToPluginList(pContext, pConVar);
dvander@2168
   329
dvander@2168
   330
		/* First find out if we already have a handle to it */
dvander@2168
   331
		if (convar_cache_lookup(name, &pInfo))
dvander@2168
   332
		{
dvander@2168
   333
			return pInfo->handle;
dvander@2168
   334
		}
dvander@2168
   335
		else
dvander@2168
   336
		{
dvander@2168
   337
			/* Create and initialize ConVarInfo structure */
dvander@2168
   338
			pInfo = new ConVarInfo();
dvander@2168
   339
			pInfo->sourceMod = false;
dvander@2168
   340
			pInfo->pChangeForward = NULL;
dvander@2168
   341
			pInfo->pVar = pConVar;
dvander@2168
   342
dvander@2168
   343
			/* If we don't, then create a new handle from the convar */
dvander@2168
   344
			hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
dvander@2168
   345
			if (hndl == BAD_HANDLE)
dvander@2168
   346
			{
dvander@2168
   347
				delete pInfo;
dvander@2168
   348
				return BAD_HANDLE;
dvander@2168
   349
			}
dvander@2168
   350
dvander@2168
   351
			pInfo->handle = hndl;
dvander@2168
   352
dvander@2168
   353
			/* Insert struct into caches */
dvander@2168
   354
			m_ConVars.push_back(pInfo);
dvander@2168
   355
			convar_cache.insert(name, pInfo);
dvander@2168
   356
			TrackConCommandBase(pConVar, this);
dvander@2168
   357
dvander@2168
   358
			return hndl;
dvander@2168
   359
		}
dvander@2168
   360
	}
dvander@2168
   361
dvander@2168
   362
	/* To prevent creating a convar that has the same name as a console command... ugh */
dvander@2168
   363
	ConCommandBase *pBase = icvar->GetCommands();
dvander@2168
   364
dvander@2168
   365
	while (pBase)
dvander@2168
   366
	{
dvander@2168
   367
		if (pBase->IsCommand() && strcmp(pBase->GetName(), name) == 0)
dvander@2168
   368
		{
dvander@2168
   369
			return BAD_HANDLE;
dvander@2168
   370
		}
dvander@2168
   371
dvander@2168
   372
		pBase = const_cast<ConCommandBase *>(pBase->GetNext());
dvander@2168
   373
	}
dvander@2168
   374
dvander@2168
   375
	/* Create and initialize ConVarInfo structure */
dvander@2168
   376
	pInfo = new ConVarInfo();
dvander@2168
   377
	pInfo->handle = hndl;
dvander@2168
   378
	pInfo->sourceMod = true;
dvander@2168
   379
	pInfo->pChangeForward = NULL;
dvander@2168
   380
dvander@2168
   381
	/* Create a handle from the new convar */
dvander@2168
   382
	hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
dvander@2168
   383
	if (hndl == BAD_HANDLE)
dvander@2168
   384
	{
dvander@2168
   385
		delete pInfo;
dvander@2168
   386
		return BAD_HANDLE;
dvander@2168
   387
	}
dvander@2168
   388
dvander@2168
   389
	pInfo->handle = hndl;
dvander@2168
   390
dvander@2168
   391
	/* Since an existing convar (or concmd with the same name) was not found , now we can finally create it */
dvander@2168
   392
	pConVar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(description), hasMin, min, hasMax, max);
dvander@2168
   393
	pInfo->pVar = pConVar;
dvander@2168
   394
dvander@2168
   395
	/* Add convar to plugin's list */
dvander@2168
   396
	AddConVarToPluginList(pContext, pConVar);
dvander@2168
   397
dvander@2168
   398
	/* Insert struct into caches */
dvander@2168
   399
	m_ConVars.push_back(pInfo);
dvander@2168
   400
	convar_cache.insert(name, pInfo);
dvander@2168
   401
dvander@2168
   402
	return hndl;
dvander@2168
   403
}
dvander@2168
   404
dvander@2168
   405
Handle_t ConVarManager::FindConVar(const char *name)
dvander@2168
   406
{
dvander@2168
   407
	ConVar *pConVar = NULL;
dvander@2168
   408
	ConVarInfo *pInfo;
dvander@2168
   409
	Handle_t hndl;
dvander@2168
   410
dvander@2168
   411
	/* Search for convar */
dvander@2168
   412
	pConVar = icvar->FindVar(name);
dvander@2168
   413
dvander@2168
   414
	/* If it doesn't exist, then return an invalid handle */
dvander@2168
   415
	if (!pConVar)
dvander@2168
   416
	{
dvander@2168
   417
		return BAD_HANDLE;
dvander@2168
   418
	}
dvander@2168
   419
dvander@2168
   420
	/* At this point, the convar exists. So, find out if we already have a handle */
dvander@2168
   421
	if (convar_cache_lookup(name, &pInfo))
dvander@2168
   422
	{
dvander@2168
   423
		return pInfo->handle;
dvander@2168
   424
	}
dvander@2168
   425
dvander@2168
   426
	/* Create and initialize ConVarInfo structure */
dvander@2168
   427
	pInfo = new ConVarInfo();
dvander@2168
   428
	pInfo->sourceMod = false;
dvander@2168
   429
	pInfo->pChangeForward = NULL;
dvander@2168
   430
	pInfo->pVar = pConVar;
dvander@2168
   431
dvander@2168
   432
	/* If we don't have a handle, then create a new one */
dvander@2168
   433
	hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
dvander@2168
   434
	if (hndl == BAD_HANDLE)
dvander@2168
   435
	{
dvander@2168
   436
		delete pInfo;
dvander@2168
   437
		return BAD_HANDLE;
dvander@2168
   438
	}
dvander@2168
   439
dvander@2168
   440
	pInfo->handle = hndl;
dvander@2168
   441
dvander@2168
   442
	/* Insert struct into our caches */
dvander@2168
   443
	m_ConVars.push_back(pInfo);
dvander@2168
   444
	convar_cache.insert(name, pInfo);
dvander@2168
   445
	TrackConCommandBase(pConVar, this);
dvander@2168
   446
dvander@2168
   447
	return hndl;
dvander@2168
   448
}
dvander@2168
   449
dvander@2168
   450
void ConVarManager::AddConVarChangeListener(const char *name, IConVarChangeListener *pListener)
dvander@2168
   451
{
dvander@2168
   452
	ConVarInfo *pInfo;
dvander@2168
   453
dvander@2168
   454
	if (FindConVar(name) == BAD_HANDLE)
dvander@2168
   455
	{
dvander@2168
   456
		return;
dvander@2168
   457
	}
dvander@2168
   458
dvander@2168
   459
	/* Find the convar in the lookup trie */
dvander@2168
   460
	if (convar_cache_lookup(name, &pInfo))
dvander@2168
   461
	{
dvander@2168
   462
		pInfo->changeListeners.push_back(pListener);
dvander@2168
   463
	}
dvander@2168
   464
}
dvander@2168
   465
dvander@2168
   466
void ConVarManager::RemoveConVarChangeListener(const char *name, IConVarChangeListener *pListener)
dvander@2168
   467
{
dvander@2168
   468
	ConVarInfo *pInfo;
dvander@2168
   469
dvander@2168
   470
	/* Find the convar in the lookup trie */
dvander@2168
   471
	if (convar_cache_lookup(name, &pInfo))
dvander@2168
   472
	{
dvander@2168
   473
		pInfo->changeListeners.remove(pListener);
dvander@2168
   474
	}
dvander@2168
   475
}
dvander@2168
   476
dvander@2168
   477
void ConVarManager::HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
dvander@2168
   478
{
dvander@2168
   479
	ConVarInfo *pInfo;
dvander@2168
   480
	IChangeableForward *pForward;
dvander@2168
   481
dvander@2168
   482
	/* Find the convar in the lookup trie */
dvander@2168
   483
	if (convar_cache_lookup(pConVar->GetName(), &pInfo))
dvander@2168
   484
	{
dvander@2168
   485
		/* Get the forward */
dvander@2168
   486
		pForward = pInfo->pChangeForward;
dvander@2168
   487
dvander@2168
   488
		/* If forward does not exist, create it */
dvander@2168
   489
		if (!pForward)
dvander@2168
   490
		{
dvander@2168
   491
			pForward = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS);
dvander@2168
   492
			pInfo->pChangeForward = pForward;
dvander@2168
   493
		}
dvander@2168
   494
dvander@2168
   495
		/* Add function to forward's list */
dvander@2168
   496
		pForward->AddFunction(pFunction);
dvander@2168
   497
	}
dvander@2168
   498
}
dvander@2168
   499
dvander@2168
   500
void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
dvander@2168
   501
{
dvander@2168
   502
	ConVarInfo *pInfo;
dvander@2168
   503
	IChangeableForward *pForward;
dvander@2168
   504
	IPluginContext *pContext = pFunction->GetParentContext();
dvander@2168
   505
dvander@2168
   506
	/* Find the convar in the lookup trie */
dvander@2168
   507
	if (convar_cache_lookup(pConVar->GetName(), &pInfo))
dvander@2168
   508
	{
dvander@2168
   509
		/* Get the forward */
dvander@2168
   510
		pForward = pInfo->pChangeForward;
dvander@2168
   511
dvander@2168
   512
		/* If the forward doesn't exist, we can't unhook anything */
dvander@2168
   513
		if (!pForward)
dvander@2168
   514
		{
dvander@2168
   515
			pContext->ThrowNativeError("Convar \"%s\" has no active hook", pConVar->GetName());
dvander@2168
   516
			return;
dvander@2168
   517
		}
dvander@2168
   518
dvander@2168
   519
		/* Remove the function from the forward's list */
dvander@2168
   520
		if (!pForward->RemoveFunction(pFunction))
dvander@2168
   521
		{
dvander@2168
   522
			pContext->ThrowNativeError("Invalid hook callback specified for convar \"%s\"", pConVar->GetName());
dvander@2168
   523
			return;
dvander@2168
   524
		}
dvander@2168
   525
dvander@2168
   526
		/* If the forward now has 0 functions in it... */
dvander@2168
   527
		if (pForward->GetFunctionCount() == 0)
dvander@2168
   528
		{
dvander@2168
   529
			/* Free this forward */
dvander@2168
   530
			g_Forwards.ReleaseForward(pForward);
dvander@2168
   531
			pInfo->pChangeForward = NULL;
dvander@2168
   532
		}
dvander@2168
   533
	}
dvander@2168
   534
}
dvander@2168
   535
dvander@2168
   536
QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl)
dvander@2168
   537
{
dvander@2168
   538
	QueryCvarCookie_t cookie;
dvander@2168
   539
dvander@2168
   540
	/* Call StartQueryCvarValue() in either the IVEngineServer or IServerPluginHelpers depending on situation */
dvander@2168
   541
	if (m_bIsDLLQueryHooked)
dvander@2168
   542
	{
dvander@2168
   543
		cookie = engine->StartQueryCvarValue(pPlayer, name);	
dvander@2168
   544
	}
dvander@2168
   545
	else if (m_bIsVSPQueryHooked)
dvander@2168
   546
	{
dvander@2168
   547
		cookie = serverpluginhelpers->StartQueryCvarValue(pPlayer, name);
dvander@2168
   548
	}
dvander@2168
   549
	else
dvander@2168
   550
	{
dvander@2168
   551
		return InvalidQueryCvarCookie;
dvander@2168
   552
	}
dvander@2168
   553
dvander@2168
   554
	ConVarQuery query = {cookie, pCallback, hndl};
dvander@2168
   555
	m_ConVarQueries.push_back(query);
dvander@2168
   556
dvander@2168
   557
	return cookie;
dvander@2168
   558
}
dvander@2168
   559
dvander@2168
   560
void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
dvander@2168
   561
{
dvander@2168
   562
	ConVarList *pConVarList;
dvander@2168
   563
	ConVarList::iterator iter;
dvander@2168
   564
	bool inserted = false;
dvander@2168
   565
	const char *orig = pConVar->GetName();
dvander@2168
   566
dvander@2168
   567
	IPlugin *plugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
dvander@2168
   568
dvander@2168
   569
	/* Check plugin for an existing convar list */
dvander@2168
   570
	if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
dvander@2168
   571
	{
dvander@2168
   572
		pConVarList = new ConVarList();
dvander@2168
   573
		plugin->SetProperty("ConVarList", pConVarList);
dvander@2168
   574
	}
dvander@2168
   575
	else if (pConVarList->find(pConVar) != pConVarList->end())
dvander@2168
   576
	{
dvander@2168
   577
		/* If convar is already in list, then don't add it */
dvander@2168
   578
		return;
dvander@2168
   579
	}
dvander@2168
   580
dvander@2168
   581
	/* Insert convar into list which is sorted alphabetically */
dvander@2168
   582
	for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
dvander@2168
   583
	{
dvander@2168
   584
		if (strcmp(orig, (*iter)->GetName()) < 0)
dvander@2168
   585
		{
dvander@2168
   586
			pConVarList->insert(iter, pConVar);
dvander@2168
   587
			inserted = true;
dvander@2168
   588
			break;
dvander@2168
   589
		}
dvander@2168
   590
	}
dvander@2168
   591
dvander@2168
   592
	if (!inserted)
dvander@2168
   593
	{
dvander@2168
   594
		pConVarList->push_back(pConVar);
dvander@2168
   595
	}
dvander@2168
   596
}
dvander@2168
   597
dvander@2168
   598
#if defined ORANGEBOX_BUILD
dvander@2168
   599
void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
dvander@2168
   600
#else
dvander@2168
   601
void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
dvander@2168
   602
#endif
dvander@2168
   603
{
dvander@2168
   604
	/* If the values are the same, exit early in order to not trigger callbacks */
dvander@2168
   605
	if (strcmp(pConVar->GetString(), oldValue) == 0)
dvander@2168
   606
	{
dvander@2168
   607
		return;
dvander@2168
   608
	}
dvander@2168
   609
dvander@2168
   610
	ConVarInfo *pInfo;
dvander@2168
   611
dvander@2168
   612
	/* Find the convar in the lookup trie */
dvander@2168
   613
	if (!convar_cache_lookup(pConVar->GetName(), &pInfo))
dvander@2168
   614
	{
dvander@2168
   615
		return;
dvander@2168
   616
	}
dvander@2168
   617
dvander@2168
   618
	IChangeableForward *pForward = pInfo->pChangeForward;
dvander@2168
   619
dvander@2168
   620
	if (pInfo->changeListeners.size() != 0)
dvander@2168
   621
	{
dvander@2168
   622
		for (List<IConVarChangeListener *>::iterator i = pInfo->changeListeners.begin();
dvander@2168
   623
			 i != pInfo->changeListeners.end();
dvander@2168
   624
			 i++)
dvander@2168
   625
		{
dvander@2168
   626
#if defined ORANGEBOX_BUILD
dvander@2168
   627
			(*i)->OnConVarChanged(pConVar, oldValue, flOldValue);
dvander@2168
   628
#else
dvander@2168
   629
			(*i)->OnConVarChanged(pConVar, oldValue, atof(oldValue));
dvander@2168
   630
#endif
dvander@2168
   631
		}
dvander@2168
   632
	}
dvander@2168
   633
dvander@2168
   634
	if (pForward != NULL)
dvander@2168
   635
	{
dvander@2168
   636
		/* Now call forwards in plugins that have hooked this */
dvander@2168
   637
		pForward->PushCell(pInfo->handle);
dvander@2168
   638
		pForward->PushString(oldValue);
dvander@2168
   639
		pForward->PushString(pConVar->GetString());
dvander@2168
   640
		pForward->Execute(NULL);
dvander@2168
   641
	}
dvander@2168
   642
}
dvander@2168
   643
dvander@2168
   644
bool ConVarManager::IsQueryingSupported()
dvander@2168
   645
{
dvander@2168
   646
	return (m_bIsDLLQueryHooked || m_bIsVSPQueryHooked);
dvander@2168
   647
}
dvander@2168
   648
dvander@2168
   649
void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
dvander@2168
   650
{
dvander@2168
   651
	IPluginFunction *pCallback = NULL;
dvander@2168
   652
	cell_t value = 0;
dvander@2168
   653
	List<ConVarQuery>::iterator iter;
dvander@2168
   654
dvander@2168
   655
	for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end(); iter++)
dvander@2168
   656
	{
dvander@2168
   657
		ConVarQuery &query = (*iter);
dvander@2168
   658
		if (query.cookie == cookie)
dvander@2168
   659
		{
dvander@2168
   660
			pCallback = query.pCallback;
dvander@2168
   661
			value = query.value;
dvander@2168
   662
			break;
dvander@2168
   663
		}
dvander@2168
   664
	}
dvander@2168
   665
dvander@2168
   666
	if (pCallback)
dvander@2168
   667
	{
dvander@2168
   668
		cell_t ret;
dvander@2168
   669
dvander@2168
   670
		pCallback->PushCell(cookie);
dvander@2168
   671
		pCallback->PushCell(engine->IndexOfEdict(pPlayer));
dvander@2168
   672
		pCallback->PushCell(result);
dvander@2168
   673
		pCallback->PushString(cvarName);
dvander@2168
   674
dvander@2168
   675
		if (result == eQueryCvarValueStatus_ValueIntact)
dvander@2168
   676
		{
dvander@2168
   677
			pCallback->PushString(cvarValue);
dvander@2168
   678
		}
dvander@2168
   679
		else
dvander@2168
   680
		{
dvander@2168
   681
			pCallback->PushString("\0");
dvander@2168
   682
		}
dvander@2168
   683
dvander@2168
   684
		pCallback->PushCell(value);
dvander@2168
   685
		pCallback->Execute(&ret);
dvander@2168
   686
dvander@2168
   687
		m_ConVarQueries.erase(iter);
dvander@2168
   688
	}
dvander@2168
   689
}
dvander@2168
   690
dvander@2168
   691
HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
dvander@2168
   692
{
dvander@2168
   693
	ConVarInfo *pInfo;
dvander@2168
   694
	HandleError error;
dvander@2168
   695
	
dvander@2168
   696
	if ((error = g_HandleSys.ReadHandle(hndl, m_ConVarType, NULL, (void **)&pInfo)) != HandleError_None)
dvander@2168
   697
	{
dvander@2168
   698
		return error;
dvander@2168
   699
	}
dvander@2168
   700
dvander@2168
   701
	if (pVar)
dvander@2168
   702
	{
dvander@2168
   703
		*pVar = pInfo->pVar;
dvander@2168
   704
	}
dvander@2168
   705
dvander@2168
   706
	return error;
dvander@2168
   707
}