Skip to content

update: add Ray/Hull Trace API (INavPhysicsInterface)#1331

Open
SlynxCZ wants to merge 3 commits into
roflmuffin:mainfrom
SlynxCZ:SlynxCZ/nav_physics_impl
Open

update: add Ray/Hull Trace API (INavPhysicsInterface)#1331
SlynxCZ wants to merge 3 commits into
roflmuffin:mainfrom
SlynxCZ:SlynxCZ/nav_physics_impl

Conversation

@SlynxCZ
Copy link
Copy Markdown
Contributor

@SlynxCZ SlynxCZ commented Jun 2, 2026

Adds a complete ray and hull trace system to CounterStrikeSharp, exposing CS2's CNavPhysicsInterface vtable to C# plugins.

What changed

C++ (src/)

  • src/core/cs2_sdk/interfaces/navphysicsinterface.h/.cpp — adapts the existing INavPhysicsInterface static wrapper to use CSSharp's own modules::server->FindVirtualTable() instead of Source2Toolkit's DynLibUtils
  • src/scripting/natives/natives_raytrace.cpp/.yaml — 6 new natives:
    • TRACE_SHAPE / TRACE_END_SHAPE / TRACE_HULL_SHAPE — write into a caller-provided CSSTraceResult buffer (no heap allocation on C++ side)
    • POINT_CONTENTS — contents bitmask at world position
    • CHECK_AREA_OVERLAPPING_ENTITY — nav area vs entity AABB overlap
    • GET_ENTITY_WORLD_SPACE_AABB — world-space AABB of an entity
  • CTraceFilter is constructed with the entity's owner handle and hierarchy ID (read via schema with static caching), matching CTraceFilterEx behaviour from Source2Toolkit

C# (managed/)

  • Modules/Utils/Trace.cs
    • TraceOptionsInteractsAs, InteractsWith, InteractsExclude
    • TraceResult[StructLayout(Sequential, Pack=1)] unmanaged struct, stack-allocated by each Trace.* method and returned by value; zero heap allocation, zero native calls for field access
    • Trace static class — TraceShape, TraceEndShape, TraceHullShape, PointContents, CheckAreaOverlappingEntity, GetEntityWorldSpaceAABB
  • Modules/Utils/ContentsMask.csContents and Mask static classes with all CONTENTS_* and MASK_* constants from bspflags.h / const.h as const ulong

Usage

// Line trace from player eye position
var result = Trace.TraceShape(player.AbsOrigin!, player.EyeAngles, player);
if (result.DidHit())
    Server.PrintToConsole($"Hit {result.HitEntity().DesignerName} at {result.HitPoint}");

// Hull trace with custom mask
var result = Trace.TraceHullShape(start, end, mins, maxs,
    options: new TraceOptions { InteractsWith = Mask.PlayerSolid });

// AABB
Trace.GetEntityWorldSpaceAABB(entity, out var mins, out var maxs);

// Contents check
bool isSolid = (Trace.PointContents(pos) & Contents.Solid) != 0;

Notes

  • gamedata.json has no new entries required — vtable is found by RTTI name ("CNavPhysicsInterface")
  • CSSTraceResult layout is #pragma pack(1) and must stay in sync with TraceResult's [StructLayout(Sequential, Pack=1)]
  • CTransform / RnCollisionAttr_t are intentionally not exposed on the C# side; opaque pointers (nint) are available via Body(), Shape(), Surface(), Hitbox() for interop

@SlynxCZ SlynxCZ requested a review from roflmuffin as a code owner June 2, 2026 13:35
@roflmuffin
Copy link
Copy Markdown
Owner

roflmuffin commented Jun 2, 2026

I think this looks passable to me. It is a bit annoying with the opaque pointers to the structs we don't have definitions for, but it is better than nothing since CS#1.0 native struct support is terrible. I'll probably write some integration/game tests for this to try it out which may take me some time.

public CEntityInstance HitEntity() => new(m_pEnt);

/// <summary>Native <c>CHitBox*</c> — opaque pointer.</summary>
public nint Hitbox() => m_pHitbox;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth considering naming these something else, like HitboxPtr, just in case we do ever add the proper struct types for these later down the line and then we would have to make a breaking change to the existing property. Don't know what your thoughts are on that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about naming them the way they those members are named as we could use NativeObject and construct them in same way as CEntityInstance wrapper is. So naming would be ideal, otherwise it can be changed right away.

@SlynxCZ
Copy link
Copy Markdown
Contributor Author

SlynxCZ commented Jun 2, 2026

I think this looks passable to me. It is a bit annoying with the opaque pointers to the structs we don't have definitions for, but it is better than nothing since CS#1.0 native struct support is terrible. I'll probably write some integration/game tests for this to try it out which may take me some time.

Thanks, yeah I didn't imlement them as we can use NativeObject or raw unsafe / marshal passing, also I had idea of having raytrace logic in core so we can just bump SDK and its fixed right away,

Comment on lines -1695 to -1714
public static IntPtr GetEconItemSystem(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.SetIdentifier(0x981E9B5B);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return ScriptContext.GlobalScriptContext.GetResultPrimitive<IntPtr>();
}
}

public static bool IsServerPaused(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.SetIdentifier(0xB216AAAC);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return ScriptContext.GlobalScriptContext.GetResultPrimitive<bool>();
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these do still exist in the yaml so next code gen would re add these, what was the point of this removal?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like they've just been moved? I'm assuming the generator may have done this? If not they should probably be moved back

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've regenerated whole API.cs using CodeGen, basically I removed everything and ran CodeGen to have up-to-date methods

@roflmuffin
Copy link
Copy Markdown
Owner

Hey @SlynxCZ have you got any examples of Trace.PointContents(pos) working? Unless I am misunderstood, I am checking a position where a world brush is returning on a trace but it seems to always return an empty contents mask.

/// Returns whether a nav area overlaps with an entity's bounding box.
/// <paramref name="area"/> is an opaque pointer to a <c>CNavArea</c> obtained from the nav system.
/// </summary>
public static bool CheckAreaOverlappingEntity(nint area, CBaseEntity entity, bool extrudeHullHeight = false)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be tempted to not expose this method to the API until we have a way to get access to CNavAreas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants