[Advanced] Hook CPluginHelpersCheck::CreateMessage

Please post any questions about developing your plugin here. Please use the search function before posting!
adrianilloo
Junior Member
Posts: 4
Joined: Tue Aug 10, 2021 9:17 am

[Advanced] Hook CPluginHelpersCheck::CreateMessage

Postby adrianilloo » Tue Aug 10, 2021 9:22 am

Hi,

I'm new to Source Python and, while I haven't developed anything on it yet, I would just like to know if Source.Python provides support for hooking CPluginHelpersCheck::CreateMessage (https://github.com/ValveSoftware/source ... ck.cpp#L25)

Thanks.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Re: [Advanced] Hook CPluginHelpersCheck::CreateMessage

Postby satoon101 » Tue Aug 10, 2021 12:31 pm

Yes we do. We also have classes that wrap each of the message types to make it easier to utilize:

http://wiki.sourcepython.com/developing ... e-messages

Look for the messages.Dialog<type> classes in that wiki link.

*Edit: sorry, just realized I misread and you said "hooking" them. Yes, this is definitely possible, as well, but a little more complicated.
Image
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: [Advanced] Hook CPluginHelpersCheck::CreateMessage

Postby L'In20Cible » Tue Aug 10, 2021 6:24 pm

Untested, but could give you some hints:

Syntax: Select all

# ============================================================================
# >> IMPORTS
# ============================================================================
# Source.Python Imports
# Core
from core import get_interface
# Entities
from entities import Edict
from entities.helpers import index_from_edict
# KeyValues
from keyvalues import KeyValues
# Memory
from memory import Convention
from memory import DataType
from memory import make_object
from memory.hooks import PreHook
# Messages
from messages import DialogType
# Players
from players.entity import Player


# ============================================================================
# >> GLOBALS
# ============================================================================
# Get the plugin helpers check interface
plugin_check = get_interface('server', 'PluginHelpersCheck001')


# ============================================================================
# >> HOOKS
# ============================================================================
# virtual bool CreateMessage( const char *plugin, edict_t *pEntity,
# DIALOG_TYPE type, KeyValues *data );
@PreHook(plugin_check.make_virtual_function(
0,
Convention.THISCALL,
(
DataType.POINTER, DataType.STRING,
DataType.POINTER, DataType.INT, DataType.POINTER
),
DataType.BOOL
))
def pre_create_message(stack_data):
"""Pre hook around IPluginHelpersCheck::CreateMessage."""
plugin = stack_data[1]
player = Player(index_from_edict(make_object(Edict, stack_data[2])))
type_ = DialogType(stack_data[3])
data = make_object(KeyValues, stack_data[4])
adrianilloo
Junior Member
Posts: 4
Joined: Tue Aug 10, 2021 9:17 am

Re: [Advanced] Hook CPluginHelpersCheck::CreateMessage

Postby adrianilloo » Wed Aug 11, 2021 1:04 pm

Thank you for the instant help, guys. Nice that the addon supports these things directly from user-level scripts, as I doubted since SM doesn't support these from scripts, for example (only through C++ extensions).

I've properly installed Source.Py in my server and started to code first bits around this hook for what I want to achieve, thanks to @L'In20Cible sample. However, I'm getting the following error when the interface load executes:

Code: Select all

pluginHelpers = get_interface('server', 'PluginHelpersCheck001')

ValueError: Unable to load library 'server'


How to solve it? For info, the server app is HL2:DM

UPDATE: This works instead:

Code: Select all

pluginHelpers = get_interface('hl2mp/bin/server_srv.so', 'PluginHelpersCheck001')

Isn't SP able to translate 'server' or even 'server_srv' to the relative mod's binary path? Similar thing happens when trying to get the engine iface (only 'engine_srv.so' works, note I don't have to prefix any bin directory to it as SP seems to already include the base bin directory in its own search path, or whatever) - May this be considerable an issue to correct/enhance at the SP core?

Another issue, by the way, from my temporary current relevant code:

Code: Select all

pluginHelpers = get_interface('hl2mp/bin/server_srv.so', 'PluginHelpersCheck001')
createMessageFn = pluginHelpers.make_virtual_function(0, Convention.THISCALL,
   # this, plugin, player edict, dialog type, data (KeyValues)
   (DataType.POINTER, DataType.STRING, DataType.POINTER, DataType.INT, DataType.POINTER), DataType.BOOL)

@PreHook(createMessageFn)
def pre_create_message(args):
   edict = edict_from_pointer(args[2])
   player = Player(index_from_edict(edict))
   type = DialogType(args[3])
   print("Sending dialog of type = ", type)


Produces:

Code: Select all

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/plugins/pluginmessages_helper/pluginmessages_helper.py", line 16, in pre_create_message
    player = Player(index_from_edict(args[2]))

Boost.Python.ArgumentError: Python argument types in
    _entities._helpers.index_from_edict(Pointer)
did not match C++ signature:
    index_from_edict(edict_t* Edict)


I tried to replace the line by the following, according from what I could understand from the API:

Code: Select all

edict = edict_from_pointer(args[2])
player = Player(index_from_edict(edict))


But this error happens instead:

Code: Select all

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/plugins/pluginmessages_helper/pluginmessages_helper.py", line 16, in pre_create_message
    edict = edict_from_pointer(args[2])

ValueError: Conversion from "Pointer" (<_memory.Pointer object at 0xea7e61d0>) to "Edict" failed.


I'd appreciate your help, I want to learn this advanced stuff regardless of my final goal. Thank you.
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: [Advanced] Hook CPluginHelpersCheck::CreateMessage

Postby L'In20Cible » Wed Aug 11, 2021 7:24 pm

adrianilloo wrote:Produces:

Code: Select all

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/plugins/pluginmessages_helper/pluginmessages_helper.py", line 16, in pre_create_message
    player = Player(index_from_edict(args[2]))

Boost.Python.ArgumentError: Python argument types in
    _entities._helpers.index_from_edict(Pointer)
did not match C++ signature:
    index_from_edict(edict_t* Edict)

My bad, forgot to cast the pointer in that sample. Basically, since args[2] is the pointer of an Edict, it have to be casted as such first:

Syntax: Select all

from entities import Edict

player = Player(index_from_edict(make_object(Edict, args[2])))


adrianilloo wrote:I tried to replace the line by the following, according from what I could understand from the API:

But this error happens instead:

ValueError: Conversion from "Pointer" (<_memory.Pointer object at 0xea7e61d0>) to "Edict" failed.[/code]

I'd appreciate your help, I want to learn this advanced stuff regardless of my final goal. Thank you.
Using index_from_pointer would have worked if args[2] was a CBaseEntity pointer.
adrianilloo
Junior Member
Posts: 4
Joined: Tue Aug 10, 2021 9:17 am

Re: [Advanced] Hook CPluginHelpersCheck::CreateMessage

Postby adrianilloo » Wed Aug 11, 2021 10:20 pm

L'In20Cible wrote:My bad, forgot to cast the pointer in that sample. Basically, since args[2] is the pointer of an Edict, it have to be casted as such first:

Syntax: Select all

from entities import Edict

player = Player(index_from_edict(make_object(Edict, args[2])))



Alright, thank you. Now my script looks like this (making a basic print that shows hook correctness):

Syntax: Select all

from core import get_interface
from entities import Edict
from entities.helpers import index_from_edict
from memory import Convention, DataType, make_object
from memory.hooks import PreHook
from players.entity import Player

pluginHelpers = get_interface('hl2mp/bin/server_srv.so', 'PluginHelpersCheck001')
# pluginHelpers = get_interface('server', 'PluginHelpersCheck001')
createMessageFn = pluginHelpers.make_virtual_function(0, Convention.THISCALL,
# this, plugin, player edict, dialog type, data (KeyValues)
(DataType.POINTER, DataType.STRING, DataType.POINTER, DataType.INT, DataType.POINTER), DataType.BOOL)

@PreHook(createMessageFn)
def OnCreateMessage(args):
edict = make_object(Edict, args[2])
player = Player(index_from_edict(edict))
print("Sending dialog of type {} to player #{}...".format(args[3], player.userid))


However, what about this original approach which failed?

Syntax: Select all

# pluginHelpers = get_interface('server', 'PluginHelpersCheck001')
Does it work to you?

While this function is the convenient wrapper to get Source virtual object interfaces, it seems to lack smart path search, unlike

Syntax: Select all

FindBinary
does. In this sense, executing

Syntax: Select all

server = find_binary('server')
instead works, but then there seems to be no way to connect the resulting

Syntax: Select all

BinaryFile
object to

Syntax: Select all

get_interface
(e.g. by getting the real translated path and passing it). Isn't there a clear support lack? Or how to make this properly without having to specify the hardcoded 'hl2mp/bin/server_srv.so' path, and naturally, not accessing symbols/signatures manually through the find_binary + operator [] way?
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: [Advanced] Hook CPluginHelpersCheck::CreateMessage

Postby L'In20Cible » Wed Aug 11, 2021 10:50 pm

adrianilloo wrote:Does it work to you?
No idea. As previously stated, I haven't tested and only wrote an example to provide some hints.

adrianilloo wrote:While this function is the convenient wrapper to get Source virtual object interfaces, it seems to lack smart path search, unlike

Syntax: Select all

FindBinary
does. In this sense, executing

Syntax: Select all

server = find_binary('server')
instead works, but then there seems to be no way to connect the resulting

Syntax: Select all

BinaryFile
object to

Syntax: Select all

get_interface
(e.g. by getting the real translated path and passing it). Isn't there a clear support lack? Or how to make this properly without having to specify the hardcoded 'hl2mp/bin/server_srv.so' path, and naturally, not accessing symbols/signatures manually through the find_binary + operator [] way?

We could probably add path finding support to get_interface, or even add BinaryFile.get_interface method that resolve the interface from that object. Feel free to open a feature request and/or make a PR on github. However, for now, you could simply format the path yourself with something like that:

Syntax: Select all

from core import GAME_NAME
from core import PLATFORM

pluginHelpers = get_interface(
f'{GAME_NAME}/bin/server{"_srv.so" if PLATFORM == "linux" else ".dll"}',
'PluginHelpersCheck001'
)
Which should have the same behaviours as find_binary('server', True, True).

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 32 guests