Skip to content

Added the ability to add callbacks to ConVar that will be called when ConVar is changed. #421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Next Next commit
Added the ability to add callbacks to ConVar that will be called when…
… ConVar is changed.
  • Loading branch information
CookStar committed Oct 2, 2021
commit ea5fab536fa223396ac4a2dd10409f7784acd5d4
96 changes: 96 additions & 0 deletions src/core/modules/cvars/cvars.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,22 @@
//-----------------------------------------------------------------------------
// Includes.
//-----------------------------------------------------------------------------
// C++
#include <unordered_map>
#include <vector>

#include "convar.h"

#include "utilities/sp_util.h"
#include "modules/listeners/listeners_manager.h"


//-----------------------------------------------------------------------------
// Global ConVar changed callback mapping.
//-----------------------------------------------------------------------------
typedef std::vector<object> ChangedCallbacks;
typedef std::unordered_map<std::string, ChangedCallbacks> ConVarMap;
ConVarMap g_ConVarMap;


//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -117,6 +131,88 @@ class ConVarExt
pConVar->m_nFlags &= ~FCVAR_NOTIFY;
g_pCVar->CallGlobalChangeCallbacks(pConVar, pConVar->GetString(), pConVar->GetFloat());
}

static void ChangedCallback(IConVar* var, const char* pOldValue, float flOldValue)
{
ConVarMap::iterator map_it = g_ConVarMap.find(var->GetName());
if (map_it == g_ConVarMap.end())
return;

ConVar* pConVar = static_cast<ConVar*>(var);

ChangedCallbacks& callables = map_it->second;
for (ChangedCallbacks::iterator it = callables.begin(); it != callables.end(); ++it)
{
BEGIN_BOOST_PY()
(*it)(ptr(pConVar), pOldValue, pConVar->GetString());
END_BOOST_PY_NORET()
}
}

static void AddChangedCallback(ConVar* pConVar, PyObject* pCallable)
{
// Get the object instance of the callable
object oCallable = object(handle<>(borrowed(pCallable)));

ChangedCallbacks& callables = g_ConVarMap[pConVar->GetName()];
if (!callables.size())
{
if (!installed)
{
g_pCVar->InstallGlobalChangeCallback(ChangedCallback);
installed = true;
}
}
else
{
for (ChangedCallbacks::iterator it = callables.begin(); it != callables.end(); ++it)
{
if (is_same_func(oCallable, *it))
BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Callback already registered.")
}
}

callables.push_back(oCallable);
}

static void RemoveChangedCallback(ConVar* pConVar, PyObject* pCallable)
{
ConVarMap::iterator map_it = g_ConVarMap.find(pConVar->GetName());
if (map_it == g_ConVarMap.end())
BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Callback not registered.")

// Get the object instance of the callable
object oCallable = object(handle<>(borrowed(pCallable)));

ChangedCallbacks& callables = map_it->second;
for (ChangedCallbacks::iterator it = callables.begin();;)
{
if(it == callables.end())
BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Callback not registered.")

if (is_same_func(oCallable, *it))
{
callables.erase(it);
break;
}
else
{
++it;
}
}

if (!callables.size())
{
g_ConVarMap.erase(map_it);
if (!g_ConVarMap.size())
{
g_pCVar->RemoveGlobalChangeCallback(ChangedCallback);
installed = false;
}
}
}

static bool installed;
};


Expand Down
20 changes: 19 additions & 1 deletion src/core/modules/cvars/cvars_wrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
extern ICvar* g_pCVar;


//-----------------------------------------------------------------------------
// ConVar extension definition.
//-----------------------------------------------------------------------------
bool ConVarExt::installed = false;


//-----------------------------------------------------------------------------
// Forward declarations.
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -299,7 +305,19 @@ void export_convar(scope _cvars)
&ConVarExt::RemovePublic,
"Remove the notify flag and make the console variable no longer public."
)


.def("add_changed_callback",
&ConVarExt::AddChangedCallback,
"Add a callable object that will be called when the ConVar is changed.",
args("callable")
)

.def("remove_changed_callback",
&ConVarExt::RemoveChangedCallback,
"Remove a callable object that will be called when the ConVar is changed.",
args("callable")
)

// Special methods...
.def("__float__",
&ConVar::GetFloat,
Expand Down