Hook virtual functions by offset
With these changes, we will be able to hook virtual functions based on offset.
Example
Testing StartTouch, Touch, EndTouch hooks on the soccerball in de_dust2
{
"StartTouch": {
"offsets": {
"windows": 143,
"linux": 142
}
},
"Touch": {
"offsets": {
"windows": 144,
"linux": 143
}
},
"EndTouch": {
"offsets": {
"windows": 145,
"linux": 144
}
}
}
public CPhysicsPropMultiplayer? SoccerBall;
public VirtualFunctionVoid<CBaseEntity, CBaseEntity>? OnStartTouch;
public VirtualFunctionVoid<CBaseEntity, CBaseEntity>? OnTouch;
public VirtualFunctionVoid<CBaseEntity, CBaseEntity>? OnEndTouch;
public override void Load(bool hotReload)
{
RegisterEventHandler<EventRoundStart>((@event, info) =>
{
IEnumerable<CPhysicsPropMultiplayer> list = Utilities.FindAllEntitiesByDesignerName<CPhysicsPropMultiplayer>("prop_physics_multiplayer");
if (!list.Any())
{
return HookResult.Continue;
}
SoccerBall = list.First();
if (OnStartTouch == null) (OnStartTouch = new VirtualFunctionVoid<CBaseEntity, CBaseEntity>(SoccerBall, GameData.GetOffset("StartTouch"))).Hook(HookOnStartTouch, HookMode.Pre);
if (OnTouch == null) (OnTouch = new VirtualFunctionVoid<CBaseEntity, CBaseEntity>(SoccerBall, GameData.GetOffset("Touch"))).Hook(HookOnTouch, HookMode.Pre);
if (OnEndTouch == null) (OnEndTouch = new VirtualFunctionVoid<CBaseEntity, CBaseEntity>(SoccerBall, GameData.GetOffset("EndTouch"))).Hook(HookOnEndTouch, HookMode.Pre);
return HookResult.Continue;
});
}
public override void Unload(bool hotReload)
{
OnStartTouch?.Unhook(HookOnStartTouch, HookMode.Pre);
OnTouch?.Unhook(HookOnTouch, HookMode.Pre);
OnEndTouch?.Unhook(HookOnEndTouch, HookMode.Pre);
}
private HookResult HookOnStartTouch(DynamicHook h)
{
CBaseEntity entity = h.GetParam<CBaseEntity>(0);
CBaseEntity otherEntity = h.GetParam<CBaseEntity>(1);
if (entity != SoccerBall && otherEntity != SoccerBall)
return HookResult.Continue;
Console.WriteLine($"[OnStartTouch] {otherEntity.DesignerName} ({otherEntity.Index}) started touching {entity.DesignerName} ({entity.Index})");
return HookResult.Continue;
}
private HookResult HookOnTouch(DynamicHook h)
{
CBaseEntity entity = h.GetParam<CBaseEntity>(0);
CBaseEntity otherEntity = h.GetParam<CBaseEntity>(1);
if (entity != SoccerBall && otherEntity != SoccerBall)
return HookResult.Continue;
Console.WriteLine($"[OnTouch] {otherEntity.DesignerName} ({otherEntity.Index}) is touching {entity.DesignerName} ({entity.Index})");
return HookResult.Continue;
}
private HookResult HookOnEndTouch(DynamicHook h)
{
CBaseEntity entity = h.GetParam<CBaseEntity>(0);
CBaseEntity otherEntity = h.GetParam<CBaseEntity>(1);
if (entity != SoccerBall && otherEntity != SoccerBall)
return HookResult.Continue;
Console.WriteLine($"[OnEndTouch] {otherEntity.DesignerName} ({otherEntity.Index}) is no longer touching {entity.DesignerName} ({entity.Index})");
return HookResult.Continue;
}
Debug output:
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_142
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7f66a15159c0
[Info] - Trampoline - Allocated trampoline at 0x7f6721514000 (using 1 attempts)
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_143
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7f66a1050e60
[Info] - Trampoline - Allocated trampoline at 0x7f672104f000 (using 1 attempts)
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_144
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7f66a1050a40
[Info] - Trampoline - Allocated trampoline at 0x7f66d0e97000 (using 1 attempts)
[OnStartTouch] worldent (0) started touching prop_physics_multiplayer (269)
[OnTouch] worldent (0) is touching prop_physics_multiplayer (269)
[OnTouch] prop_physics_multiplayer (269) is touching worldent (0)
Developers should be aware that they are hooking the virtual function, which is shared between instances (same as with signatures)
@roflmuffin +1 on this
@roflmuffin +1 on this
my second review has to be resolved before this pr is ready
@roflmuffin +1 on this
my second review has to be resolved before this pr is ready
what is second pr? can community help you somehow?
@roflmuffin +1 on this
my second review has to be resolved before this pr is ready
what is second pr? can community help you somehow?
https://github.com/roflmuffin/CounterStrikeSharp/pull/617#discussion_r1793274716
this is the second review
virtual functions that has been created by an offset are now stored as {typeof(TArg1).Name}_{OffsetNum}, for e.g.:
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_142
TArg1 is meant to represent the originating class of the function
@roflmuffin Unsure of the interaction if someone hooks a CBaseEntity and a CCSPlayerPawn with the same offset.
12:40:05 [INFO] (cssharp:Core) Creating function CBaseEntity_142
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7fc5331159c0
[Info] - Trampoline - Allocated trampoline at 0x7fc5b3114000 (using 1 attempts)
12:40:05 [INFO] (cssharp:Core) Creating function CPhysicsPropMultiplayer_142
12:40:05 [INFO] (cssharp:Core) Creating function CBaseEntity_143
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7fc532c50e60
[Info] - Trampoline - Allocated trampoline at 0x7fc5b2c4f000 (using 1 attempts)
12:40:05 [INFO] (cssharp:Core) Creating function CBaseEntity_144
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7fc532c50a40
the hook itself is reused, but currently only the latter is called, I believe this could also happen if the same function is hooked with 2 different signatures? (tested it and yes)
is there any plans to add this to the main branch?
is there any plans to add this to the main branch?
Hi, I plan to finish it in general, just need to find a better way to store these functions as the behaviour I've mentioned in my previous comment is not really viable in my opinion
Looking forward for it
This is fixed by #1143 and released