Skip to content

Commit ea5fab5

Browse files
committed
Added the ability to add callbacks to ConVar that will be called when ConVar is changed.
1 parent 45b24d8 commit ea5fab5

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

src/core/modules/cvars/cvars.h

100644100755
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,22 @@
3030
//-----------------------------------------------------------------------------
3131
// Includes.
3232
//-----------------------------------------------------------------------------
33+
// C++
34+
#include <unordered_map>
35+
#include <vector>
36+
3337
#include "convar.h"
38+
3439
#include "utilities/sp_util.h"
40+
#include "modules/listeners/listeners_manager.h"
41+
42+
43+
//-----------------------------------------------------------------------------
44+
// Global ConVar changed callback mapping.
45+
//-----------------------------------------------------------------------------
46+
typedef std::vector<object> ChangedCallbacks;
47+
typedef std::unordered_map<std::string, ChangedCallbacks> ConVarMap;
48+
ConVarMap g_ConVarMap;
3549

3650

3751
//-----------------------------------------------------------------------------
@@ -117,6 +131,88 @@ class ConVarExt
117131
pConVar->m_nFlags &= ~FCVAR_NOTIFY;
118132
g_pCVar->CallGlobalChangeCallbacks(pConVar, pConVar->GetString(), pConVar->GetFloat());
119133
}
134+
135+
static void ChangedCallback(IConVar* var, const char* pOldValue, float flOldValue)
136+
{
137+
ConVarMap::iterator map_it = g_ConVarMap.find(var->GetName());
138+
if (map_it == g_ConVarMap.end())
139+
return;
140+
141+
ConVar* pConVar = static_cast<ConVar*>(var);
142+
143+
ChangedCallbacks& callables = map_it->second;
144+
for (ChangedCallbacks::iterator it = callables.begin(); it != callables.end(); ++it)
145+
{
146+
BEGIN_BOOST_PY()
147+
(*it)(ptr(pConVar), pOldValue, pConVar->GetString());
148+
END_BOOST_PY_NORET()
149+
}
150+
}
151+
152+
static void AddChangedCallback(ConVar* pConVar, PyObject* pCallable)
153+
{
154+
// Get the object instance of the callable
155+
object oCallable = object(handle<>(borrowed(pCallable)));
156+
157+
ChangedCallbacks& callables = g_ConVarMap[pConVar->GetName()];
158+
if (!callables.size())
159+
{
160+
if (!installed)
161+
{
162+
g_pCVar->InstallGlobalChangeCallback(ChangedCallback);
163+
installed = true;
164+
}
165+
}
166+
else
167+
{
168+
for (ChangedCallbacks::iterator it = callables.begin(); it != callables.end(); ++it)
169+
{
170+
if (is_same_func(oCallable, *it))
171+
BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Callback already registered.")
172+
}
173+
}
174+
175+
callables.push_back(oCallable);
176+
}
177+
178+
static void RemoveChangedCallback(ConVar* pConVar, PyObject* pCallable)
179+
{
180+
ConVarMap::iterator map_it = g_ConVarMap.find(pConVar->GetName());
181+
if (map_it == g_ConVarMap.end())
182+
BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Callback not registered.")
183+
184+
// Get the object instance of the callable
185+
object oCallable = object(handle<>(borrowed(pCallable)));
186+
187+
ChangedCallbacks& callables = map_it->second;
188+
for (ChangedCallbacks::iterator it = callables.begin();;)
189+
{
190+
if(it == callables.end())
191+
BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Callback not registered.")
192+
193+
if (is_same_func(oCallable, *it))
194+
{
195+
callables.erase(it);
196+
break;
197+
}
198+
else
199+
{
200+
++it;
201+
}
202+
}
203+
204+
if (!callables.size())
205+
{
206+
g_ConVarMap.erase(map_it);
207+
if (!g_ConVarMap.size())
208+
{
209+
g_pCVar->RemoveGlobalChangeCallback(ChangedCallback);
210+
installed = false;
211+
}
212+
}
213+
}
214+
215+
static bool installed;
120216
};
121217

122218

src/core/modules/cvars/cvars_wrap.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
extern ICvar* g_pCVar;
4646

4747

48+
//-----------------------------------------------------------------------------
49+
// ConVar extension definition.
50+
//-----------------------------------------------------------------------------
51+
bool ConVarExt::installed = false;
52+
53+
4854
//-----------------------------------------------------------------------------
4955
// Forward declarations.
5056
//-----------------------------------------------------------------------------
@@ -299,7 +305,19 @@ void export_convar(scope _cvars)
299305
&ConVarExt::RemovePublic,
300306
"Remove the notify flag and make the console variable no longer public."
301307
)
302-
308+
309+
.def("add_changed_callback",
310+
&ConVarExt::AddChangedCallback,
311+
"Add a callable object that will be called when the ConVar is changed.",
312+
args("callable")
313+
)
314+
315+
.def("remove_changed_callback",
316+
&ConVarExt::RemoveChangedCallback,
317+
"Remove a callable object that will be called when the ConVar is changed.",
318+
args("callable")
319+
)
320+
303321
// Special methods...
304322
.def("__float__",
305323
&ConVar::GetFloat,

0 commit comments

Comments
 (0)