diff --git a/managed/RayTrace/RayTraceApi.cs b/managed/RayTrace/RayTraceApi.cs
new file mode 100644
index 0000000..44ee6b5
--- /dev/null
+++ b/managed/RayTrace/RayTraceApi.cs
@@ -0,0 +1,117 @@
+using System.Numerics;
+using System.Runtime.InteropServices;
+using CounterStrikeSharp.API;
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Modules.Memory;
+using CounterStrikeSharp.API.Modules.Utils;
+using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
+
+namespace RayTraceAPI
+{
+#region Native Structs (matching C++ layout exactly)
+ [Flags]
+ public enum InteractionLayers : ulong
+ {
+ Solid = 0x1,
+ Hitboxes = 0x2,
+ Trigger = 0x4,
+ Sky = 0x8,
+ PlayerClip = 0x10,
+ NPCClip = 0x20,
+ BlockLOS = 0x40,
+ BlockLight = 0x80,
+ Ladder = 0x100,
+ Pickup = 0x200,
+ BlockSound = 0x400,
+ NoDraw = 0x800,
+ Window = 0x1000,
+ PassBullets = 0x2000,
+ WorldGeometry = 0x4000,
+ Water = 0x8000,
+ Slime = 0x10000,
+ TouchAll = 0x20000,
+ Player = 0x40000,
+ NPC = 0x80000,
+ Debris = 0x100000,
+ Physics_Prop = 0x200000,
+ NavIgnore = 0x400000,
+ NavLocalIgnore = 0x800000,
+ PostProcessingVolume = 0x1000000,
+ UnusedLayer3 = 0x2000000,
+ CarriedObject = 0x4000000,
+ PushAway = 0x8000000,
+ ServerEntityOnClient = 0x10000000,
+ CarriedWeapon = 0x20000000,
+ StaticLevel = 0x40000000,
+ csgo_team1 = 0x80000000,
+ csgo_team2 = 0x100000000,
+ csgo_grenadeclip = 0x200000000,
+ csgo_droneclip = 0x400000000,
+ csgo_moveable = 0x800000000,
+ csgo_opaque = 0x1000000000,
+ csgo_monster = 0x2000000000,
+ csgo_thrown_grenade = 0x8000000000,
+
+ MASK_SHOT_PHYSICS = Solid | PlayerClip | Window | PassBullets | Player | NPC | Physics_Prop,
+ MASK_SHOT_HITBOX = Hitboxes | Player | NPC,
+ MASK_SHOT_FULL = MASK_SHOT_PHYSICS | Hitboxes,
+ MASK_WORLD_ONLY = Solid | Window | PassBullets,
+ MASK_GRENADE = Solid | Window | Physics_Prop | PassBullets,
+ MASK_BRUSH_ONLY = Solid | Window,
+ MASK_PLAYER_MOVE = Solid | Window | PlayerClip | PassBullets,
+ MASK_NPC_MOVE = Solid | Window | NPCClip | PassBullets
+ }
+
+ [StructLayout(LayoutKind.Explicit, Size = 32)]
+ public struct TraceOptions
+ {
+ [FieldOffset(0)] public ulong InteractsAs;
+ [FieldOffset(8)] public ulong InteractsWith;
+ [FieldOffset(16)] public ulong InteractsExclude;
+ [FieldOffset(24)] public int DrawBeam;
+
+ public TraceOptions()
+ {
+ InteractsAs = 0;
+ InteractsWith = (ulong)InteractionLayers.MASK_SHOT_PHYSICS;
+ InteractsExclude = 0;
+ DrawBeam = 0;
+ }
+
+ public TraceOptions(InteractionLayers interactsAs, InteractionLayers interactsWith,
+ InteractionLayers interactsExclude = 0, bool drawBeam = false)
+ {
+ InteractsAs = (ulong)interactsAs;
+ InteractsWith = (ulong)interactsWith;
+ InteractsExclude = (ulong)interactsExclude;
+ DrawBeam = drawBeam ? 1 : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit, Size = 48)]
+ public struct TraceResult
+ {
+ [FieldOffset(0)] public float EndPosX;
+ [FieldOffset(4)] public float EndPosY;
+ [FieldOffset(8)] public float EndPosZ;
+ [FieldOffset(16)] public nint HitEntity;
+ [FieldOffset(24)] public float Fraction;
+ [FieldOffset(28)] public int AllSolid;
+ [FieldOffset(32)] public float NormalX;
+ [FieldOffset(36)] public float NormalY;
+ [FieldOffset(40)] public float NormalZ;
+
+ public Vector3 EndPos => new(EndPosX, EndPosY, EndPosZ);
+ public Vector3 Normal => new(NormalX, NormalY, NormalZ);
+ public bool DidHit => Fraction < 1.0f;
+ public bool IsAllSolid => AllSolid != 0;
+ }
+#endregion
+
+ public interface CRayTraceInterface
+ {
+ public unsafe bool TraceShape(Vector origin, QAngle angles, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result);
+ public unsafe bool TraceEndShape(Vector origin, Vector endOrigin, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result);
+ public unsafe bool TraceHullShape(Vector vecStart, Vector vecEnd, Vector hullMins, Vector hullMaxs, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result);
+ }
+}
\ No newline at end of file
diff --git a/managed/RayTrace/RayTraceApi.csproj b/managed/RayTrace/RayTraceApi.csproj
new file mode 100644
index 0000000..dacee6c
--- /dev/null
+++ b/managed/RayTrace/RayTraceApi.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
diff --git a/managed/RayTrace/RayTraceImpl.cs b/managed/RayTrace/RayTraceImpl.cs
new file mode 100644
index 0000000..be25a9e
--- /dev/null
+++ b/managed/RayTrace/RayTraceImpl.cs
@@ -0,0 +1,174 @@
+//
+// Created by Michal Přikryl on 10.10.2025.
+// Copyright (c) 2025 slynxcz. All rights reserved.
+//
+using System.Buffers;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using CounterStrikeSharp.API;
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Core.Attributes.Registration;
+using CounterStrikeSharp.API.Core.Capabilities;
+using CounterStrikeSharp.API.Modules.Commands;
+using CounterStrikeSharp.API.Modules.Memory;
+using CounterStrikeSharp.API.Modules.Utils;
+using RayTraceAPI;
+
+namespace RayTrace;
+
+// ReSharper disable once InconsistentNaming
+// ReSharper disable once UnusedType.Global
+public class RayTraceImpl : BasePlugin
+{
+ public override string ModuleName => "RayTraceImpl";
+ public override string ModuleVersion => "v1.0.0";
+ public override string ModuleAuthor => "Slynx";
+
+ internal static PluginCapability RayTraceInterface { get; } = new("raytrace:craytraceinterface");
+
+ public override void Load(bool hotReload)
+ {
+ Capabilities.RegisterPluginCapability(RayTraceInterface, () => new CRayTrace());
+ RegisterListener(OnMetamodAllPluginsLoaded);
+ }
+
+ public override void Unload(bool hotReload)
+ {
+ RemoveListener(OnMetamodAllPluginsLoaded);
+ }
+
+ private void OnMetamodAllPluginsLoaded()
+ {
+ if (!NativeBridge.Initialize())
+ {
+ Prints.ServerLog("[RayTraceImpl] Native bridge initialization failed.", ConsoleColor.Red);
+ return;
+ }
+ Prints.ServerLog("[RayTraceImpl] Managed side initialized.", ConsoleColor.Green);
+ }
+}
+
+public class CRayTrace : CRayTraceInterface
+{
+ public unsafe bool TraceShape(Vector origin, QAngle angles, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result)
+ {
+ result = default;
+
+ if (!NativeBridge.m_bRayTraceLoaded || NativeBridge.m_pRayTraceHandle == nint.Zero)
+ return false;
+
+ TraceResult resultBuffer = default;
+ TraceOptions optionsBuffer = options;
+
+ bool success = NativeBridge._traceShape!(NativeBridge.m_pRayTraceHandle,
+ origin.Handle,
+ angles.Handle,
+ ignoreEntity?.Handle ?? nint.Zero,
+ (nint)(&optionsBuffer),
+ (nint)(&resultBuffer));
+
+ result = resultBuffer;
+ return success;
+ }
+
+ public unsafe bool TraceEndShape(Vector origin, Vector endOrigin, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result)
+ {
+ result = default;
+
+ if (!NativeBridge.m_bRayTraceLoaded || NativeBridge.m_pRayTraceHandle == nint.Zero)
+ return false;
+
+ TraceResult resultBuffer = default;
+ TraceOptions optionsBuffer = options;
+
+ bool success = NativeBridge._traceEndShape!(NativeBridge.m_pRayTraceHandle,
+ origin.Handle,
+ endOrigin.Handle,
+ ignoreEntity?.Handle ?? nint.Zero,
+ (nint)(&optionsBuffer),
+ (nint)(&resultBuffer));
+
+ result = resultBuffer;
+ return success;
+ }
+
+ public unsafe bool TraceHullShape(Vector vecStart, Vector vecEnd, Vector hullMins, Vector hullMaxs, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result)
+ {
+ result = default;
+
+ if (!NativeBridge.m_bRayTraceLoaded || NativeBridge.m_pRayTraceHandle == nint.Zero)
+ return false;
+
+ TraceResult resultBuffer = default;
+ TraceOptions optionsBuffer = options;
+
+ bool success = NativeBridge._traceHullShape!(NativeBridge.m_pRayTraceHandle,
+ vecStart.Handle,
+ vecEnd.Handle,
+ hullMins.Handle,
+ hullMaxs.Handle,
+ ignoreEntity?.Handle ?? nint.Zero,
+ (nint)(&optionsBuffer),
+ (nint)(&resultBuffer));
+
+ result = resultBuffer;
+ return success;
+ }
+}
+
+public static class NativeBridge
+{
+ public static nint m_pRayTraceHandle = nint.Zero;
+ public static bool m_bRayTraceLoaded = false;
+
+ public static Func? _traceShape;
+ public static Func? _traceEndShape;
+ public static Func? _traceHullShape;
+
+ public static bool Initialize()
+ {
+ m_pRayTraceHandle = (nint)Utilities.MetaFactory("CRayTraceInterface001")!;
+
+ if (m_pRayTraceHandle == nint.Zero)
+ {
+ Prints.ServerLog("[RayTraceImpl] Failed to get Ray-Trace interface handle. Is Ray-Trace MetaMod module loaded?", ConsoleColor.Red);
+ return false;
+ }
+
+ Bind();
+ m_bRayTraceLoaded = true;
+ return true;
+ }
+
+ private static void Bind()
+ {
+ int traceShapeIndex = 2;
+ int traceEndShapeIndex = 3;
+ int traceHullShapeIndex = 4;
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ traceShapeIndex = 1;
+ traceEndShapeIndex = 2;
+ traceHullShapeIndex = 3;
+ }
+
+ _traceShape = VirtualFunction.Create(m_pRayTraceHandle, traceShapeIndex);
+ _traceEndShape = VirtualFunction.Create(m_pRayTraceHandle, traceEndShapeIndex);
+ _traceHullShape = VirtualFunction.Create(m_pRayTraceHandle, traceHullShapeIndex);
+ }
+}
+
+public static class Prints
+{
+ public static void ServerLog(string msg, ConsoleColor color = ConsoleColor.White)
+ {
+ Console.ForegroundColor = color;
+ Console.WriteLine(msg);
+ Console.ResetColor();
+ }
+}
\ No newline at end of file
diff --git a/managed/RayTraceApi/RayTraceApi.csproj b/managed/RayTrace/RayTraceImpl.csproj
similarity index 74%
rename from managed/RayTraceApi/RayTraceApi.csproj
rename to managed/RayTrace/RayTraceImpl.csproj
index f0f5dcc..1360a19 100644
--- a/managed/RayTraceApi/RayTraceApi.csproj
+++ b/managed/RayTrace/RayTraceImpl.csproj
@@ -7,8 +7,15 @@
true
+
+
+ ..\0Harmony.dll
+
+
+
+
diff --git a/managed/RayTraceApi/RayTraceApi.sln b/managed/RayTrace/RayTraceImpl.sln
similarity index 57%
rename from managed/RayTraceApi/RayTraceApi.sln
rename to managed/RayTrace/RayTraceImpl.sln
index 16fe941..b5b4c6c 100644
--- a/managed/RayTraceApi/RayTraceApi.sln
+++ b/managed/RayTrace/RayTraceImpl.sln
@@ -1,6 +1,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RayTraceApi", "RayTraceApi.csproj", "{0D0FD0C6-A1A6-47A7-8370-29CCE2CD4598}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RayTraceImpl", "RayTraceImpl.csproj", "{0D0FD0C6-A1A6-47A7-8370-29CCE2CD4598}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RayTraceApi", "RayTraceApi.csproj", "{13E6E993-1E09-4B9A-9888-98481E1C3178}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -12,5 +14,9 @@ Global
{0D0FD0C6-A1A6-47A7-8370-29CCE2CD4598}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D0FD0C6-A1A6-47A7-8370-29CCE2CD4598}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D0FD0C6-A1A6-47A7-8370-29CCE2CD4598}.Release|Any CPU.Build.0 = Release|Any CPU
+ {13E6E993-1E09-4B9A-9888-98481E1C3178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {13E6E993-1E09-4B9A-9888-98481E1C3178}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {13E6E993-1E09-4B9A-9888-98481E1C3178}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {13E6E993-1E09-4B9A-9888-98481E1C3178}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/managed/RayTraceApi/RayTraceApi.cs b/managed/RayTraceApi/RayTraceApi.cs
deleted file mode 100644
index b2b99b3..0000000
--- a/managed/RayTraceApi/RayTraceApi.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// Created by Michal Přikryl on 11.02.2025.
-// Copyright (c) 2025 slynxcz. All rights reserved.
-//
-using System.Buffers;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Runtime.Loader;
-using System.Numerics;
-using System.Text;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-using CounterStrikeSharp.API;
-using CounterStrikeSharp.API.Core;
-using CounterStrikeSharp.API.Core.Attributes.Registration;
-using CounterStrikeSharp.API.Modules.Commands;
-using CounterStrikeSharp.API.Modules.Memory;
-using CounterStrikeSharp.API.Modules.Utils;
-using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
-
-namespace RayTraceImpl;
-
-// ReSharper disable once InconsistentNaming
-// ReSharper disable once UnusedType.Global
-public class RayTraceImpl : BasePlugin
-{
- public override string ModuleName => "RayTraceImpl";
- public override string ModuleVersion => m_pszVersion;
- public override string ModuleAuthor => "Slynx";
-
- public override void Load(bool hotReload)
- {
- RegisterListener(OnMetamodAllPluginsLoaded);
- }
-
- public override void Unload(bool hotReload)
- {
- RemoveListener(OnMetamodAllPluginsLoaded);
- }
-
- private void OnMetamodAllPluginsLoaded()
- {
-
- }
-
- public static string m_pszVersion { get; private set; } = "";
-}
\ No newline at end of file