Page 1 of 1

[HL2DM/ANY] Hooking VphysicsCollision

Posted: Thu Oct 01, 2020 6:30 am
by VinciT
I'm working with a bunch of prop_physics entities and I'm trying to check when they hit the world. I settled for the CPhysicsProp::VPhysicsCollision() function, but I've no idea how to recreate the gamevcollisionevent_t struct. All 4 attributes are arrays/lists of 2 - how can I recreate those?

Syntax: Select all

# ../vphysics_collision/vphysics_collision.py

# Source.Python
from core import PLATFORM
from entities.entity import Entity
from memory import Convention, DataType
from memory.hooks import PreHook
from memory.manager import CustomType, TypeManager, Type


manager = TypeManager()


VPHYSICS_COLLISION_OFFSET = 161 if PLATFORM == 'windows' else 162


vphysics_collision = Entity.find('prop_physics').pointer.make_virtual_function(
VPHYSICS_COLLISION_OFFSET,
Convention.THISCALL,
(DataType.POINTER, DataType.INT, DataType.POINTER),
DataType.VOID
)


class GameVCollisionEvent(CustomType, metaclass=manager):
# No idea what Type to use.
pass


# void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
@PreHook(vphysics_collision)
def vphysics_collision_pre(stack_data):
print(stack_data)

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Thu Oct 01, 2020 3:50 pm
by L'In20Cible

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Thu Oct 01, 2020 8:07 pm
by VinciT
Thank you! I managed to get the first three attributes working, but the last one (array of entity pointers) is giving me some trouble.
This is what I have so far:

Syntax: Select all

# ../vphysics_collision/vphysics_collision.py

# Source.Python
from core import PLATFORM
from entities.entity import Entity
from memory import Convention, DataType
from memory.hooks import PreHook
from memory.manager import CustomType, TypeManager, Type


manager = TypeManager()


VPHYSICS_COLLISION_OFFSET = 161 if PLATFORM == 'windows' else 162


vphysics_collision = Entity.find('prop_physics').pointer.make_virtual_function(
VPHYSICS_COLLISION_OFFSET,
Convention.THISCALL,
(DataType.POINTER, DataType.INT, DataType.POINTER),
DataType.VOID
)


class GameVCollisionEvent(CustomType, metaclass=manager):
"""Reconstructed 'gamevcollisionevent_t' struct from the engine."""
pre_velocity = manager.static_instance_array('Vector', 0, 2)
post_velocity = manager.static_instance_array('Vector', 24, 2)
pre_angular_velocity = manager.static_instance_array('Vector', 48, 2)
entity_pointers = manager.static_instance_array(Type.POINTER, 72, 2)


# void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
@PreHook(vphysics_collision)
def vphysics_collision_pre(stack_data):
index = stack_data[1]
event = GameVCollisionEvent._obj(stack_data[2])
entity = Entity._obj(event.entity_pointers[index])

print(entity.classname)

Code: Select all

[SP] Caught an Exception:
Traceback (most recent call last):
  File "..\addons\source-python\plugins\vphysics_collision\vphysics_collision.py", line 40, in vphysics_collision_pre
    entity = Entity._obj(event.entity_pointers[index])
  File "..\addons\source-python\packages\source-python\entities\_base.py", line 293, in _obj
    return cls(index_from_pointer(ptr))

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

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Fri Oct 02, 2020 3:18 am
by L'In20Cible
Tried BaseEntity? It's possible that entity is simply not networked.

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Fri Oct 02, 2020 3:30 am
by VinciT
With BaseEntity I'm either getting:

Code: Select all

[SP] Caught an Exception:
Traceback (most recent call last):
  File "..\addons\source-python\plugins\vphysics_collision\vphysics_collision.py", line 41, in vphysics_collision_pre
    print(base_entity.classname)

AttributeError: 'NoneType' object has no attribute 'classname'
Or:

Code: Select all

[SP] Caught an Exception:
Traceback (most recent call last):
  File "..\addons\source-python\plugins\vphysics_collision\vphysics_collision.py", line 39, in vphysics_collision_pre
    base_entity = BaseEntity._obj(event.entity_pointers[index])

RuntimeError: Access violation - no RTTI data!

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Fri Oct 02, 2020 5:36 am
by Jezza
That's because your offsets are incorrect.

Syntax: Select all

class GameVCollisionEvent(CustomType, metaclass=manager):
manager.register_converter("BaseEntity", BaseEntity._obj)
pre_velocity = manager.static_instance_array("Vector", 32, 2, "Vector preVelocity[2]")
post_velocity = manager.static_instance_array("Vector", 56, 2, "Vector postVelocity[2]")
pre_angular_velocity = manager.static_instance_array("Vector", 80, 2, "AngularImpulse preAngularVelocity[2]")
p_entities = manager.static_pointer_array("BaseEntity", 104, 2, "CBaseEntity *pEntities[2]")

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Fri Oct 02, 2020 6:11 am
by VinciT
It works! Much appreciated Jezza. I'm assuming the offsets start at 32 because of the inherited vcollisionevent_t struct , am I correct?

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Fri Oct 02, 2020 6:24 am
by Ayuto
Exactly! Btw. I don't think you need to register a converter. IIRC, you have to use the C++ class name (CBaseEntity in that case).

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Fri Oct 02, 2020 6:42 am
by VinciT
Cool, I got one more question if you don't mind. :tongue:
Am I terrible at counting or should the starting offset be at 30?

Syntax: Select all

struct vcollisionevent_t
{
IPhysicsObject *pObjects[2]; // 0
int surfaceProps[2]; // 8
bool isCollision; // 16
bool isShadowCollision; // 17
float deltaCollisionTime; // 18

float collisionSpeed; // 22
IPhysicsCollisionData *pInternalData; // 26
};

Ayuto wrote:IIRC, you have to use the C++ class name (CBaseEntity in that case).
Had no idea about converters at all, but I'll probably stick to pointers or register an Entity converter and get that sweet performance from caching.


Edit: Never mind, it appears I'm terrible at counting. Where am I missing those 2 bytes? :confused:

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Fri Oct 02, 2020 7:38 am
by L'In20Cible
It's caused by the 32 bit alignment. Offsets are:

Syntax: Select all

struct vcollisionevent_t
{
IPhysicsObject *pObjects[2]; // 0
int surfaceProps[2]; // 8
bool isCollision; // 16
bool isShadowCollision; // 17
float deltaCollisionTime; // 20

float collisionSpeed; // 24
IPhysicsCollisionData *pInternalData; // 28
};

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Sat Oct 03, 2020 4:30 am
by VinciT
I see, thanks for the info. Guess I'll have to do some reading on memory/data alignment.

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Sat Oct 03, 2020 4:40 am
by Jezza
Ayuto wrote:Exactly! Btw. I don't think you need to register a converter. IIRC, you have to use the C++ class name (CBaseEntity in that case).

I forgot about that, and yes, it's cleaner that way.

VinciT wrote:Cool, I got one more question if you don't mind. :tongue:
Am I terrible at counting or should the starting offset be at 30?

In general, size of bool is 1. But as L'In20Cible wrote, compiler will add padding to improve performance.
In this case, at bool we count 16, 17 and add 2 bytes for padding, and we get an offset value of 32.

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Sat Oct 03, 2020 4:57 am
by Jezza
And using godbolt.org is an easy way to check.

Example: https://godbolt.org/z/4zn6rY

Re: [HL2DM/ANY] Hooking VphysicsCollision

Posted: Sun Oct 04, 2020 11:09 am
by VinciT
Jezza wrote:In general, size of bool is 1. But as L'In20Cible wrote, compiler will add padding to improve performance.
In this case, at bool we count 16, 17 and add 2 bytes for padding, and we get an offset value of 32.
Thank you for the detailed explanation!

Jezza wrote:And using godbolt.org is an easy way to check.

Example: https://godbolt.org/z/4zn6rY
This is going to come in handy. :grin:
Thanks again for all the help guys!