diff --git a/managed/CounterStrikeSharp.API/Core/Model/CBaseEntity.cs b/managed/CounterStrikeSharp.API/Core/Model/CBaseEntity.cs index 4dfa008d2..7533fc920 100644 --- a/managed/CounterStrikeSharp.API/Core/Model/CBaseEntity.cs +++ b/managed/CounterStrikeSharp.API/Core/Model/CBaseEntity.cs @@ -69,11 +69,17 @@ public void Teleport(Vector3? position = null, Vector3? angles = null, Vector3? } /// Entity is not valid - public void DispatchSpawn() + public void DispatchSpawn(CEntityKeyValues? keyValues) { Guard.IsValidEntity(this); - VirtualFunctions.CBaseEntity_DispatchSpawn(Handle, IntPtr.Zero); + NativeAPI.DispatchSpawn(Handle, keyValues?.Handle ?? IntPtr.Zero); + } + + public void DispatchSpawn() + { + Guard.IsValidEntity(this); + NativeAPI.DispatchSpawn(Handle, IntPtr.Zero); } /// diff --git a/managed/CounterStrikeSharp.API/Core/ScriptContext.cs b/managed/CounterStrikeSharp.API/Core/ScriptContext.cs index af6064f57..f3461e7fc 100644 --- a/managed/CounterStrikeSharp.API/Core/ScriptContext.cs +++ b/managed/CounterStrikeSharp.API/Core/ScriptContext.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Concurrent; using System.Diagnostics; +using System.Drawing; using System.Runtime.InteropServices; using System.Security; using System.Text; @@ -448,6 +449,19 @@ internal unsafe object GetResult(Type type, byte* ptr) return Activator.CreateInstance(type, pointer); } + if (type == typeof(Color)) + { + var pointer = (IntPtr)GetResult(typeof(IntPtr), ptr); + return Marshaling.ColorMarshaler.NativeToManaged(pointer); + } + + // this one only works if the 'Raw'/uint is passed + // maybe do this with a marshaler?! + if (type == typeof(CEntityHandle)) + { + return new CEntityHandle((uint)GetResult(typeof(uint), ptr)); + } + if (type == typeof(object)) { // var dataPtr = *(IntPtr*)&ptr[0]; diff --git a/managed/CounterStrikeSharp.API/Generated/Natives/API.cs b/managed/CounterStrikeSharp.API/Generated/Natives/API.cs index f5743dee8..7ad8032c7 100644 --- a/managed/CounterStrikeSharp.API/Generated/Natives/API.cs +++ b/managed/CounterStrikeSharp.API/Generated/Natives/API.cs @@ -1098,6 +1098,78 @@ public static uint EmitSoundFilter(ulong filtermask, uint ent, string sound, flo } } + public static void DispatchSpawn(IntPtr entity, IntPtr keyvalues){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(entity); + ScriptContext.GlobalScriptContext.Push(keyvalues); + ScriptContext.GlobalScriptContext.SetIdentifier(0xAE01E931); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + + public static IntPtr EntityKeyValuesNew(){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.SetIdentifier(0x445FE212); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + + public static void EntityKeyValuesRelease(IntPtr keyvalues){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(keyvalues); + ScriptContext.GlobalScriptContext.SetIdentifier(0xAE679E87); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + + public static T EntityKeyValuesGetValue(IntPtr keyvalues, string key, uint type){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(keyvalues); + ScriptContext.GlobalScriptContext.Push(key); + ScriptContext.GlobalScriptContext.Push(type); + ScriptContext.GlobalScriptContext.SetIdentifier(0xA9A569AC); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (T)ScriptContext.GlobalScriptContext.GetResult(typeof(T)); + } + } + + public static void EntityKeyValuesSetValue(IntPtr keyvalues, string key, uint type, object[] arguments){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(keyvalues); + ScriptContext.GlobalScriptContext.Push(key); + ScriptContext.GlobalScriptContext.Push(type); + foreach (var obj in arguments) + { + ScriptContext.GlobalScriptContext.Push(obj); + } + ScriptContext.GlobalScriptContext.SetIdentifier(0x60234AB8); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + + public static bool EntityKeyValuesHasValue(IntPtr keyvalues, string key){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(keyvalues); + ScriptContext.GlobalScriptContext.Push(key); + ScriptContext.GlobalScriptContext.SetIdentifier(0xD3E04DA0); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool)); + } + } + public static void HookEvent(string name, InputArgument callback, bool ispost){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); @@ -2117,6 +2189,46 @@ public static IntPtr VectorNew(){ } } + public static IntPtr Vector2dNew(){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.SetIdentifier(0x2CD71169); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + + public static IntPtr Vector4dNew(){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.SetIdentifier(0x16585EAF); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + + public static IntPtr Matrix3x4New(){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.SetIdentifier(0xA2E1A42); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + + public static IntPtr QuaternionNew(){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.SetIdentifier(0xD27D7946); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + public static IntPtr AngleNew(){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/CEntityKeyValues.cs b/managed/CounterStrikeSharp.API/Modules/Utils/CEntityKeyValues.cs new file mode 100644 index 000000000..0ff4e68db --- /dev/null +++ b/managed/CounterStrikeSharp.API/Modules/Utils/CEntityKeyValues.cs @@ -0,0 +1,427 @@ +/* + * This file is part of CounterStrikeSharp. + * CounterStrikeSharp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CounterStrikeSharp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CounterStrikeSharp. If not, see . * + */ + +using System.Drawing; +using System.Numerics; + +namespace CounterStrikeSharp.API.Modules.Utils +{ + /// + /// WARNING: This is intended to be only used with for now! + /// + public enum KeyValuesType : uint + { + TYPE_BOOL, + TYPE_INT, + TYPE_UINT, + TYPE_INT64, + TYPE_UINT64, + TYPE_FLOAT, + TYPE_DOUBLE, + TYPE_STRING, + TYPE_POINTER, + TYPE_STRING_TOKEN, + TYPE_EHANDLE, + TYPE_COLOR, + TYPE_VECTOR, + TYPE_VECTOR2D, + TYPE_VECTOR4D, + TYPE_QUATERNION, + TYPE_QANGLE, + TYPE_MATRIX3X4 + } + + /// + /// WARNING: This is intended to be only used with for now! + /// + public class CEntityKeyValues : NativeObject, IDisposable + { + public CEntityKeyValues() : base(NativeAPI.EntityKeyValuesNew()) + { + } + + public CEntityKeyValues(nint pointer) : base(pointer) + { + } + + public object? this[string key, KeyValuesType type] + { + get + { + return type switch + { + KeyValuesType.TYPE_BOOL => GetBool(key), + KeyValuesType.TYPE_INT => GetInt(key), + KeyValuesType.TYPE_UINT => GetUInt(key), + KeyValuesType.TYPE_INT64 => GetInt64(key), + KeyValuesType.TYPE_UINT64 => GetUInt64(key), + KeyValuesType.TYPE_FLOAT => GetFloat(key), + KeyValuesType.TYPE_DOUBLE => GetDouble(key), + KeyValuesType.TYPE_STRING => GetString(key), + KeyValuesType.TYPE_POINTER => GetPointer(key), + KeyValuesType.TYPE_STRING_TOKEN => GetStringToken(key), + KeyValuesType.TYPE_EHANDLE => GetEHandle(key), + KeyValuesType.TYPE_COLOR => GetColor(key), + KeyValuesType.TYPE_VECTOR => GetVector(key), + KeyValuesType.TYPE_VECTOR2D => GetVector2D(key), + KeyValuesType.TYPE_VECTOR4D => GetVector4D(key), + KeyValuesType.TYPE_QUATERNION => GetQuaternion(key), + KeyValuesType.TYPE_QANGLE => GetAngle(key), + KeyValuesType.TYPE_MATRIX3X4 => GetMatrix3x4(key), + _ => null + }; + } + + set + { +#pragma warning disable CS8605 // Unboxing a possibly null value. +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8604 // Possible null reference argument. + switch (type) + { + case KeyValuesType.TYPE_BOOL: + SetBool(key, (bool)value); + break; + case KeyValuesType.TYPE_INT: + SetInt(key, (int)value); + break; + case KeyValuesType.TYPE_UINT: + SetUInt(key, (uint)value); + break; + case KeyValuesType.TYPE_INT64: + SetInt64(key, (long)value); + break; + case KeyValuesType.TYPE_UINT64: + SetUInt64(key, (ulong)value); + break; + case KeyValuesType.TYPE_FLOAT: + SetFloat(key, (float)value); + break; + case KeyValuesType.TYPE_DOUBLE: + SetDouble(key, (double)value); + break; + case KeyValuesType.TYPE_STRING: + SetString(key, (string)value); + break; + case KeyValuesType.TYPE_POINTER: + SetPointer(key, (nint)value); + break; + case KeyValuesType.TYPE_STRING_TOKEN: + // TODO: use 'CUtlStringToken' once we have it + SetStringToken(key, (uint)value); + break; + case KeyValuesType.TYPE_EHANDLE: + SetEHandle(key, (CEntityHandle)value); + break; + case KeyValuesType.TYPE_COLOR: + SetColor(key, (Color)value); + break; + case KeyValuesType.TYPE_VECTOR: + SetVector(key, (Vector)value); + break; + case KeyValuesType.TYPE_VECTOR2D: + SetVector2D(key, (Vector2D)value); + break; + case KeyValuesType.TYPE_VECTOR4D: + SetVector4D(key, (Vector4D)value); + break; + case KeyValuesType.TYPE_QUATERNION: + SetQuaternion(key, (Quaternion)value); + break; + case KeyValuesType.TYPE_QANGLE: + SetAngle(key, (QAngle)value); + break; + case KeyValuesType.TYPE_MATRIX3X4: + SetMatrix3x4(key, (matrix3x4_t)value); + break; + default: + throw new InvalidOperationException("Unsupported KeyValuesType."); + } +#pragma warning restore CS8605 // Unboxing a possibly null value. +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning restore CS8604 // Possible null reference argument. + } + } + + #region GETTER + + public bool GetBool(string key, bool defaultValue = false) => GetValue(key, KeyValuesType.TYPE_BOOL, defaultValue); + + public int GetInt(string key, int defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_INT, defaultValue); + + public uint GetUInt(string key, uint defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_UINT, defaultValue); + + public long GetInt64(string key, long defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_INT64, defaultValue); + + public ulong GetUInt64(string key, ulong defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_UINT64, defaultValue); + + public float GetFloat(string key, float defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_FLOAT, defaultValue); + + public double GetDouble(string key, double defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_DOUBLE, defaultValue); + + public string? GetString(string key, string defaultValue = "") => GetValue(key, KeyValuesType.TYPE_STRING, defaultValue); + + public nint GetPointer(string key, nint defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_POINTER, defaultValue); + + // TODO: use 'CUtlStringToken' once we have it + public uint GetStringToken(string key, uint defaultValue = 0) => GetValue(key, KeyValuesType.TYPE_STRING_TOKEN, defaultValue); + + public CEntityHandle? GetEHandle(string key, CEntityHandle? defaultValue = null) => + GetValue(key, KeyValuesType.TYPE_EHANDLE, defaultValue); + + public Color GetColor(string key) => GetValue(key, KeyValuesType.TYPE_COLOR, Color.Empty); + + public Vector? GetVector(string key, Vector? defaultValue = null) => + GetValue(key, KeyValuesType.TYPE_VECTOR, defaultValue); + + public Vector2D? GetVector2D(string key, Vector2D? defaultValue = null) => + GetValue(key, KeyValuesType.TYPE_VECTOR2D, defaultValue); + + public Vector4D? GetVector4D(string key, Vector4D? defaultValue = null) => + GetValue(key, KeyValuesType.TYPE_VECTOR4D, defaultValue); + + public Quaternion? GetQuaternion(string key, Quaternion? defaultValue = null) => + GetValue(key, KeyValuesType.TYPE_QUATERNION, defaultValue); + + public QAngle? GetAngle(string key, QAngle? defaultValue = null) => GetValue(key, KeyValuesType.TYPE_QANGLE, defaultValue); + + public matrix3x4_t? GetMatrix3x4(string key, matrix3x4_t? defaultValue = null) => + GetValue(key, KeyValuesType.TYPE_MATRIX3X4, defaultValue); + + #endregion + + #region SETTER + + public void SetBool(string key, bool value) => SetValue(key, KeyValuesType.TYPE_BOOL, value); + + public void SetInt(string key, int value) => SetValue(key, KeyValuesType.TYPE_INT, value); + + public void SetUInt(string key, uint value) => SetValue(key, KeyValuesType.TYPE_UINT, value); + + public void SetInt64(string key, long value) => SetValue(key, KeyValuesType.TYPE_INT64, value); + + public void SetUInt64(string key, ulong value) => SetValue(key, KeyValuesType.TYPE_UINT64, value); + + public void SetFloat(string key, float value) => SetValue(key, KeyValuesType.TYPE_FLOAT, value); + + public void SetDouble(string key, double value) => SetValue(key, KeyValuesType.TYPE_DOUBLE, value); + + public void SetString(string key, string value) => SetValue(key, KeyValuesType.TYPE_STRING, value); + + public void SetPointer(string key, nint value) => SetValue(key, KeyValuesType.TYPE_POINTER, value); + + // TODO: use 'CUtlStringToken' once we have it + public void SetStringToken(string key, uint value) => SetValue(key, KeyValuesType.TYPE_STRING_TOKEN, value); + + public void SetEHandle(string key, CEntityHandle value) => SetValue(key, KeyValuesType.TYPE_EHANDLE, value); + + public void SetColor(string key, Color value) => SetValue(key, KeyValuesType.TYPE_COLOR, value); + + public void SetVector(string key, float x, float y, float z) => + SetValue(key, KeyValuesType.TYPE_VECTOR, new Vector(x, y, z)); + + public void SetVector(string key, Vector vector) => SetValue(key, KeyValuesType.TYPE_VECTOR, vector); + + public void SetVector2D(string key, float x, float y) => SetValue(key, KeyValuesType.TYPE_VECTOR2D, new Vector2D(x, y)); + + public void SetVector2D(string key, Vector2D value) => SetValue(key, KeyValuesType.TYPE_VECTOR2D, value); + + public void SetVector4D(string key, float x, float y, float z, float w) => + SetValue(key, KeyValuesType.TYPE_VECTOR4D, new Vector4D(x, y, z, w)); + + public void SetVector4D(string key, Vector4D value) => SetValue(key, KeyValuesType.TYPE_VECTOR4D, value); + + public void SetQuaternion(string key, float x, float y, float z, float w) => + SetValue(key, KeyValuesType.TYPE_QUATERNION, new Quaternion(x, y, z, w)); + + public void SetQuaternion(string key, Quaternion value) => SetValue(key, KeyValuesType.TYPE_QUATERNION, value); + + public void SetAngle(string key, float pitch, float yaw, float roll) => + SetValue(key, KeyValuesType.TYPE_QANGLE, new QAngle(pitch, yaw, roll)); + + public void SetAngle(string key, QAngle angle) => SetValue(key, KeyValuesType.TYPE_QANGLE, angle); + + public void SetMatrix3x4(string key, matrix3x4_t value) => SetValue(key, KeyValuesType.TYPE_MATRIX3X4, value); + + #endregion + + public bool HasValue(string key) + { + return NativeAPI.EntityKeyValuesHasValue(Handle, key); + } + + internal void SetValue(string key, KeyValuesType type, T value) + { + List arguments = new List(); + + switch (type) + { + case KeyValuesType.TYPE_EHANDLE: + { + if (value is CEntityHandle entityHandle) + { + arguments.Add(entityHandle.Raw); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + case KeyValuesType.TYPE_COLOR: + { + if (value is Color color) + { + arguments.Add(color.R); + arguments.Add(color.G); + arguments.Add(color.B); + arguments.Add(color.A); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + case KeyValuesType.TYPE_VECTOR: + { + if (value is Vector vector) + { + arguments.Add(vector.X); + arguments.Add(vector.Y); + arguments.Add(vector.Z); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + case KeyValuesType.TYPE_VECTOR2D: + { + if (value is Vector2D vector) + { + arguments.Add(vector.X); + arguments.Add(vector.Y); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + case KeyValuesType.TYPE_VECTOR4D: + { + if (value is Vector4D vector) + { + arguments.Add(vector.X); + arguments.Add(vector.Y); + arguments.Add(vector.Z); + arguments.Add(vector.W); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + case KeyValuesType.TYPE_QUATERNION: + { + if (value is Quaternion quaternion) + { + arguments.Add(quaternion.X); + arguments.Add(quaternion.Y); + arguments.Add(quaternion.Z); + arguments.Add(quaternion.W); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + case KeyValuesType.TYPE_QANGLE: + { + if (value is QAngle angle) + { + arguments.Add(angle.X); + arguments.Add(angle.Y); + arguments.Add(angle.Z); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + case KeyValuesType.TYPE_MATRIX3X4: + { + if (value is matrix3x4_t matrix) + { + arguments.Add(matrix.M00); + arguments.Add(matrix.M01); + arguments.Add(matrix.M02); + arguments.Add(matrix.M03); + + arguments.Add(matrix.M10); + arguments.Add(matrix.M11); + arguments.Add(matrix.M12); + arguments.Add(matrix.M13); + + arguments.Add(matrix.M20); + arguments.Add(matrix.M21); + arguments.Add(matrix.M22); + arguments.Add(matrix.M23); + } + else + { + BadTypeHandler(key, type, value); + } + } + break; + + default: + arguments.Add((object)value!); + break; + } + + NativeAPI.EntityKeyValuesSetValue(Handle, key, (uint)type, arguments.ToArray()); + } + + internal T? GetValue(string key, KeyValuesType type, T? defaultValue) + { + return NativeAPI.EntityKeyValuesGetValue(Handle, key, (uint)type) ?? defaultValue; + } + + internal void BadTypeHandler(string key, KeyValuesType type, T value) + { + throw new ArgumentException($"Bad type for EntityKeyValues: got '{typeof(T)}' expected: '{type}'"); + } + + public void Dispose() + { + NativeAPI.EntityKeyValuesRelease(Handle); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs index 84622c298..0a1497831 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs @@ -14,18 +14,38 @@ * along with CounterStrikeSharp. If not, see . * */ -using System; using System.Runtime.CompilerServices; -using CounterStrikeSharp.API.Core; namespace CounterStrikeSharp.API.Modules.Utils { public class Quaternion : NativeObject { + // Not sure who made this one? maybe mark it as 'obsolete' to don't break existing plugins but warn them? + public unsafe ref float Value => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); + + public unsafe ref float X => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); + + public unsafe ref float Y => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 1); + + public unsafe ref float Z => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 2); + + public unsafe ref float W => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 3); + public Quaternion(IntPtr pointer) : base(pointer) { } - public unsafe ref float Value => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); + public Quaternion(float? x = null, float? y = null, float? z = null, float? w = null) : this(NativeAPI.QuaternionNew()) + { + this.X = x ?? 0; + this.Y = y ?? 0; + this.Z = z ?? 0; + this.W = w ?? 0; + } + + public override string ToString() + { + return $"{X:n2} {Y:n2} {Z:n2} {W:n2}"; + } } } \ No newline at end of file diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Vector2D.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Vector2D.cs index c6047451c..e80563015 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Vector2D.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Vector2D.cs @@ -14,19 +14,29 @@ * along with CounterStrikeSharp. If not, see . * */ -using System; using System.Runtime.CompilerServices; -using CounterStrikeSharp.API.Core; namespace CounterStrikeSharp.API.Modules.Utils { public class Vector2D : NativeObject { + public unsafe ref float X => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); + + public unsafe ref float Y => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 1); + public Vector2D(IntPtr pointer) : base(pointer) { } - public unsafe ref float X => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); - public unsafe ref float Y => ref Unsafe.Add(ref *(float*)Handle, 1); + public Vector2D(float? x = null, float? y = null) : this(NativeAPI.Vector2dNew()) + { + this.X = x ?? 0; + this.Y = y ?? 0; + } + + public override string ToString() + { + return $"{X:n2} {Y:n2}"; + } } } \ No newline at end of file diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Vector4D.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Vector4D.cs index 758719773..6a9267f76 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Vector4D.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Vector4D.cs @@ -14,21 +14,35 @@ * along with CounterStrikeSharp. If not, see . * */ -using System; using System.Runtime.CompilerServices; -using CounterStrikeSharp.API.Core; namespace CounterStrikeSharp.API.Modules.Utils { public class Vector4D : NativeObject { + public unsafe ref float X => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); + + public unsafe ref float Y => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 1); + + public unsafe ref float Z => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 2); + + public unsafe ref float W => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 3); + public Vector4D(IntPtr pointer) : base(pointer) { } - public unsafe ref float X => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); - public unsafe ref float Y => ref Unsafe.Add(ref *(float*)Handle, 1); - public unsafe ref float Z => ref Unsafe.Add(ref *(float*)Handle, 2); - public unsafe ref float W => ref Unsafe.Add(ref *(float*)Handle, 3); + public Vector4D(float? x = null, float? y = null, float? z = null, float? w = null) : this(NativeAPI.Vector4dNew()) + { + this.X = x ?? 0; + this.Y = y ?? 0; + this.Z = z ?? 0; + this.W = w ?? 0; + } + + public override string ToString() + { + return $"{X:n2} {Y:n2} {Z:n2} {W:n2}"; + } } } \ No newline at end of file diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs b/managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs index 9b40c5be3..fa11f567c 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs @@ -1,13 +1,79 @@ -using System; +/* + * This file is part of CounterStrikeSharp. + * CounterStrikeSharp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CounterStrikeSharp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CounterStrikeSharp. If not, see . * + */ + using System.Runtime.CompilerServices; namespace CounterStrikeSharp.API.Modules.Utils; public partial class matrix3x4_t : NativeObject { + public unsafe ref float this[int row, int column] => ref Unsafe.Add(ref *(float*)Handle, row * 4 + column); + + public float M00 => this[0, 0]; + + public float M01 => this[0, 1]; + + public float M02 => this[0, 2]; + + public float M03 => this[0, 3]; + + public float M10 => this[1, 0]; + + public float M11 => this[1, 1]; + + public float M12 => this[1, 2]; + + public float M13 => this[1, 3]; + + public float M20 => this[2, 0]; + + public float M21 => this[2, 1]; + + public float M22 => this[2, 2]; + + public float M23 => this[2, 3]; + public matrix3x4_t(IntPtr pointer) : base(pointer) { } - - public unsafe ref float this[int row, int column] => ref Unsafe.Add(ref *(float*)Handle, row * 4 + column); + + public matrix3x4_t(float? m00 = null, float? m01 = null, float? m02 = null, float? m03 = null, + float? m10 = null, float? m11 = null, float? m12 = null, float? m13 = null, + float? m20 = null, float? m21 = null, float? m22 = null, float? m23 = null) : this(NativeAPI.Matrix3x4New()) + { + this[0, 0] = m00 ?? 0; + this[0, 1] = m01 ?? 0; + this[0, 2] = m02 ?? 0; + this[0, 3] = m03 ?? 0; + + this[1, 0] = m10 ?? 0; + this[1, 1] = m11 ?? 0; + this[1, 2] = m12 ?? 0; + this[1, 3] = m13 ?? 0; + + this[2, 0] = m20 ?? 0; + this[2, 1] = m21 ?? 0; + this[2, 2] = m22 ?? 0; + this[2, 3] = m23 ?? 0; + } + + public override string ToString() + { + return $"{this[0, 0]:n2} {this[0, 1]:n2} {this[0, 2]:n2} {this[0, 3]:n2}\n" + + $"{this[1, 0]:n2} {this[1, 1]:n2} {this[1, 2]:n2} {this[1, 3]:n2}\n" + + $"{this[2, 0]:n2} {this[2, 1]:n2} {this[2, 2]:n2} {this[2, 3]:n2}"; + } } \ No newline at end of file diff --git a/managed/CounterStrikeSharp.Tests.Native/EntityKeyValuesTests.cs b/managed/CounterStrikeSharp.Tests.Native/EntityKeyValuesTests.cs new file mode 100644 index 000000000..682962e85 --- /dev/null +++ b/managed/CounterStrikeSharp.Tests.Native/EntityKeyValuesTests.cs @@ -0,0 +1,64 @@ +using System.Drawing; +using System.Threading.Tasks; +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using Xunit; + +public class EntityKeyValuesTests +{ + [Fact] + public async Task CanCreateAndSetValues() + { + using var kv = new CEntityKeyValues(); + kv.SetString("name", "test_entity"); + kv.SetInt("health", 100); + kv.SetFloat("speed", 5.5f); + kv.SetDouble("double", Double.MaxValue); + kv.SetVector("position", new Vector(1.0f, 2.0f, 3.0f)); + kv.SetAngle("view_angle", new QAngle(90.0f, 45.0f, 12.5f)); + kv.SetColor("color", Color.FromArgb(255, 128, 64, 32)); + kv.SetEHandle("owner", new CEntityHandle((uint)12345)); + + Assert.Equal("test_entity", kv.GetString("name")); + Assert.Equal(100, kv.GetInt("health")); + Assert.Equal(5.5f, kv.GetFloat("speed")); + Assert.Equal(Double.MaxValue, kv.GetDouble("double")); + var position = kv.GetVector("position"); + Assert.Equal(position.X, 1.0f); + Assert.Equal(position.Y, 2.0f); + Assert.Equal(position.Z, 3.0f); + + var angle = kv.GetAngle("view_angle"); + Assert.Equal(angle.X, 90.0f); + Assert.Equal(angle.Y, 45.0f); + Assert.Equal(angle.Z, 12.5f); + + Assert.Equal(Color.FromArgb(255, 128, 64, 32), kv.GetColor("color")); + Assert.Equal((uint)12345, kv.GetEHandle("owner").Raw); + } + + [Fact] + public async Task CanSpawnEntityWithKeyValues() + { + var light = Utilities.CreateEntityByName("light_barn")!; + using var kv = new CEntityKeyValues(); + + kv.SetColor("color", Color.BlanchedAlmond); + kv.SetFloat("brightness", 750.0f); + kv.SetBool("enabled", true); + kv.SetVector("size_params", new Vector(60.0f, 120.0f, 0.05f)); + kv.SetInt("directlight", 3); + light.DispatchSpawn(kv); + + Assert.Equal(750.0f, light.Brightness); + Assert.Equal(Color.BlanchedAlmond.ToArgb(), light.Color.ToArgb()); + Assert.True(light.Enabled); + Assert.Equal(60.0f, light.SizeParams.X); + Assert.Equal(120.0f, light.SizeParams.Y); + Assert.Equal(0.05f, light.SizeParams.Z); + Assert.Equal(3, light.DirectLight); + + light.Remove(); + } +} diff --git a/managed/CounterStrikeSharp.Tests.Native/NativeTestsPlugin.cs b/managed/CounterStrikeSharp.Tests.Native/NativeTestsPlugin.cs index 3e59e16ea..99c2c0850 100644 --- a/managed/CounterStrikeSharp.Tests.Native/NativeTestsPlugin.cs +++ b/managed/CounterStrikeSharp.Tests.Native/NativeTestsPlugin.cs @@ -15,12 +15,16 @@ */ using System; -using System.Reflection; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API.Modules.Commands; using Xunit; +using Xunit.Abstractions; namespace NativeTestsPlugin @@ -44,17 +48,38 @@ public override void Load(bool hotReload) AddCommand("css_run_tests", "Runs the xUnit tests for the native plugin.", (player, info) => { RunTests(); }); } - async Task RunTests() + [ConsoleCommand("css_itest")] + public void OnCommandTest(CCSPlayerController? player, CommandInfo command) + { + var filter = command.GetArg(1); + if (string.IsNullOrWhiteSpace(filter)) + { + Server.PrintToConsole("Usage: css_itest "); + Server.PrintToConsole("Example: css_itest MyTestClass"); + Server.PrintToConsole(" css_itest MyTestMethod"); + return; + } + + RunTests(filter); + } + + public async Task RunTests(string? filter = null) { Console.WriteLine("*****************************************************************"); - Console.WriteLine($"[{ModuleName}] Starting xUnit test run..."); + if (!string.IsNullOrWhiteSpace(filter)) + { + Console.WriteLine($"[{ModuleName}] Starting xUnit test run with filter: {filter}"); + } + else + { + Console.WriteLine($"[{ModuleName}] Starting xUnit test run..."); + } + Console.WriteLine("*****************************************************************"); try { using var reporter = new ConsoleTestReporterSink(); - - var project = new XunitProject(); using var controller = new XunitFrontController(AppDomainSupport.IfAvailable, this.ModulePath); var executionOptions = TestFrameworkOptions.ForExecution(); @@ -63,7 +88,46 @@ async Task RunTests() executionOptions.SetSynchronousMessageReporting(true); SynchronizationContext.SetSynchronizationContext(new SourceSynchronizationContext(gameThreadId)); - controller.RunAll(reporter, TestFrameworkOptions.ForDiscovery(), executionOptions); + var discoveryOptions = TestFrameworkOptions.ForDiscovery(); + + if (!string.IsNullOrWhiteSpace(filter)) + { + // Discover all tests first + var discoverySink = new TestDiscoverySink(); + controller.Find(false, discoverySink, discoveryOptions); + discoverySink.Finished.WaitOne(); + + // Filter test cases by class name or method name + var filteredTests = new List(); + foreach (var testCase in discoverySink.TestCases) + { + var testClassName = testCase.TestMethod?.TestClass?.Class?.Name ?? ""; + var testMethodName = testCase.TestMethod?.Method?.Name ?? ""; + var displayName = testCase.DisplayName ?? ""; + + if (testClassName.Contains(filter, StringComparison.OrdinalIgnoreCase) || + testMethodName.Contains(filter, StringComparison.OrdinalIgnoreCase) || + displayName.Contains(filter, StringComparison.OrdinalIgnoreCase)) + { + filteredTests.Add(testCase); + } + } + + if (filteredTests.Count == 0) + { + Console.WriteLine($"[{ModuleName}] No tests matched filter: {filter}"); + return; + } + + Console.WriteLine($"[{ModuleName}] Found {filteredTests.Count} test(s) matching filter."); + + // Run only the filtered tests + controller.RunTests(filteredTests, reporter, executionOptions); + } + else + { + controller.RunAll(reporter, discoveryOptions, executionOptions); + } await reporter.Finished.Task; Console.WriteLine("*****************************************************************"); @@ -90,7 +154,7 @@ public SourceSynchronizationContext(int mainThreadId) _mainThreadId = mainThreadId; } - public override void Post(SendOrPostCallback d, object state) + public override void Post(SendOrPostCallback d, object? state) { Server.NextWorldUpdate(() => d(state)); } @@ -100,4 +164,24 @@ public override SynchronizationContext CreateCopy() return this; } } + + public class TestDiscoverySink : LongLivedMarshalByRefObject, IMessageSink + { + public List TestCases { get; } = new List(); + public ManualResetEvent Finished { get; } = new ManualResetEvent(false); + + public bool OnMessage(IMessageSinkMessage message) + { + if (message is ITestCaseDiscoveryMessage discoveryMessage) + { + TestCases.Add(discoveryMessage.TestCase); + } + else if (message is IDiscoveryCompleteMessage) + { + Finished.Set(); + } + + return true; + } + } } \ No newline at end of file diff --git a/src/core/managers/entity_manager.cpp b/src/core/managers/entity_manager.cpp index c3acca491..ff57fda92 100644 --- a/src/core/managers/entity_manager.cpp +++ b/src/core/managers/entity_manager.cpp @@ -94,6 +94,15 @@ void EntityManager::OnAllInitialized() CSSHARP_CORE_CRITICAL("Failed to find signature for \'CBaseEntity_EmitSoundFilter\'"); } + CBaseEntity_DispatchSpawn = (decltype(CBaseEntity_DispatchSpawn))(( + modules::server->FindSignature(globals::gameConfig->GetSignature("CBaseEntity_DispatchSpawn")))); + + if (!CBaseEntity_DispatchSpawn) + { + CSSHARP_CORE_CRITICAL("Failed to find signature for \'CBaseEntity_DispatchSpawn\'"); + return; + } + auto m_hook = funchook_create(); funchook_prepare(m_hook, (void**)&m_pFireOutputInternal, (void*)&DetourFireOutputInternal); funchook_install(m_hook, 0); diff --git a/src/core/managers/entity_manager.h b/src/core/managers/entity_manager.h index 7a7a18a21..0a7fab06e 100644 --- a/src/core/managers/entity_manager.h +++ b/src/core/managers/entity_manager.h @@ -156,6 +156,8 @@ static void DetourFireOutputInternal(CEntityIOOutput* const pThis, static FireOutputInternal m_pFireOutputInternal = nullptr; +inline void (*CBaseEntity_DispatchSpawn)(void* pEntity, CEntityKeyValues* pKeyValues); + // Do it in here because i didn't found a good place to do this inline void (*CEntityInstance_AcceptInput)(CEntityInstance* pThis, const char* pInputName, diff --git a/src/scripting/natives/natives_entities.cpp b/src/scripting/natives/natives_entities.cpp index b4116c24c..b16e0a942 100644 --- a/src/scripting/natives/natives_entities.cpp +++ b/src/scripting/natives/natives_entities.cpp @@ -24,11 +24,34 @@ #include "core/managers/player_manager.h" #include "core/memory.h" #include "core/recipientfilters.h" +#include "entitykeyvalues.h" #include "scripting/autonative.h" #include "scripting/script_engine.h" namespace counterstrikesharp { +enum KeyValuesType_t : uint8_t +{ + TYPE_BOOL, + TYPE_INT, + TYPE_UINT, + TYPE_INT64, + TYPE_UINT64, + TYPE_FLOAT, + TYPE_DOUBLE, + TYPE_STRING, + TYPE_POINTER, + TYPE_STRING_TOKEN, + TYPE_EHANDLE, + TYPE_COLOR, + TYPE_VECTOR, + TYPE_VECTOR2D, + TYPE_VECTOR4D, + TYPE_QUATERNION, + TYPE_QANGLE, + TYPE_MATRIX3X4 +}; + CEntityInstance* GetEntityFromIndex(ScriptContext& script_context) { if (!globals::entitySystem) @@ -74,6 +97,300 @@ void* GetEntityPointerFromHandle(ScriptContext& scriptContext) return globals::entitySystem->GetEntityInstance(*handle); } +void DispatchSpawn(ScriptContext& scriptContext) +{ + auto entity = scriptContext.GetArgument(0); + auto keyValues = scriptContext.GetArgument(1); + CBaseEntity_DispatchSpawn(entity, keyValues); +} + +CEntityKeyValues* EntityKeyValuesNew(ScriptContext& script_context) +{ + auto* kv = new CEntityKeyValues(); + kv->AddRef(); + return kv; +} + +void EntityKeyValuesRelease(ScriptContext& script_context) +{ + CEntityKeyValues* keyValues = script_context.GetArgument(0); + if (!keyValues) + { + script_context.ThrowNativeError("Invalid KeyValues pointer"); + return; + } + + keyValues->Release(); +} + +bool EntityKeyValuesHasValue(ScriptContext& script_context) +{ + CEntityKeyValues* keyValues = script_context.GetArgument(0); + const char* key = script_context.GetArgument(1); + return keyValues->HasValue(key); +} + +void EntityKeyValuesSetValue(ScriptContext& script_context) +{ + CEntityKeyValues* keyValues = script_context.GetArgument(0); + const char* key = script_context.GetArgument(1); + KeyValuesType_t type = script_context.GetArgument(2); + + int offset = 3; + + switch (type) + { + case counterstrikesharp::TYPE_BOOL: + keyValues->SetBool(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_INT: + keyValues->SetInt(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_UINT: + keyValues->SetUint(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_INT64: + keyValues->SetInt64(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_UINT64: + keyValues->SetUint64(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_FLOAT: + keyValues->SetFloat(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_DOUBLE: + keyValues->SetDouble(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_STRING: + keyValues->SetString(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_POINTER: + keyValues->SetPtr(key, script_context.GetArgument(offset)); + break; + + case counterstrikesharp::TYPE_STRING_TOKEN: + keyValues->SetStringToken(key, CUtlStringToken(script_context.GetArgument(offset))); + break; + + case counterstrikesharp::TYPE_EHANDLE: + keyValues->SetEHandle(key, CEntityHandle(script_context.GetArgument(offset))); + break; + + case counterstrikesharp::TYPE_COLOR: + { + char r = script_context.GetArgument(offset); + char g = script_context.GetArgument(offset + 1); + char b = script_context.GetArgument(offset + 2); + char a = script_context.GetArgument(offset + 3); + keyValues->SetColor(key, Color(r, g, b, a)); + break; + } + + case counterstrikesharp::TYPE_VECTOR: + { + float x = script_context.GetArgument(offset); + float y = script_context.GetArgument(offset + 1); + float z = script_context.GetArgument(offset + 2); + keyValues->SetVector(key, Vector(x, y, z)); + break; + } + + case counterstrikesharp::TYPE_VECTOR2D: + { + float x = script_context.GetArgument(offset); + float y = script_context.GetArgument(offset + 1); + keyValues->SetVector2D(key, Vector2D(x, y)); + break; + } + + case counterstrikesharp::TYPE_VECTOR4D: + { + float x = script_context.GetArgument(offset); + float y = script_context.GetArgument(offset + 1); + float z = script_context.GetArgument(offset + 2); + float w = script_context.GetArgument(offset + 3); + keyValues->SetVector4D(key, Vector4D(x, y, z, w)); + break; + } + + case counterstrikesharp::TYPE_QUATERNION: + { + float x = script_context.GetArgument(offset); + float y = script_context.GetArgument(offset + 1); + float z = script_context.GetArgument(offset + 2); + float w = script_context.GetArgument(offset + 3); + keyValues->SetQuaternion(key, Quaternion(x, y, z, w)); + break; + } + + case counterstrikesharp::TYPE_QANGLE: + { + float x = script_context.GetArgument(offset); + float y = script_context.GetArgument(offset + 1); + float z = script_context.GetArgument(offset + 2); + keyValues->SetQAngle(key, QAngle(x, y, z)); + break; + } + + case counterstrikesharp::TYPE_MATRIX3X4: + { + float m11 = script_context.GetArgument(offset); + float m12 = script_context.GetArgument(offset + 1); + float m13 = script_context.GetArgument(offset + 2); + float m14 = script_context.GetArgument(offset + 3); + + float m21 = script_context.GetArgument(offset + 4); + float m22 = script_context.GetArgument(offset + 5); + float m23 = script_context.GetArgument(offset + 6); + float m24 = script_context.GetArgument(offset + 7); + + float m31 = script_context.GetArgument(offset + 8); + float m32 = script_context.GetArgument(offset + 9); + float m33 = script_context.GetArgument(offset + 10); + float m34 = script_context.GetArgument(offset + 11); + + keyValues->SetMatrix3x4(key, matrix3x4_t(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34)); + break; + } + + default: + script_context.ThrowNativeError("Invalid KeyValues Type! ({})", type); + break; + } +} + +void EntityKeyValuesGetValue(ScriptContext& script_context) +{ + CEntityKeyValues* keyValues = script_context.GetArgument(0); + const char* key = script_context.GetArgument(1); + KeyValuesType_t type = script_context.GetArgument(2); + + switch (type) + { + case counterstrikesharp::TYPE_BOOL: + { + script_context.SetResult(keyValues->GetBool(key)); + } + break; + + case counterstrikesharp::TYPE_INT: + { + script_context.SetResult(keyValues->GetInt(key)); + } + break; + + case counterstrikesharp::TYPE_UINT: + { + script_context.SetResult(keyValues->GetUint(key)); + } + break; + + case counterstrikesharp::TYPE_INT64: + { + script_context.SetResult(keyValues->GetInt64(key)); + } + break; + + case counterstrikesharp::TYPE_UINT64: + { + script_context.SetResult(keyValues->GetUint64(key)); + } + break; + + case counterstrikesharp::TYPE_FLOAT: + { + script_context.SetResult(keyValues->GetFloat(key)); + } + break; + + case counterstrikesharp::TYPE_DOUBLE: + { + script_context.SetResult(keyValues->GetDouble(key)); + } + break; + + case counterstrikesharp::TYPE_STRING: + { + script_context.SetResult(keyValues->GetString(key)); + } + break; + + case counterstrikesharp::TYPE_POINTER: + { + script_context.SetResult(keyValues->GetPtr(key)); + } + break; + + case counterstrikesharp::TYPE_STRING_TOKEN: + { + script_context.SetResult(keyValues->GetStringToken(key).GetHashCode()); + } + break; + + case counterstrikesharp::TYPE_EHANDLE: + { + script_context.SetResult(keyValues->GetEHandle(key)); + } + break; + + case counterstrikesharp::TYPE_COLOR: + { + script_context.SetResult(new Color(keyValues->GetColor(key))); + break; + } + + case counterstrikesharp::TYPE_VECTOR: + { + script_context.SetResult(new Vector(keyValues->GetVector(key))); + break; + } + + case counterstrikesharp::TYPE_VECTOR2D: + { + script_context.SetResult(new Vector2D(keyValues->GetVector2D(key))); + break; + } + + case counterstrikesharp::TYPE_VECTOR4D: + { + script_context.SetResult(new Vector4D(keyValues->GetVector4D(key))); + break; + } + + case counterstrikesharp::TYPE_QUATERNION: + { + script_context.SetResult(new Quaternion(keyValues->GetQuaternion(key))); + break; + } + + case counterstrikesharp::TYPE_QANGLE: + { + script_context.SetResult(new QAngle(keyValues->GetQAngle(key))); + break; + } + + case counterstrikesharp::TYPE_MATRIX3X4: + { + script_context.SetResult(new matrix3x4_t(keyValues->GetMatrix3x4(key))); + break; + } + + default: + { + script_context.ThrowNativeError("Invalid KeyValues Type! ({})", type); + } + break; + } +} + void* GetEntityPointerFromRef(ScriptContext& scriptContext) { if (!globals::entitySystem) @@ -290,6 +607,12 @@ REGISTER_NATIVES(entities, { ScriptEngine::RegisterNativeHandler("UNHOOK_ENTITY_OUTPUT", UnhookEntityOutput); ScriptEngine::RegisterNativeHandler("ACCEPT_INPUT", AcceptInput); ScriptEngine::RegisterNativeHandler("ADD_ENTITY_IO_EVENT", AddEntityIOEvent); + ScriptEngine::RegisterNativeHandler("DISPATCH_SPAWN", DispatchSpawn); + ScriptEngine::RegisterNativeHandler("ENTITY_KEY_VALUES_NEW", EntityKeyValuesNew); + ScriptEngine::RegisterNativeHandler("ENTITY_KEY_VALUES_RELEASE", EntityKeyValuesRelease); + ScriptEngine::RegisterNativeHandler("ENTITY_KEY_VALUES_GET_VALUE", EntityKeyValuesGetValue); + ScriptEngine::RegisterNativeHandler("ENTITY_KEY_VALUES_SET_VALUE", EntityKeyValuesSetValue); + ScriptEngine::RegisterNativeHandler("ENTITY_KEY_VALUES_HAS_VALUE", EntityKeyValuesHasValue); ScriptEngine::RegisterNativeHandler("EMIT_SOUND_FILTER", EmitSoundFilter); }) } // namespace counterstrikesharp diff --git a/src/scripting/natives/natives_entities.yaml b/src/scripting/natives/natives_entities.yaml index 0cb22c196..ec32ca909 100644 --- a/src/scripting/natives/natives_entities.yaml +++ b/src/scripting/natives/natives_entities.yaml @@ -15,3 +15,9 @@ UNHOOK_ENTITY_OUTPUT: classname:string, outputName:string, callback:func, mode:H ACCEPT_INPUT: pThis:pointer, inputName:string, activator:pointer, caller:pointer, value:string, outputID:int -> void ADD_ENTITY_IO_EVENT: pTarget:pointer, inputName:string, activator:pointer, caller:pointer, value:string, delay:float, outputID:int -> void EMIT_SOUND_FILTER: filtermask:uint64, ent:uint, sound:string, volume:float, pitch:float -> uint +DISPATCH_SPAWN: entity:pointer, keyvalues:pointer -> void +ENTITY_KEY_VALUES_NEW: -> pointer +ENTITY_KEY_VALUES_RELEASE : keyvalues:pointer -> void +ENTITY_KEY_VALUES_GET_VALUE: keyvalues:pointer, key:string, type:uint -> any +ENTITY_KEY_VALUES_SET_VALUE: keyvalues:pointer, key:string, type:uint, arguments:object[] -> void +ENTITY_KEY_VALUES_HAS_VALUE: keyvalues:pointer, key:string -> bool \ No newline at end of file diff --git a/src/scripting/natives/natives_vector.cpp b/src/scripting/natives/natives_vector.cpp index 973006a4b..2f5850a2b 100644 --- a/src/scripting/natives/natives_vector.cpp +++ b/src/scripting/natives/natives_vector.cpp @@ -57,6 +57,14 @@ CON_COMMAND(css_dump_leaks, "dump css leaks") Msg("===== Dumping leaks =====\n"); } +Vector2D* Vector2DNew(ScriptContext& script_context) { return new Vector2D(); } + +Vector4D* Vector4DNew(ScriptContext& script_context) { return new Vector4D(); } + +matrix3x4_t* Matrix3x4New(ScriptContext& script_context) { return new matrix3x4_t(); } + +Quaternion* QuaternionNew(ScriptContext& script_context) { return new Quaternion(); } + Vector* VectorNew(ScriptContext& script_context) { auto vec = new Vector(); @@ -99,6 +107,10 @@ void NativeAngleVectors(ScriptContext& script_context) REGISTER_NATIVES(vector, { ScriptEngine::RegisterNativeHandler("VECTOR_NEW", VectorNew); + ScriptEngine::RegisterNativeHandler("VECTOR2D_NEW", Vector2DNew); + ScriptEngine::RegisterNativeHandler("VECTOR4D_NEW", Vector4DNew); + ScriptEngine::RegisterNativeHandler("MATRIX3X4_NEW", Matrix3x4New); + ScriptEngine::RegisterNativeHandler("QUATERNION_NEW", QuaternionNew); ScriptEngine::RegisterNativeHandler("ANGLE_NEW", AngleNew); ScriptEngine::RegisterNativeHandler("VECTOR_SET_X", VectorSetX); diff --git a/src/scripting/natives/natives_vector.yaml b/src/scripting/natives/natives_vector.yaml index e594c773b..d315902cb 100644 --- a/src/scripting/natives/natives_vector.yaml +++ b/src/scripting/natives/natives_vector.yaml @@ -1,4 +1,8 @@ VECTOR_NEW: -> pointer +VECTOR2D_NEW: -> pointer +VECTOR4D_NEW: -> pointer +MATRIX3X4_NEW: -> pointer +QUATERNION_NEW: -> pointer ANGLE_NEW: -> pointer VECTOR_GET_X: vector:pointer -> float VECTOR_GET_Y: vector:pointer -> float