Track the creating plugin for convars (#1537)

Similar to the recent work for commands, track and expose the creating
plugin for convars. The first plugin to register a given cvar becomes
the owner until that plugin is unloaded. If a plugin attempts to
register a convar that was already registered and the originally
registering plugin has been unloaded, that plugin becomes the owner.
This isn't quite as nice as the way commands shift "ownership" as
plugins are unloaded, but we don't have a sane data structure currently
to implement that, and it seemed like a lot of unnecessary work as there
shouldn't really be multiple plugins with conflicting cvars.

Closes #1492
This commit is contained in:
Asher Baker 2021-07-18 17:19:27 +01:00 committed by GitHub
parent 39aa75436e
commit 6a2ac9800b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 11 deletions

View File

@ -210,18 +210,27 @@ void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *na
void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
{
ConVarList *pConVarList;
List<ConVarQuery>::iterator iter;
/* If plugin has a convar list, free its memory */
if (plugin->GetProperty("ConVarList", (void **)&pConVarList, true))
{
delete pConVarList;
}
/* Clear any references to this plugin as the convar creator */
for (List<ConVarInfo *>::iterator iter = m_ConVars.begin(); iter != m_ConVars.end(); ++iter)
{
ConVarInfo *pInfo = (*iter);
if (pInfo->pPlugin == plugin)
{
pInfo->pPlugin = nullptr;
}
}
const IPluginRuntime * pRuntime = plugin->GetRuntime();
/* Remove convar queries for this plugin that haven't returned results yet */
for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end();)
for (List<ConVarQuery>::iterator iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end();)
{
ConVarQuery &query = (*iter);
if (query.pCallback->GetParentRuntime() == pRuntime)
@ -347,6 +356,8 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
ConVarInfo *pInfo = NULL;
Handle_t hndl = 0;
IPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext());
/* Find out if the convar exists already */
pConVar = icvar->FindVar(name);
@ -354,11 +365,16 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
if (pConVar)
{
/* Add convar to plugin's list */
AddConVarToPluginList(pContext, pConVar);
AddConVarToPluginList(plugin, pConVar);
/* First find out if we already have a handle to it */
if (convar_cache_lookup(name, &pInfo))
{
/* If the convar doesn't have an owning plugin, but SM created it, adopt it */
if (pInfo->sourceMod && pInfo->pPlugin == nullptr) {
pInfo->pPlugin = plugin;
}
return pInfo->handle;
}
else
@ -399,6 +415,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo->handle = hndl;
pInfo->sourceMod = true;
pInfo->pChangeForward = NULL;
pInfo->pPlugin = plugin;
/* Create a handle from the new convar */
hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
@ -415,7 +432,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo->pVar = pConVar;
/* Add convar to plugin's list */
AddConVarToPluginList(pContext, pConVar);
AddConVarToPluginList(plugin, pConVar);
/* Insert struct into caches */
m_ConVars.push_back(pInfo);
@ -569,15 +586,13 @@ QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char
return cookie;
}
void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
void ConVarManager::AddConVarToPluginList(IPlugin *plugin, const ConVar *pConVar)
{
ConVarList *pConVarList;
ConVarList::iterator iter;
bool inserted = false;
const char *orig = pConVar->GetName();
IPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext());
/* Check plugin for an existing convar list */
if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
{
@ -696,7 +711,7 @@ void ConVarManager::OnClientQueryFinished(QueryCvarCookie_t cookie,
}
#endif
HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar, IPlugin **ppPlugin)
{
ConVarInfo *pInfo;
HandleError error;
@ -711,5 +726,10 @@ HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
*pVar = pInfo->pVar;
}
if (ppPlugin)
{
*ppPlugin = pInfo->pPlugin;
}
return error;
}

View File

@ -62,6 +62,7 @@ struct ConVarInfo
bool sourceMod; /**< Determines whether or not convar was created by a SourceMod plugin */
IChangeableForward *pChangeForward; /**< Forward associated with convar */
ConVar *pVar; /**< The actual convar */
IPlugin *pPlugin; /**< Originally owning plugin */
List<IConVarChangeListener *> changeListeners;
struct ConVarPolicy
@ -154,7 +155,7 @@ public:
bool IsQueryingSupported();
HandleError ReadConVarHandle(Handle_t hndl, ConVar **pVar);
HandleError ReadConVarHandle(Handle_t hndl, ConVar **pVar, IPlugin **ppPlugin = nullptr);
// Called via game hooks.
void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue);
@ -171,7 +172,7 @@ private:
/**
* Adds a convar to a plugin's list.
*/
static void AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar);
static void AddConVarToPluginList(IPlugin *plugin, const ConVar *pConVar);
private:
HandleType_t m_ConVarType;
List<ConVarInfo *> m_ConVars;

View File

@ -553,6 +553,21 @@ static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
return pConVar->m_nFlags;
}
static cell_t sm_GetConVarPlugin(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast<Handle_t>(params[1]);
HandleError err;
IPlugin *pPlugin;
if ((err=g_ConVarManager.ReadConVarHandle(hndl, nullptr, &pPlugin))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
}
return pPlugin ? pPlugin->GetMyHandle() : BAD_HANDLE;
}
static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast<Handle_t>(params[1]);
@ -1517,6 +1532,7 @@ REGISTER_NATIVES(consoleNatives)
{"ConVar.IntValue.set", sm_SetConVarNum},
{"ConVar.Flags.get", sm_GetConVarFlags},
{"ConVar.Flags.set", sm_SetConVarFlags},
{"ConVar.Plugin.get", sm_GetConVarPlugin},
{"ConVar.SetBool", sm_SetConVarNum},
{"ConVar.SetInt", sm_SetConVarNum},
{"ConVar.SetFloat", sm_SetConVarFloat},

View File

@ -122,6 +122,11 @@ methodmap ConVar < Handle
public native set(int flags);
}
// Retrieves the plugin handle of the convar's creator
property Handle Plugin {
public native get();
}
// Sets the boolean value of a console variable.
//
// Note: The replicate and notify params are only relevant for the