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;
}