diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 57a42c2..e33ae98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,7 +88,7 @@ jobs: with: artifacts: "${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}-${{ env.BUILD_NUMBER }}.zip" name: "CS2-SimpleAdmin - Build ${{ env.BUILD_NUMBER }}" - tag: "build-${{ env.BUILD_NUMBER }}" + tag: "fork-build-${{ env.BUILD_NUMBER }}-${{ github.run_id }}" body: | Place files in addons/counterstrikesharp After first launch, configure the plugins in the respective configs: diff --git a/CS2-SimpleAdmin/Commands/playercommands.cs b/CS2-SimpleAdmin/Commands/playercommands.cs index 3be0b36..dc24e95 100644 --- a/CS2-SimpleAdmin/Commands/playercommands.cs +++ b/CS2-SimpleAdmin/Commands/playercommands.cs @@ -6,6 +6,7 @@ using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Utils; +using FixVectorLeak; namespace CS2_SimpleAdmin; diff --git a/CS2-SimpleAdmin/Events.cs b/CS2-SimpleAdmin/Events.cs index 7253ded..0901771 100644 --- a/CS2-SimpleAdmin/Events.cs +++ b/CS2-SimpleAdmin/Events.cs @@ -13,6 +13,7 @@ using CounterStrikeSharp.API.Core.Translations; using CounterStrikeSharp.API.Modules.Admin; using CounterStrikeSharp.API.Modules.UserMessages; using CounterStrikeSharp.API.ValveConstants.Protobuf; +using FixVectorLeak; namespace CS2_SimpleAdmin; @@ -523,16 +524,16 @@ public partial class CS2_SimpleAdmin if (!PlayersInfo.ContainsKey(player.UserId.Value) || @event.Attacker == null) return HookResult.Continue; - var playerPosition = player.PlayerPawn.Value?.AbsOrigin; - var playerRotation = player.PlayerPawn.Value?.AbsRotation; - + var playerPosition = player.PlayerPawn.Value?.AbsOrigin?.ToVector_t(); + var playerRotation = player.PlayerPawn.Value?.AbsRotation?.ToQAngle_t(); + PlayersInfo[player.UserId.Value].DiePosition = new DiePosition( - new Vector( + new Vector_t( playerPosition?.X ?? 0, playerPosition?.Y ?? 0, playerPosition?.Z ?? 0 ), - new QAngle( + new QAngle_t( playerRotation?.X ?? 0, playerRotation?.Y ?? 0, playerRotation?.Z ?? 0 diff --git a/CS2-SimpleAdmin/Extensions/PlayerExtensions.cs b/CS2-SimpleAdmin/Extensions/PlayerExtensions.cs index 93088af..853845d 100644 --- a/CS2-SimpleAdmin/Extensions/PlayerExtensions.cs +++ b/CS2-SimpleAdmin/Extensions/PlayerExtensions.cs @@ -7,7 +7,7 @@ using CounterStrikeSharp.API.Modules.Memory; using Microsoft.Extensions.Localization; using System.Text; using CounterStrikeSharp.API.Modules.UserMessages; -using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; +using FixVectorLeak; namespace CS2_SimpleAdmin; @@ -87,18 +87,18 @@ public static class PlayerExtensions public static void Bury(this CBasePlayerPawn pawn, float depth = 10f) { - var newPos = new Vector(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y, + var newPos = new Vector_t(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y, pawn.AbsOrigin!.Z - depth); - pawn.Teleport(newPos, pawn.AbsRotation!, pawn.AbsVelocity); + pawn.Teleport(newPos, pawn.AbsRotation!.ToQAngle_t(), pawn.AbsVelocity.ToVector_t()); } public static void Unbury(this CBasePlayerPawn pawn, float depth = 15f) { - var newPos = new Vector(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y, + var newPos = new Vector_t(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y, pawn.AbsOrigin!.Z + depth); - pawn.Teleport(newPos, pawn.AbsRotation!, pawn.AbsVelocity); + pawn.Teleport(newPos, pawn.AbsRotation!.ToQAngle_t(), pawn.AbsVelocity.ToVector_t()); } public static void Freeze(this CBasePlayerPawn pawn) @@ -186,7 +186,7 @@ public static class PlayerExtensions /* Teleport in a random direction - thank you, Mani!*/ /* Thank you AM & al!*/ var random = new Random(); - var vel = new Vector(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z); + var vel = new Vector_t(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z); vel.X += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1); vel.Y += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1); diff --git a/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj b/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj index 97feaf5..d675c66 100644 --- a/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj +++ b/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj @@ -5,6 +5,7 @@ CS2_SimpleAdminApi enable enable + true diff --git a/CS2-SimpleAdminApi/Extensions/FixVectorLeak.cs b/CS2-SimpleAdminApi/Extensions/FixVectorLeak.cs new file mode 100644 index 0000000..0cff58d --- /dev/null +++ b/CS2-SimpleAdminApi/Extensions/FixVectorLeak.cs @@ -0,0 +1,280 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Numerics; +using CounterStrikeSharp.API.Modules.Utils; + +namespace FixVectorLeak; + +public struct Vector_t : IAdditionOperators, + ISubtractionOperators, + IMultiplyOperators, + IDivisionOperators +{ + public float X, Y, Z; + + public const int SIZE = 3; + + public unsafe float this[int i] + { + readonly get + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + return Unsafe.Read(Unsafe.Add(ptr, i)); + } + } + set + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + Unsafe.Write(Unsafe.Add(ptr, i), value); + } + } + } + + public Vector_t() + { + } + + public unsafe Vector_t(nint ptr) : this(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef((void*)ptr), SIZE)) + { + } + + public Vector_t(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + public Vector_t(ReadOnlySpan values) + { + if (values.Length < SIZE) + { + throw new ArgumentOutOfRangeException(nameof(values)); + } + + this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + + public readonly float Length() + { + return (float)Math.Sqrt(X * X + Y * Y + Z * Z); + } + + public readonly float Length2D() + { + return (float)Math.Sqrt(X * X + Y * Y); + } + + public readonly bool IsZero(float tolerance = 0.0001f) + { + return Math.Abs(X) <= tolerance && Math.Abs(Y) <= tolerance && Math.Abs(Z) <= tolerance; + } + + public void Scale(float scale) + { + X *= scale; + Y *= scale; + Z *= scale; + } + + public readonly override string ToString() + { + return $"{X:n2} {Y:n2} {Z:n2}"; + } + + public static Vector_t operator +(Vector_t a, Vector_t b) + { + return new Vector_t(a.X + b.X, a.Y + b.Y, a.Z + b.Z); + } + + public static Vector_t operator -(Vector_t a, Vector_t b) + { + return new Vector_t(a.X - b.X, a.Y - b.Y, a.Z - b.Z); + } + + public static Vector_t operator -(Vector_t a) + { + return new Vector_t(-a.X, -a.Y, -a.Z); + } + + public static Vector_t operator *(Vector_t a, float b) + { + return new Vector_t(a.X * b, a.Y * b, a.Z * b); + } + + public static Vector_t operator /(Vector_t a, float b) + { + return new Vector_t(a.X / b, a.Y / b, a.Z / b); + } +} + +public struct QAngle_t : IAdditionOperators, + ISubtractionOperators, + IMultiplyOperators, + IDivisionOperators +{ + public float X, Y, Z; + + public const int SIZE = 3; + + public unsafe float this[int i] + { + readonly get + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + return Unsafe.Read(Unsafe.Add(ptr, i)); + } + } + set + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + Unsafe.Write(Unsafe.Add(ptr, i), value); + } + } + } + + public QAngle_t() + { + } + + public unsafe QAngle_t(nint ptr) : this(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef((void*)ptr), SIZE)) + { + } + + public QAngle_t(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + public QAngle_t(ReadOnlySpan values) + { + if (values.Length < SIZE) + { + throw new ArgumentOutOfRangeException(nameof(values)); + } + + this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + + public unsafe (Vector_t fwd, Vector_t right, Vector_t up) AngleVectors() + { + Vector_t fwd = default, right = default, up = default; + + nint pFwd = (nint)Unsafe.AsPointer(ref fwd); + nint pRight = (nint)Unsafe.AsPointer(ref right); + nint pUp = (nint)Unsafe.AsPointer(ref up); + + fixed (void* ptr = &this) + { + NativeAPI.AngleVectors((nint)ptr, pFwd, pRight, pUp); + } + + return (fwd, right, up); + } + + public unsafe void AngleVectors(out Vector_t fwd, out Vector_t right, out Vector_t up) + { + fixed (void* ptr = &this, pFwd = &fwd, pRight = &right, pUp = &up) + { + NativeAPI.AngleVectors((nint)ptr, (nint)pFwd, (nint)pRight, (nint)pUp); + } + } + + public readonly override string ToString() + { + return $"{X:n2} {Y:n2} {Z:n2}"; + } + + public static QAngle_t operator +(QAngle_t a, QAngle_t b) + { + return new QAngle_t(a.X + b.X, a.Y + b.Y, a.Z + b.Z); + } + + public static QAngle_t operator -(QAngle_t a, QAngle_t b) + { + return new QAngle_t(a.X - b.X, a.Y - b.Y, a.Z - b.Z); + } + + public static QAngle_t operator -(QAngle_t a) + { + return new QAngle_t(-a.X, -a.Y, -a.Z); + } + + public static QAngle_t operator *(QAngle_t a, float b) + { + return new QAngle_t(a.X * b, a.Y * b, a.Z * b); + } + + public static QAngle_t operator /(QAngle_t a, float b) + { + return new QAngle_t(a.X / b, a.Y / b, a.Z / b); + } +} + +public unsafe static class Extensions +{ + public static void Teleport(this CBaseEntity entity, Vector_t? position = null, QAngle_t? angles = null, Vector_t? velocity = null) + { + Guard.IsValidEntity(entity); + + void* pPos = null, pAng = null, pVel = null; + + // Structs are stored on the stack, GC should not break pointers. + + if (position.HasValue) + { + var pos = position.Value; // Remove nullable wrapper + pPos = &pos; + } + + if (angles.HasValue) + { + var ang = angles.Value; + pAng = ∠ + } + + if (velocity.HasValue) + { + var vel = velocity.Value; + pVel = &vel; + } + + VirtualFunction.CreateVoid(entity.Handle, GameData.GetOffset("CBaseEntity_Teleport"))(entity.Handle, (nint)pPos, + (nint)pAng, (nint)pVel); + } + + public static (Vector_t fwd, Vector_t right, Vector_t up) AngleVectors(this QAngle vec) => vec.ToQAngle_t().AngleVectors(); + public static void AngleVectors(this QAngle vec, out Vector_t fwd, out Vector_t right, out Vector_t up) => vec.ToQAngle_t().AngleVectors(out fwd, out right, out up); + + public static Vector_t ToVector_t(this CounterStrikeSharp.API.Modules.Utils.Vector vec) => new(vec.Handle); + public static QAngle_t ToQAngle_t(this QAngle vec) => new(vec.Handle); +} \ No newline at end of file diff --git a/CS2-SimpleAdminApi/PlayerInfo.cs b/CS2-SimpleAdminApi/PlayerInfo.cs index b492322..71ccc27 100644 --- a/CS2-SimpleAdminApi/PlayerInfo.cs +++ b/CS2-SimpleAdminApi/PlayerInfo.cs @@ -1,5 +1,5 @@ using CounterStrikeSharp.API.Modules.Entities; -using CounterStrikeSharp.API.Modules.Utils; +using FixVectorLeak; namespace CS2_SimpleAdminApi; @@ -30,8 +30,8 @@ public class PlayerInfo( public DiePosition? DiePosition { get; set; } } -public struct DiePosition(Vector position, QAngle angle) +public struct DiePosition(Vector_t position, QAngle_t angle) { - public Vector Position { get; set; } = position; - public QAngle Angle { get; set; } = angle; + public Vector_t Position { get; set; } = position; + public QAngle_t Angle { get; set; } = angle; }