Page 1 of 2
TF2 run_command hook
Posted: Wed Jul 13, 2016 12:36 am
by quartata
Hi all. I'm making a TF2 plugin in SourcePython and I'm trying to make a hook using @EntityPreHook for the run_command hook (which should fire every time a player's usercmd is processed if I'm not mistaken). However I keep getting a nasty error:
Code: Select all
Traceback (most recent call last):
File '../addons/source-python/packages/source-python/entities/hooks.py', line 184, in on_entity_created
_waiting_entity_hooks.initialize(index)
File '../addons/source-python/packages/source-python/entities/hooks.py', line 170, in initialize
if hook.initialize(entity):
File '../addons/source-python/packages/source-python/entities/hooks.py', line 131, in initialize
self.hooked_function = getattr(entity, self.function)
File '../addons/source-python/packages/source-python/entities/entity.py', line 102, in __getattr__
raise AttributeError('Attribute '{0}' not found'.format(attr))
AttributeError: Attribute 'run_command' not found
Here's the really minimal code I was testing with:
Syntax: Select all
from entities.hooks import EntityCondition, EntityPreHook
from players import UserCmd
from memory import make_object
@EntityPreHook(EntityCondition.is_player, "run_command")
def classmenu(stack):
print(make_object(UserCmd, stack[1]).buttons)
What's going wrong here?
(I'm using the latest version of SourcePython)
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 3:11 am
by L'In20Cible
I guess this:
https://github.com/Source-Python-Dev-Te ... ini#L39-43Should be moved into ../orangebox/CBasePlayer.ini as it is most likely the same for all OB games. I will have to update some of my servers before confirming it is and push that but try to replace the content of that file with the following:
Syntax: Select all
# ../orangebox/CBasePlayer.ini
[function]
[[increment_frag_count]]
identifier_windows = 55 8B EC 8B 45 08 01 81 F8 0C 00 00
identifier_linux = _ZN11CBasePlayer18IncrementFragCountEi
arguments = INT
[[increment_death_count]]
identifier_windows = 55 8B EC 8B 45 08 01 81 FC 0C 00 00
identifier_linux = _ZN11CBasePlayer19IncrementDeathCountEi
arguments = INT
[virtual_function]
# _ZN11CBasePlayer11Weapon_DropEP17CBaseCombatWeaponPK6VectorS4_
[[drop_weapon]]
offset_linux = 264
offset_windows = 263
arguments = POINTER, POINTER, POINTER
# _ZN11CBasePlayer8PreThinkEv
[[pre_think]]
offset_linux = 333
offset_windows = 332
# _ZN11CBasePlayer9PostThinkEv
[[post_think]]
offset_linux = 334
offset_windows = 333
# _ZN11CBasePlayer16PlayerRunCommandEP8CUserCmdP11IMoveHelper
[[run_command]]
offset_linux = 420
offset_windows = 419
arguments = POINTER, POINTER
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 3:15 am
by satoon101
No it isn't, at least not as of the time this was added:
https://github.com/Source-Python-Dev-Te ... yer.ini#L4We do need to add it for all engines/games, though. I am finally getting up and running at my new place, but my computer has been offline for 2 months and has a lot of catching up to do before I can look to add those in.
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 4:04 am
by L'In20Cible
You are right, HL2:MP/DOD:S are one offset lesser than CS:S/TF2. I've pushed for those 2 missing games, don't have updated servers for the others.
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 2:44 pm
by quartata
So wait, was the Python binding for CBasePlayer::PlayerRunCommand missing in the TF2 build but not in the others? That explains why I saw a forum thread about it working in HL2DM... I assumed I was just doing it wrong :P
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 3:00 pm
by Ayuto
Yep, that was the reason.
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 4:03 pm
by quartata
Just tried it with the new build, when I added a puppet bot to test it (I was too lazy to actually connect to the server) it segfaults. Give me a moment to attach a debug.log
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 5:11 pm
by quartata
Sorry for the delay:
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 5:12 pm
by quartata
Probably should have mentioned this earlier, but I'm on Linux (Ubuntu 14.04). I do have two other plugins on my testing server (TFTrue and TF2Items) but looking at the stack trace they're definitely not the culprits.
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 5:20 pm
by Ayuto
Looks like Sourcemod is also hooking this method and now they are conflicting when calling make_object().
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 5:23 pm
by quartata
Interesting; I'll take out Sourcemod for the time and see if it works
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 5:32 pm
by quartata
Actually, I'll just whip up a clean new server to test it with. Can't do that right now though.
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 8:48 pm
by quartata
No luck even on a clean server without Sourcemod or any of that:
Re: TF2 run_command hook
Posted: Wed Jul 13, 2016 10:39 pm
by quartata
I should probably open this as an issue on Github instead of here
Re: TF2 run_command hook
Posted: Thu Jul 14, 2016 4:45 am
by L'In20Cible
What if, instead of using EntityCondition.is_player, you try with EntityCondition.is_human_player and you actually goes on the server yourself instead of adding bots?
Re: TF2 run_command hook
Posted: Thu Jul 14, 2016 2:35 pm
by quartata
Stranger and stranger. It works perfectly fine like that, but adding a bot still crashes it even though it's using is_human_player now (which means the hook shouldn't even get called for a bot). It's only when this plugin is loaded so it isn't a general bug in SP or anything. NextBots don't do this, just puppet bots. Not a big deal, just somewhat strange.
Re: TF2 run_command hook
Posted: Thu Jul 14, 2016 2:57 pm
by L'In20Cible
quartata wrote:even though it's using is_human_player now (which means the hook shouldn't even get called).
That's not right. The hook will gets called if both classes share the same address in their dispatch table. The EntityCondition only makes sure that the address is retrieved from an human player before registering the hook but if both classes are sharing the same pointer, it will get fired no matter what the actual "this" pointer is (done this way so you can fully control which method to hook in case of multiple inheritances but filtering the calls, is your responsability).
Now it is time to debug, what gets printed to the console before it crashes?
Syntax: Select all
from entities.hooks import EntityCondition, EntityPreHook
from players import UserCmd
from memory import make_object
@EntityPreHook(EntityCondition.is_human_player, 'run_command')
def classmenu(stack):
print('Callback executed...')
print('Arguments =', list(stack))
usercmd = make_object(UserCmd, stack[1])
print('UserCmd =', usercmd)
print('Buttons =', usercmd.buttons)
Additional question, does it crashes even if you didn't join the server first? In such case, the hook should not be registered so if it does, we may have to add a specific check in either the hook API or the conversion functions which may get a NULL pointer somewhere for those specific bots.
Also, I originally proposed EntityCondition.is_human_player cause I thought the bot class may override that method and pass a CBotCmd instance instead of a CUserCmd.
Re: TF2 run_command hook
Posted: Thu Jul 14, 2016 3:32 pm
by quartata
It does not crash if I don't join the server first. When I do join, it prints the Callback executed and the Arguments before crashing. So the bots using a different class for their usercmd seems plausible; it's obviously crashing when dereferencing the pointer to the usercmd.
Re: TF2 run_command hook
Posted: Thu Jul 14, 2016 3:36 pm
by L'In20Cible
Yeah, I think so. What if you use the following:
Syntax: Select all
from entities.hooks import EntityCondition, EntityPreHook
from players.bots import BotCmd
from players import UserCmd
from players.entity import Player
from memory import make_object
@EntityPreHook(EntityCondition.is_player, "run_command")
def classmenu(stack):
player = make_object(Player, stack[0])
if player.is_fake_client():
cls = BotCmd
else:
cls = UserCmd
usercmd = make_object(cls, stack[1])
print(usercmd.buttons)
Re: TF2 run_command hook
Posted: Thu Jul 14, 2016 3:41 pm
by quartata
Did you mean
Syntax: Select all
from players.bots import BotCmd
?