Page 1 of 1
ConVar - equivalent to RegisterConVarChanges
Posted: Mon Jun 08, 2015 5:35 am
by joshtrav
I have been browsing the wiki, as well as the source on convars.
https://github.com/Source-Python-Dev-Team/Source.Python/blob/master/src/core/modules/cvars/cvars_wrap_python.cpp
http://wiki.sourcepython.com/pages/cvars
I haven't found anything that would resemble a way to hook a convar change, is there a current wrapper for an equivalent? If not, would it be feasible as a feature request?
Posted: Mon Jun 08, 2015 6:15 am
by satoon101
If you don't need to stop the change from happening, you could always just add the NOTIFY flag to the ConVar and listen to the event server_cvar.
Outside of that, we do have an experimental branch currently that will allow you to hook exposed methods of a class.
https://github.com/Source-Python-Dev-Team/Source.Python/tree/exposed_function_hooks
Here is an example of its usage:
https://github.com/Source-Python-Dev-Team/Source.Python/commit/408e95b0e0cc3ab25a5551eb46169709ece35d8a#commitcomment-11510933
Posted: Mon Jun 08, 2015 3:08 pm
by joshtrav
Thanks for the info. I think the first option will work just fine. I assume I could then just set the bDontBroadcast value to True in the server_cvar event?
Posted: Mon Jun 08, 2015 3:21 pm
by satoon101
To set the bDontBroadcast, you will have to hook fire_game_event. Basic event calling does not have that implemented. We do plan on having a PreEvent decorator at some point, probably once the experimental branch is finalized. Currently, you have to do it more like:
Syntax: Select all
from core import PLATFORM
from events import GameEvent
from events.manager import GameEventManager
from memory import Convention
from memory import DataType
from memory import get_object_pointer
from memory import make_object
from memory.hooks import PreHook
FIRE_EVENT_FUNC = get_object_pointer(
GameEventManager).make_virtual_function(
7 if PLATFORM == 'windows' else 8,
Convention.THISCALL,
(DataType.POINTER, DataType.POINTER, DataType.BOOL),
DataType.VOID
)
@PreHook(FIRE_EVENT_FUNC)
def pre_fire_event(arguments):
game_event = make_object(GameEvent, arguments[1])
if game_event.get_name() == 'server_cvar':
arguments[2] = True
Though, hopefully fairly soon, you will just be able to do something more like:
Syntax: Select all
from events.hooks import PreEvent
@PreEvent
def server_cvar(game_event):
# Block the broadcasting of the event to clients
return False
Posted: Mon Jun 08, 2015 4:09 pm
by stonedegg
satoon101 wrote:Though, hopefully fairly soon, you will just be able to do something more like:
[python]from events.hooks import PreEvent
@PreEvent
def server_cvar(game_event):
# Block the broadcasting of the event to clients
return False[/python]
Yes please!!
Posted: Mon Jun 08, 2015 5:34 pm
by Ayuto
I would like to take this chance to show another feature of SP, which is already available since a long time. Though, we have improved this feature a few weeks ago.
You can create functions, which have an address in memory, so you can pass them to any function you want. This means that you can create C++ functions with any calling convention on the fly!
Syntax: Select all
# This example has been made for Windows (CS:S)
import memory
from memory import DataType
from memory import Convention
from memory import Callback
from cvars import cvar
from _cvars import _IConVar
# Get the cvar pointer
cvar_ptr = memory.get_object_pointer(cvar)
# virtual void ICvar::InstallGlobalChangeCallback(FnChangeCallback_t callback) = 0;
InstallGlobalChangeCallback = cvar_ptr.make_virtual_function(
18,
Convention.THISCALL,
[DataType.POINTER, DataType.POINTER],
DataType.VOID
)
# virtual void ICvar::RemoveGlobalChangeCallback(FnChangeCallback_t callback) = 0;
RemoveGlobalChangeCallback = cvar_ptr.make_virtual_function(
19,
Convention.THISCALL,
[DataType.POINTER, DataType.POINTER],
DataType.VOID
)
# Create a function in memory, which can be treated just like a function
# pointer. Awesome, eh? :P
# typedef void ( *FnChangeCallback_t )( IConVar *var, const char *pOldValue, float flOldValue );
@Callback(
Convention.CDECL,
[DataType.POINTER, DataType.STRING, DataType.FLOAT],
DataType.VOID)
def on_cvar_changed(args):
"""Called whenever a ConVar changes."""
convar = memory.make_object(_IConVar, args[0])
old_str_value = args[1]
old_float_value = args[2]
print(convar.get_name(), 'has changed')
def load():
# Install the callback
InstallGlobalChangeCallback(cvar_ptr, on_cvar_changed)
def unload():
# Remove the callback
RemoveGlobalChangeCallback(cvar_ptr, on_cvar_changed)
Posted: Tue Jun 09, 2015 3:44 am
by joshtrav
satoon101 wrote: Though, hopefully fairly soon, you will just be able to do something more like:
Syntax: Select all
from events.hooks import PreEvent
@PreEvent
def server_cvar(game_event):
# Block the broadcasting of the event to clients
return False
This will be very nice, and thanks for the current way as well!
Ayuto wrote:I would like to take this chance to show another feature of SP, which is already available since a long time. Though, we have improved this feature a few weeks ago.
You can create functions, which have an address in memory, so you can pass them to any function you want. This means that you can create C++ functions with any calling convention on the fly!
Syntax: Select all
# This example has been made for Windows (CS:S)
import memory
from memory import DataType
from memory import Convention
from memory import Callback
from cvars import cvar
from _cvars import _IConVar
# Get the cvar pointer
cvar_ptr = memory.get_object_pointer(cvar)
# virtual void ICvar::InstallGlobalChangeCallback(FnChangeCallback_t callback) = 0;
InstallGlobalChangeCallback = cvar_ptr.make_virtual_function(
18,
Convention.THISCALL,
[DataType.POINTER, DataType.POINTER],
DataType.VOID
)
# virtual void ICvar::RemoveGlobalChangeCallback(FnChangeCallback_t callback) = 0;
RemoveGlobalChangeCallback = cvar_ptr.make_virtual_function(
19,
Convention.THISCALL,
[DataType.POINTER, DataType.POINTER],
DataType.VOID
)
# Create a function in memory, which can be treated just like a function
# pointer. Awesome, eh? :P
# typedef void ( *FnChangeCallback_t )( IConVar *var, const char *pOldValue, float flOldValue );
@Callback(
Convention.CDECL,
[DataType.POINTER, DataType.STRING, DataType.FLOAT],
DataType.VOID)
def on_cvar_changed(args):
"""Called whenever a ConVar changes."""
convar = memory.make_object(_IConVar, args[0])
old_str_value = args[1]
old_float_value = args[2]
print(convar.get_name(), 'has changed')
def load():
# Install the callback
InstallGlobalChangeCallback(cvar_ptr, on_cvar_changed)
def unload():
# Remove the callback
RemoveGlobalChangeCallback(cvar_ptr, on_cvar_changed)
This seems very involved and powerful, is there a substantial amount of info on the wiki in regards to this that I missed?
Posted: Tue Jun 09, 2015 6:24 am
by Ayuto
Posted: Wed Jun 10, 2015 2:08 am
by joshtrav
Ayuto, would you still need to hook the GameEventManager to stop the broadcast using the method you posted?
Posted: Wed Jun 10, 2015 6:54 am
by Ayuto
The callback gets called everytime a ConVar changes and it doesn't need the NOTIFY flag. But yes, if it has this flag, you would need to hook FireEvent.