From 6a2ac9800bfd10ec986d18d2286177f4cbc52803 Mon Sep 17 00:00:00 2001 From: Asher Baker Date: Sun, 18 Jul 2021 17:19:27 +0100 Subject: [PATCH] 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 --- core/ConVarManager.cpp | 38 ++++++++++++++++++++++++++++--------- core/ConVarManager.h | 5 +++-- core/smn_console.cpp | 16 ++++++++++++++++ plugins/include/convars.inc | 5 +++++ 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/core/ConVarManager.cpp b/core/ConVarManager.cpp index 52700679..dcd5d8ed 100644 --- a/core/ConVarManager.cpp +++ b/core/ConVarManager.cpp @@ -210,18 +210,27 @@ void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *na void ConVarManager::OnPluginUnloaded(IPlugin *plugin) { ConVarList *pConVarList; - List::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::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::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; } diff --git a/core/ConVarManager.h b/core/ConVarManager.h index fd3b0025..b541baf9 100644 --- a/core/ConVarManager.h +++ b/core/ConVarManager.h @@ -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 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 m_ConVars; diff --git a/core/smn_console.cpp b/core/smn_console.cpp index 5ed90a7e..25da56ba 100644 --- a/core/smn_console.cpp +++ b/core/smn_console.cpp @@ -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(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(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}, diff --git a/plugins/include/convars.inc b/plugins/include/convars.inc index cde31d1e..8e7ccace 100644 --- a/plugins/include/convars.inc +++ b/plugins/include/convars.inc @@ -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