void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name)
{
	/* Only check convars that have not been created by SourceMod's core */
	ConVarInfo *pInfo;
	if (!convar_cache_lookup(name, &pInfo))
	{
		return;
	}

	HandleSecurity sec(NULL, g_pCoreIdent);

	/* Remove it from our cache */
	m_ConVars.remove(pInfo);
	convar_cache.remove(name);

	/* Now make sure no plugins are referring to this pointer */
	IPluginIterator *pl_iter = scripts->GetPluginIterator();
	while (pl_iter->MorePlugins())
	{
		IPlugin *pl = pl_iter->GetPlugin();

		ConVarList *pConVarList;
		if (pl->GetProperty("ConVarList", (void **)&pConVarList, true) 
			&& pConVarList != NULL)
		{
			pConVarList->remove(pInfo->pVar);
		}

		pl_iter->NextPlugin();
	}

	/* Free resources */
	handlesys->FreeHandle(pInfo->handle, &sec);
	delete pInfo;
}
void ConVarManager::OnSourceModShutdown()
{
	List<ConVarInfo *>::iterator iter = m_ConVars.begin();
	HandleSecurity sec(NULL, g_pCoreIdent);

	/* Iterate list of ConVarInfo structures, remove every one of them */
	while (iter != m_ConVars.end())
	{
		ConVarInfo *pInfo = (*iter);

		iter = m_ConVars.erase(iter);

		handlesys->FreeHandle(pInfo->handle, &sec);
		if (pInfo->pChangeForward != NULL)
		{
			forwardsys->ReleaseForward(pInfo->pChangeForward);
		}
		if (pInfo->sourceMod)
		{
			/* If we created it, we won't be tracking it, therefore it is 
			 * safe to remove everything in one go.
			 */
			META_UNREGCVAR(pInfo->pVar);
			delete [] pInfo->pVar->GetName();
			delete [] pInfo->pVar->GetHelpText();
			delete [] pInfo->pVar->GetDefault();
			delete pInfo->pVar;
		}
		else
		{
			/* If we didn't create it, we might be tracking it.  Also, 
			 * it could be unreadable.
			 */
			UntrackConCommandBase(pInfo->pVar, this);
		}

		/* It's not safe to read the name here, so we simply delete the 
		 * the info struct and clear the lookup cache at the end.
		 */
		delete pInfo;
	}
	convar_cache.clear();

	g_Players.RemoveClientListener(this);

	/* Remove the 'convars' option from the 'sm' console command */
	rootmenu->RemoveRootConsoleCommand("cvars", this);

	scripts->RemovePluginsListener(this);

	/* Remove the 'ConVar' handle type */
	handlesys->RemoveType(m_ConVarType, g_pCoreIdent);
}
Handle_t ConVarManager::FindConVar(const char *name)
{
	ConVar *pConVar = NULL;
	ConVarInfo *pInfo;
	Handle_t hndl;

	/* Check convar cache to find out if we already have a handle */
	if (convar_cache_lookup(name, &pInfo))
	{
		return pInfo->handle;
	}

	/* Couldn't find it in cache, so search for it */
	pConVar = icvar->FindVar(name);

	/* If it doesn't exist, then return an invalid handle */
	if (!pConVar)
	{
		return BAD_HANDLE;
	}

	/* Create and initialize ConVarInfo structure */
	pInfo = new ConVarInfo();
	pInfo->sourceMod = false;
	pInfo->pChangeForward = NULL;
	pInfo->pVar = pConVar;

	/* If we don't have a handle, then create a new one */
	hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
	if (hndl == BAD_HANDLE)
	{
		delete pInfo;
		return BAD_HANDLE;
	}

	pInfo->handle = hndl;

	/* Insert struct into our caches */
	m_ConVars.push_back(pInfo);
	convar_cache.insert(name, pInfo);
	TrackConCommandBase(pConVar, this);

	return hndl;
}
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)
{
	ConVar *pConVar = NULL;
	ConVarInfo *pInfo = NULL;
	Handle_t hndl = 0;

	/* Find out if the convar exists already */
	pConVar = icvar->FindVar(name);

	/* If the convar already exists... */
	if (pConVar)
	{
		/* Add convar to plugin's list */
		AddConVarToPluginList(pContext, pConVar);

		/* First find out if we already have a handle to it */
		if (convar_cache_lookup(name, &pInfo))
		{
			return pInfo->handle;
		}
		else
		{
			/* Create and initialize ConVarInfo structure */
			pInfo = new ConVarInfo();
			pInfo->sourceMod = false;
			pInfo->pChangeForward = NULL;
			pInfo->pVar = pConVar;

			/* If we don't, then create a new handle from the convar */
			hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
			if (hndl == BAD_HANDLE)
			{
				delete pInfo;
				return BAD_HANDLE;
			}

			pInfo->handle = hndl;

			/* Insert struct into caches */
			m_ConVars.push_back(pInfo);
			convar_cache.insert(name, pInfo);
			TrackConCommandBase(pConVar, this);

			return hndl;
		}
	}

	/* Prevent creating a convar that has the same name as a console command */
	if (FindCommand(name))
	{
		return BAD_HANDLE;
	}

	/* Create and initialize ConVarInfo structure */
	pInfo = new ConVarInfo();
	pInfo->handle = hndl;
	pInfo->sourceMod = true;
	pInfo->pChangeForward = NULL;

	/* Create a handle from the new convar */
	hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
	if (hndl == BAD_HANDLE)
	{
		delete pInfo;
		return BAD_HANDLE;
	}

	pInfo->handle = hndl;

	/* Hardening: remove NOTIFY from any convar plugins attempt to create */
	flags &= ~FCVAR_NOTIFY;

	/* Since an existing convar (or concmd with the same name) was not found , now we can finally create it */
	pConVar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(description), hasMin, min, hasMax, max);
	pInfo->pVar = pConVar;

	/* Add convar to plugin's list */
	AddConVarToPluginList(pContext, pConVar);

	/* Insert struct into caches */
	m_ConVars.push_back(pInfo);
	convar_cache.insert(name, pInfo);

	return hndl;
}
bool convar_cache_lookup(const char *name, ConVarInfo **pVar)
{
	return convar_cache.retrieve(name, pVar);
}
void ConVarManager::OnSourceModShutdown()
{
	List<ConVarInfo *>::iterator iter = m_ConVars.begin();
	HandleSecurity sec(NULL, g_pCoreIdent);

	/* Iterate list of ConVarInfo structures, remove every one of them */
	while (iter != m_ConVars.end())
	{
		ConVarInfo *pInfo = (*iter);

		iter = m_ConVars.erase(iter);

		handlesys->FreeHandle(pInfo->handle, &sec);
		if (pInfo->pChangeForward != NULL)
		{
			forwardsys->ReleaseForward(pInfo->pChangeForward);
		}
		if (pInfo->sourceMod)
		{
			/* If we created it, we won't be tracking it, therefore it is 
			 * safe to remove everything in one go.
			 */
			META_UNREGCVAR(pInfo->pVar);
			delete [] pInfo->pVar->GetName();
			delete [] pInfo->pVar->GetHelpText();
			delete [] pInfo->pVar->GetDefault();
			delete pInfo->pVar;
		}
		else
		{
			/* If we didn't create it, we might be tracking it.  Also, 
			 * it could be unreadable.
			 */
			UntrackConCommandBase(pInfo->pVar, this);
		}

		/* It's not safe to read the name here, so we simply delete the 
		 * the info struct and clear the lookup cache at the end.
		 */
		delete pInfo;
	}
	convar_cache.clear();

#if SOURCE_ENGINE != SE_DARKMESSIAH
	/* Unhook things */
	if (m_bIsDLLQueryHooked)
	{
		SH_REMOVE_HOOK(IServerGameDLL, OnQueryCvarValueFinished, gamedll, SH_MEMBER(this, &ConVarManager::OnQueryCvarValueFinished), false);
		m_bIsDLLQueryHooked = false;
	}
	else if (m_bIsVSPQueryHooked)
	{
#if SOURCE_ENGINE != SE_DOTA
		SH_REMOVE_HOOK(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, SH_MEMBER(this, &ConVarManager::OnQueryCvarValueFinished), false);
#endif
		m_bIsVSPQueryHooked = false;
	}
#endif

	g_Players.RemoveClientListener(this);

#if SOURCE_ENGINE >= SE_ORANGEBOX
	SH_REMOVE_HOOK(ICvar, CallGlobalChangeCallbacks, icvar, SH_STATIC(OnConVarChanged), false);
#else
	SH_REMOVE_HOOK(ICvar, CallGlobalChangeCallback, icvar, SH_STATIC(OnConVarChanged), false);
#endif

	/* Remove the 'convars' option from the 'sm' console command */
	g_RootMenu.RemoveRootConsoleCommand("cvars", this);

	scripts->RemovePluginsListener(this);

	/* Remove the 'ConVar' handle type */
	handlesys->RemoveType(m_ConVarType, g_pCoreIdent);
}