From b5f6067afb1c417c7429fae8460bd6710b6ad7dc Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Wed, 19 Nov 2025 14:29:27 +0800 Subject: [PATCH 1/4] feat: Add CMoveData, CMoveDataBase, SubtickMove and TouchListT structure definitions --- .../Natives/Structs/CMoveData.cs | 36 +++++++++++++++ .../Natives/Structs/CMoveDataBase.cs | 46 +++++++++++++++++++ .../Natives/Structs/SubtickMove.cs | 39 ++++++++++++++++ .../Natives/Structs/TouchListT.cs | 12 +++++ managed/src/TestPlugin/TestPlugin.cs | 22 +++++++-- 5 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveData.cs create mode 100644 managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs create mode 100644 managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs create mode 100644 managed/src/SwiftlyS2.Shared/Natives/Structs/TouchListT.cs diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveData.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveData.cs new file mode 100644 index 000000000..3b81c4c9b --- /dev/null +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveData.cs @@ -0,0 +1,36 @@ +// Reference: https://github.com/KZGlobalTeam/cs2kz-metamod/blob/8d4038394173f1c10d763346d45cd3ccbc0091aa/src/sdk/datatypes.h#L252-L278 + +using System.Runtime.InteropServices; + +namespace SwiftlyS2.Shared.Natives; + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct CMoveData +{ + public CMoveDataBase Base; // class CMoveData : public CMoveDataBase + + public Vector OutWishVel; + public QAngle OldAngles; + + /// + /// World space input vector. Used to compare against the movement services' previous rotation for ground movement stuff. + /// + public Vector InputRotated; + + /// + /// Continuous acceleration in units per second squared (u/s²). + /// + public Vector ContinuousAcceleration; + + /// + /// Immediate delta in u/s. Air acceleration bypasses per second acceleration, + /// applies up to half of its impulse to the velocity and the rest goes straight into this. + /// + public Vector FrameVelocityDelta; + + public float MaxSpeed; + public float ClientMaxSpeed; + public float FrictionDecel; + public bool InAir; + public bool GameCodeMovedPlayer; // true if usercmd cmd number == (m_nGameCodeHasMovedPlayerAfterCommand + 1) +} \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs new file mode 100644 index 000000000..008f1b48a --- /dev/null +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs @@ -0,0 +1,46 @@ +// Reference: https://github.com/KZGlobalTeam/cs2kz-metamod/blob/8d4038394173f1c10d763346d45cd3ccbc0091aa/src/sdk/datatypes.h#L165-L250 + +using System.Runtime.InteropServices; +using SwiftlyS2.Shared.SchemaDefinitions; + +namespace SwiftlyS2.Shared.Natives; + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct CMoveDataBase +{ + // Bitfield members + private byte _bitfield0; + + public bool HasZeroFrametime { + get => (_bitfield0 & 0x01) != 0; + set => _bitfield0 = (byte)(value ? (_bitfield0 | 0x01) : (_bitfield0 & ~0x01)); + } + + public bool IsLateCommand { + get => (_bitfield0 & 0x02) != 0; + set => _bitfield0 = (byte)(value ? (_bitfield0 | 0x02) : (_bitfield0 & ~0x02)); + } + + public CHandle PlayerHandle; + public QAngle AbsViewAngles; + public QAngle ViewAngles; + public Vector LastMovementImpulses; + public float ForwardMove; + public float SideMove; // Warning! Flipped compared to CS:GO, moving right gives negative value + public float UpMove; + public Vector Velocity; + public QAngle Angles; + public Vector Unknown; // Unused. Probably pulled from engine upstream. + public CUtlVector SubtickMoves; + public CUtlVector AttackSubtickMoves; + public bool HasSubtickInputs; + public float UnknownFloat; // Set to 1.0 during SetupMove, never change during gameplay. Is apparently used for weapon services stuff. + public CUtlVector TouchList; + public Vector CollisionNormal; + public Vector GroundNormal; + public Vector AbsOrigin; + public int TickCount; + public int TargetTick; + public float SubtickStartFraction; + public float SubtickEndFraction; +} \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs new file mode 100644 index 000000000..bc3069481 --- /dev/null +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs @@ -0,0 +1,39 @@ +// Reference: https://github.com/KZGlobalTeam/cs2kz-metamod/blob/8d4038394173f1c10d763346d45cd3ccbc0091aa/src/sdk/datatypes.h#L141-L163 + +using System.Runtime.InteropServices; + +namespace SwiftlyS2.Shared.Natives; + +[StructLayout(LayoutKind.Explicit, Size = 0x20, Pack = 1)] +public struct SubtickMove +{ + [FieldOffset(0x00)] + public float When; + + [FieldOffset(0x04)] + public ulong Button; + + // Union: pressed (bool) or analogMove struct (shares same memory at 0x0C) + [FieldOffset(0x0C)] + public bool Pressed; + + [FieldOffset(0x0C)] + public float AnalogForwardDelta; + + [FieldOffset(0x10)] + public float AnalogLeftDelta; + + [FieldOffset(0x14)] + public float PitchDelta; + + [FieldOffset(0x18)] + public float YawDelta; + + [FieldOffset(0x1C)] + private unsafe fixed byte _padding[4]; + + public readonly bool IsAnalogInput() + { + return Button == 0; + } +} \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/TouchListT.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/TouchListT.cs new file mode 100644 index 000000000..29086221c --- /dev/null +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/TouchListT.cs @@ -0,0 +1,12 @@ +// Reference: https://github.com/KZGlobalTeam/cs2kz-metamod/blob/8d4038394173f1c10d763346d45cd3ccbc0091aa/src/sdk/datatypes.h#L135-L139 + +using System.Runtime.InteropServices; + +namespace SwiftlyS2.Shared.Natives; + +[StructLayout(LayoutKind.Sequential)] +public struct TouchListT +{ + public Vector DeltaVelocity; + public CGameTrace Trace; +} \ No newline at end of file diff --git a/managed/src/TestPlugin/TestPlugin.cs b/managed/src/TestPlugin/TestPlugin.cs index 9342ee300..2bac69adf 100644 --- a/managed/src/TestPlugin/TestPlugin.cs +++ b/managed/src/TestPlugin/TestPlugin.cs @@ -632,11 +632,6 @@ public void GetIpCommand( ICommandContext context ) // player.SendMessage(MessageType.Chat, "Button"); // }); - // settingsMenu.Builder.Design.OverrideExitButton("shift"); - // settingsMenu.Builder.Design.OverrideSelectButton("e"); - - // Core.Menus.OpenMenu(player, settingsMenu); - // } [Command("ed")] public void EndRoundCommand( ICommandContext _ ) { @@ -644,6 +639,23 @@ public void EndRoundCommand( ICommandContext _ ) gameRules.TerminateRound(RoundEndReason.CTsWin, 10.0f); } + [Command("sizecheck")] + public void SizeCheckCommand( ICommandContext _ ) + { + unsafe + { + var moveDataSize = sizeof(CMoveData); + var moveDataBaseSize = sizeof(CMoveDataBase); + var subtickMoveSize = sizeof(SubtickMove); + var touchListSize = sizeof(TouchListT); + + Console.WriteLine($"CMoveData size: {moveDataSize} bytes"); + Console.WriteLine($"CMoveDataBase size: {moveDataBaseSize} bytes"); + Console.WriteLine($"SubtickMove size: {subtickMoveSize} bytes"); + Console.WriteLine($"TouchListT size: {touchListSize} bytes"); + } + } + [Command("tm")] public void TestMenuCommand( ICommandContext context ) { From dde6e1d4ec905503f5278c4a0d1c4259ebba0f27 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Wed, 19 Nov 2025 16:27:26 +0800 Subject: [PATCH 2/4] chore: Clean up code --- managed/src/TestPlugin/TestPlugin.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/managed/src/TestPlugin/TestPlugin.cs b/managed/src/TestPlugin/TestPlugin.cs index 2bac69adf..6ef31563b 100644 --- a/managed/src/TestPlugin/TestPlugin.cs +++ b/managed/src/TestPlugin/TestPlugin.cs @@ -632,6 +632,12 @@ public void GetIpCommand( ICommandContext context ) // player.SendMessage(MessageType.Chat, "Button"); // }); + // settingsMenu.Builder.Design.OverrideExitButton("shift"); + // settingsMenu.Builder.Design.OverrideSelectButton("e"); + + // Core.Menus.OpenMenu(player, settingsMenu); + // } + [Command("ed")] public void EndRoundCommand( ICommandContext _ ) { From ba87838dceb8af8637b1c62473ba6cc9ecc71627 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Wed, 19 Nov 2025 16:35:23 +0800 Subject: [PATCH 3/4] chore: Follow coding standards --- .../SwiftlyS2.Shared/Misc/BitFieldHelper.cs | 40 +++++++++---------- .../Natives/Structs/CMoveDataBase.cs | 12 +++--- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/managed/src/SwiftlyS2.Shared/Misc/BitFieldHelper.cs b/managed/src/SwiftlyS2.Shared/Misc/BitFieldHelper.cs index 502e8a0c8..f7962bedd 100644 --- a/managed/src/SwiftlyS2.Shared/Misc/BitFieldHelper.cs +++ b/managed/src/SwiftlyS2.Shared/Misc/BitFieldHelper.cs @@ -1,55 +1,55 @@ -namespace SwiftlyS2.Shared.Misc; +namespace SwiftlyS2.Shared.Misc; -static class BitFieldHelper +internal static class BitFieldHelper { - public static int GetBits(ref byte data, int index, int bitCount) + public static int GetBits( ref byte data, int index, int bitCount ) { if (index < 0 || index + bitCount > 8) throw new ArgumentOutOfRangeException(); - int mask = (1 << bitCount) - 1; + var mask = (1 << bitCount) - 1; return (data >> index) & mask; } - public static void SetBits(ref byte data, int index, int bitCount, int value) + public static void SetBits( ref byte data, int index, int bitCount, int value ) { if (index < 0 || index + bitCount > 8) throw new ArgumentOutOfRangeException(); - int mask = ((1 << bitCount) - 1) << index; + var mask = ((1 << bitCount) - 1) << index; data = (byte)((data & ~mask) | ((value << index) & mask)); } - public static int GetBits(ref int data, int index, int bitCount) + public static int GetBits( ref int data, int index, int bitCount ) { if (index < 0 || index + bitCount > 32) throw new ArgumentOutOfRangeException(); - int mask = (1 << bitCount) - 1; + var mask = (1 << bitCount) - 1; return (data >> index) & mask; } - public static void SetBits(ref int data, int index, int bitCount, int value) + public static void SetBits( ref int data, int index, int bitCount, int value ) { if (index < 0 || index + bitCount > 32) throw new ArgumentOutOfRangeException(); - int mask = ((1 << bitCount) - 1) << index; + var mask = ((1 << bitCount) - 1) << index; data = (data & ~mask) | ((value << index) & mask); } - public static long GetBits(ref long data, int index, int bitCount) + public static long GetBits( ref long data, int index, int bitCount ) { if (index < 0 || index + bitCount > 64) throw new ArgumentOutOfRangeException(); - long mask = (1L << bitCount) - 1; + var mask = (1L << bitCount) - 1; return (data >> index) & mask; } - public static void SetBits(ref long data, int index, int bitCount, long value) + public static void SetBits( ref long data, int index, int bitCount, long value ) { if (index < 0 || index + bitCount > 64) throw new ArgumentOutOfRangeException(); - long mask = ((1L << bitCount) - 1) << index; + var mask = ((1L << bitCount) - 1) << index; data = (data & ~mask) | ((value << index) & mask); } - public static bool GetBit(ref byte data, int index) => GetBits(ref data, index, 1) != 0; - public static void SetBit(ref byte data, int index, bool value) => SetBits(ref data, index, 1, value ? 1 : 0); + public static bool GetBit( ref byte data, int index ) => GetBits(ref data, index, 1) != 0; + public static void SetBit( ref byte data, int index, bool value ) => SetBits(ref data, index, 1, value ? 1 : 0); - public static bool GetBit(ref int data, int index) => GetBits(ref data, index, 1) != 0; - public static void SetBit(ref int data, int index, bool value) => SetBits(ref data, index, 1, value ? 1 : 0); + public static bool GetBit( ref int data, int index ) => GetBits(ref data, index, 1) != 0; + public static void SetBit( ref int data, int index, bool value ) => SetBits(ref data, index, 1, value ? 1 : 0); - public static bool GetBit(ref long data, int index) => GetBits(ref data, index, 1) != 0; - public static void SetBit(ref long data, int index, bool value) => SetBits(ref data, index, 1, value ? 1 : 0); + public static bool GetBit( ref long data, int index ) => GetBits(ref data, index, 1) != 0; + public static void SetBit( ref long data, int index, bool value ) => SetBits(ref data, index, 1, value ? 1 : 0); } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs index 008f1b48a..93a5a0e02 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/CMoveDataBase.cs @@ -1,6 +1,7 @@ // Reference: https://github.com/KZGlobalTeam/cs2kz-metamod/blob/8d4038394173f1c10d763346d45cd3ccbc0091aa/src/sdk/datatypes.h#L165-L250 using System.Runtime.InteropServices; +using SwiftlyS2.Shared.Misc; using SwiftlyS2.Shared.SchemaDefinitions; namespace SwiftlyS2.Shared.Natives; @@ -8,17 +9,16 @@ namespace SwiftlyS2.Shared.Natives; [StructLayout(LayoutKind.Sequential)] public unsafe struct CMoveDataBase { - // Bitfield members - private byte _bitfield0; + private byte _bitfield0; // Bitfield members public bool HasZeroFrametime { - get => (_bitfield0 & 0x01) != 0; - set => _bitfield0 = (byte)(value ? (_bitfield0 | 0x01) : (_bitfield0 & ~0x01)); + get => BitFieldHelper.GetBit(ref _bitfield0, 0); + set => BitFieldHelper.SetBit(ref _bitfield0, 0, value); } public bool IsLateCommand { - get => (_bitfield0 & 0x02) != 0; - set => _bitfield0 = (byte)(value ? (_bitfield0 | 0x02) : (_bitfield0 & ~0x02)); + get => BitFieldHelper.GetBit(ref _bitfield0, 1); + set => BitFieldHelper.SetBit(ref _bitfield0, 1, value); } public CHandle PlayerHandle; From 833123c758266f3e45421ad1c3864a6181b3e13c Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Wed, 19 Nov 2025 17:37:21 +0800 Subject: [PATCH 4/4] fix: Correct SubtickMove memory padding alignment --- .../Natives/Structs/SubtickMove.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs index bc3069481..0df6e5088 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/SubtickMove.cs @@ -11,26 +11,26 @@ public struct SubtickMove public float When; [FieldOffset(0x04)] + private readonly int Padding0; + + [FieldOffset(0x08)] public ulong Button; - // Union: pressed (bool) or analogMove struct (shares same memory at 0x0C) - [FieldOffset(0x0C)] + // Union: pressed (bool) or analogMove struct + [FieldOffset(0x10)] public bool Pressed; - [FieldOffset(0x0C)] - public float AnalogForwardDelta; - [FieldOffset(0x10)] - public float AnalogLeftDelta; + public float AnalogForwardDelta; [FieldOffset(0x14)] - public float PitchDelta; + public float AnalogLeftDelta; [FieldOffset(0x18)] - public float YawDelta; + public float PitchDelta; [FieldOffset(0x1C)] - private unsafe fixed byte _padding[4]; + public float YawDelta; public readonly bool IsAnalogInput() {