From fd3f19ee9c465b3122437e50c2562bff2bc4bae9 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 19:14:29 +0200 Subject: [PATCH 01/35] feat: Added new natives: - VECTOR_DELETE - ANGLE_DELETE - VECTOR_COUNT - ANGLE_COUNT --- managed/CounterStrikeSharp.API/Core/API.cs | 40 +++++++++++ src/scripting/natives/natives_vector.cpp | 81 +++++++++++++++++++++- src/scripting/natives/natives_vector.yaml | 4 ++ 3 files changed, 123 insertions(+), 2 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/API.cs b/managed/CounterStrikeSharp.API/Core/API.cs index 40785f520..2b3ce3a59 100644 --- a/managed/CounterStrikeSharp.API/Core/API.cs +++ b/managed/CounterStrikeSharp.API/Core/API.cs @@ -1645,6 +1645,46 @@ public static IntPtr AngleNew(){ } } + public static void VectorDelete(IntPtr vector){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(vector); + ScriptContext.GlobalScriptContext.SetIdentifier(0x82187C1A); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + + public static void AngleDelete(IntPtr angle){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(angle); + ScriptContext.GlobalScriptContext.SetIdentifier(0xC622DA22); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + + public static int VectorCount(){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.SetIdentifier(0x2A4502C0); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int)); + } + } + + public static int AngleCount(){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.SetIdentifier(0xB7A887F8); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int)); + } + } + public static float VectorGetX(IntPtr vector){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); diff --git a/src/scripting/natives/natives_vector.cpp b/src/scripting/natives/natives_vector.cpp index 4fca493a8..28cd6dd3f 100644 --- a/src/scripting/natives/natives_vector.cpp +++ b/src/scripting/natives/natives_vector.cpp @@ -16,6 +16,7 @@ #include +#include "core/log.h" #include "scripting/autonative.h" #include "scripting/script_engine.h" @@ -35,7 +36,6 @@ CREATE_SETTER_FUNCTION(Vector, float, X, Vector*, obj->x = value); CREATE_SETTER_FUNCTION(Vector, float, Y, Vector*, obj->y = value); CREATE_SETTER_FUNCTION(Vector, float, Z, Vector*, obj->z = value); -// TODO: These need to be cleared out somehow std::vector managed_vectors; Vector* VectorNew(ScriptContext& script_context) @@ -45,7 +45,42 @@ Vector* VectorNew(ScriptContext& script_context) return vec; } -// TODO: These need to be cleared out somehow +int VectorCount(ScriptContext& script_context) +{ + return managed_vectors.size(); +} + +void VectorDel(ScriptContext& script_context) +{ + auto vec = script_context.GetArgument(0); + auto it = std::find(managed_vectors.begin(), managed_vectors.end(), vec); + + if (it != managed_vectors.end()) + { + CSSHARP_CORE_INFO("Releasing Vector: {}", (void*)vec); + managed_vectors.erase(it); + +#ifdef _WIN32 + _try + { + delete *it; + } + _except (EXCEPTION_ACCESS_VIOLATION) + { + script_context.ThrowNativeError("Invalid Vector reference: %p (EXCEPTION_ACCESS_VIOLATION)", vec); + } +#else + delete *it; +#endif + } else + { + if (managed_vectors.size() != 0) + { + script_context.ThrowNativeError("Invalid Vector reference: %p", vec); + } + } +} + std::vector managed_angles; QAngle* AngleNew(ScriptContext& script_context) @@ -55,6 +90,42 @@ QAngle* AngleNew(ScriptContext& script_context) return ang; } +int AngleCount(ScriptContext& script_context) +{ + return managed_angles.size(); +} + +void AngleDel(ScriptContext& script_context) +{ + auto ang = script_context.GetArgument(0); + auto it = std::find(managed_angles.begin(), managed_angles.end(), ang); + + if (it != managed_angles.end()) + { + CSSHARP_CORE_INFO("Releasing QAngle: {}", (void*)ang); + managed_angles.erase(it); + +#ifdef _WIN32 + _try + { + delete *it; + } + _except (EXCEPTION_ACCESS_VIOLATION) + { + script_context.ThrowNativeError("Invalid QAngle reference: %p (EXCEPTION_ACCESS_VIOLATION)", ang); + } +#else + delete *it; +#endif + } else + { + if (managed_angles.size() != 0) + { + script_context.ThrowNativeError("Invalid angle reference: %p", ang); + } + } +} + void NativeVectorAngles(ScriptContext& script_context) { auto vec = script_context.GetArgument(0); @@ -85,6 +156,12 @@ REGISTER_NATIVES(vector, { ScriptEngine::RegisterNativeHandler("VECTOR_NEW", VectorNew); ScriptEngine::RegisterNativeHandler("ANGLE_NEW", AngleNew); + ScriptEngine::RegisterNativeHandler("VECTOR_DELETE", VectorDel); + ScriptEngine::RegisterNativeHandler("ANGLE_DELETE", AngleDel); + + ScriptEngine::RegisterNativeHandler("VECTOR_COUNT", VectorCount); + ScriptEngine::RegisterNativeHandler("ANGLE_COUNT", AngleCount); + ScriptEngine::RegisterNativeHandler("VECTOR_SET_X", VectorSetX); ScriptEngine::RegisterNativeHandler("VECTOR_SET_Y", VectorSetY); ScriptEngine::RegisterNativeHandler("VECTOR_SET_Z", VectorSetZ); diff --git a/src/scripting/natives/natives_vector.yaml b/src/scripting/natives/natives_vector.yaml index e594c773b..07ff43bf2 100644 --- a/src/scripting/natives/natives_vector.yaml +++ b/src/scripting/natives/natives_vector.yaml @@ -1,5 +1,9 @@ VECTOR_NEW: -> pointer ANGLE_NEW: -> pointer +VECTOR_DELETE: vector:pointer -> void +ANGLE_DELETE: angle:pointer -> void +VECTOR_COUNT: -> int +ANGLE_COUNT: -> int VECTOR_GET_X: vector:pointer -> float VECTOR_GET_Y: vector:pointer -> float VECTOR_GET_Z: vector:pointer -> float From 63511589beea5109fc51e5d116a2dbb4b889e340 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 19:19:57 +0200 Subject: [PATCH 02/35] feat: added `MemoryManager` related `CoreConfig` options --- .../counterstrikesharp/configs/core.example.json | 4 +++- managed/CounterStrikeSharp.API/Core/CoreConfig.cs | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/configs/addons/counterstrikesharp/configs/core.example.json b/configs/addons/counterstrikesharp/configs/core.example.json index 97dc9bfc3..282d19e1c 100644 --- a/configs/addons/counterstrikesharp/configs/core.example.json +++ b/configs/addons/counterstrikesharp/configs/core.example.json @@ -6,5 +6,7 @@ "PluginAutoLoadEnabled": true, "ServerLanguage": "en", "UnlockConCommands": true, - "UnlockConVars": true + "UnlockConVars": true, + "EnableMemoryManager": true, + "MemoryManagerInterval": 10000 } \ No newline at end of file diff --git a/managed/CounterStrikeSharp.API/Core/CoreConfig.cs b/managed/CounterStrikeSharp.API/Core/CoreConfig.cs index 7771456bf..e6c8fd567 100644 --- a/managed/CounterStrikeSharp.API/Core/CoreConfig.cs +++ b/managed/CounterStrikeSharp.API/Core/CoreConfig.cs @@ -61,6 +61,12 @@ internal sealed partial class CoreConfigData [JsonPropertyName("UnlockConVars")] public bool UnlockConVars { get; set; } = true; + + [JsonPropertyName("EnableMemoryManager")] + public bool EnableMemoryManager { get; set; } = true; + + [JsonPropertyName("MemoryManagerInterval")] + public int MemoryManagerInterval { get; set; } = 10000; } /// @@ -115,6 +121,15 @@ public partial class CoreConfig public static bool UnlockConVars => _coreConfig.UnlockConVars; + /// + /// Enable the EXPERIMENTAL MemoryManager. + /// + public static bool EnableMemoryManager => _coreConfig.EnableMemoryManager; + + /// + /// The interval (in milliseconds) at which the memory manager checks for leaking references. + /// + public static int MemoryManagerInterval => _coreConfig.MemoryManagerInterval; } public partial class CoreConfig : IStartupService From 421662a1815adf35c25059936c8890aac5fd1ca6 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 19:22:36 +0200 Subject: [PATCH 03/35] feat: `MemoryManager` implementation --- .../Core/Application.cs | 6 +- .../Core/Memory/IMemoryManager.cs | 27 +++ .../Core/Memory/MemoryManager.cs | 177 ++++++++++++++++++ .../CounterStrikeSharp.API.csproj | 46 +++-- .../Modules/Memory/DisposableMemory.cs | 72 +++++++ .../Modules/Memory/IDisposableMemory.cs | 64 +++++++ .../Modules/Utils/Angle.cs | 25 ++- .../Modules/Utils/QAngle.cs | 24 ++- .../Modules/Utils/Vector.cs | 20 +- 9 files changed, 425 insertions(+), 36 deletions(-) create mode 100644 managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs create mode 100644 managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs create mode 100644 managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs create mode 100644 managed/CounterStrikeSharp.API/Modules/Memory/IDisposableMemory.cs diff --git a/managed/CounterStrikeSharp.API/Core/Application.cs b/managed/CounterStrikeSharp.API/Core/Application.cs index bd948014f..4a5cc5e78 100644 --- a/managed/CounterStrikeSharp.API/Core/Application.cs +++ b/managed/CounterStrikeSharp.API/Core/Application.cs @@ -19,6 +19,7 @@ using System.Text; using CounterStrikeSharp.API.Core.Commands; using CounterStrikeSharp.API.Core.Hosting; +using CounterStrikeSharp.API.Core.Memory; using CounterStrikeSharp.API.Core.Plugin; using CounterStrikeSharp.API.Core.Plugin.Host; using CounterStrikeSharp.API.Core.Translations; @@ -48,11 +49,12 @@ public sealed class Application private readonly IPluginContextQueryHandler _pluginContextQueryHandler; private readonly IPlayerLanguageManager _playerLanguageManager; private readonly ICommandManager _commandManager; + private readonly IMemoryManager _memoryManager; public Application(ILoggerFactory loggerFactory, IScriptHostConfiguration scriptHostConfiguration, GameDataProvider gameDataProvider, CoreConfig coreConfig, IPluginManager pluginManager, IPluginContextQueryHandler pluginContextQueryHandler, IPlayerLanguageManager playerLanguageManager, - ICommandManager commandManager) + ICommandManager commandManager, IMemoryManager memoryManager) { Logger = loggerFactory.CreateLogger("Core"); _scriptHostConfiguration = scriptHostConfiguration; @@ -62,6 +64,7 @@ public Application(ILoggerFactory loggerFactory, IScriptHostConfiguration script _pluginContextQueryHandler = pluginContextQueryHandler; _playerLanguageManager = playerLanguageManager; _commandManager = commandManager; + _memoryManager = memoryManager; _instance = this; } @@ -90,6 +93,7 @@ public void Start() RegisterPluginCommands(); _pluginManager.Load(); + _memoryManager.Load(); for (var i = 1; i <= 9; i++) { diff --git a/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs new file mode 100644 index 000000000..e2c9e103c --- /dev/null +++ b/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs @@ -0,0 +1,27 @@ +/* + * 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 . * + */ + +namespace CounterStrikeSharp.API.Core.Memory +{ + public interface IMemoryManager + { + public void Load(); + + public void Start(); + + public void Stop(bool forceStop = false); + } +} diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs new file mode 100644 index 000000000..77794df24 --- /dev/null +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -0,0 +1,177 @@ +/* + * 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.Reflection; +using System.Threading; +using System.Threading.Tasks; + +using CounterStrikeSharp.API.Core.Commands; +using CounterStrikeSharp.API.Modules.Memory; + +using Microsoft.Extensions.Logging; + +namespace CounterStrikeSharp.API.Core.Memory +{ + /// + /// Represents the states. + /// + public enum MemoryManagerState : int + { + /// + /// Waiting in idle state. + /// + Idle, + + /// + /// Currently releasing resources. Normally after this it will enter the idle state if there is nothing else going on. + /// + InProgress, + + /// + /// Something caused to enter this state. ( has to be restarted in order to continue working.) + /// + Stopped, + + /// + /// Halted, once it can continue it will enter the state. + /// + Halted, + + /// + /// We have lost this dude. + /// + Unknown + } + + /// + /// Handles forced garbage collection and + /// + public class MemoryManager : IMemoryManager, IStartupService + { + /// + /// Returns the total amount of released resources since startup. + /// + public int TotalReleased { get; private set; } = 0; + + /// + /// Returns how much resources were released last time. + /// + public int LastReleased { get; private set; } = 0; + + /// + /// Returns how much resources lives currently. + /// + public int CurrentResources => DisposableMemory.Instances; + + /// + /// Last time the run. + /// + public DateTime LastUpdated { get; private set; } = DateTime.UtcNow; + + /// + /// state. + /// + public MemoryManagerState State { get; private set; } = MemoryManagerState.Unknown; + + private readonly ILogger _logger; + + private readonly ICommandManager _commandManager; + + private readonly Thread _thread; + + public MemoryManager(ILogger logger, ICommandManager commandManager) + { + _logger = logger; + _commandManager = commandManager; + + _thread = new Thread(BackgroundThread); + } + + public void Load() + { + if (CoreConfig.EnableMemoryManager) + { + Start(); + } + } + + public void Start() + { + _thread.Start(); + _logger.LogInformation("Service has been started"); + } + + public void Stop(bool forceStop = false) + { + if (forceStop) + { + Task.Run(() => + { + State = MemoryManagerState.Stopped; + + _thread.Join(); + _logger.LogInformation("Service has been stopped"); + }); + } else + { + State = MemoryManagerState.Halted; + _logger.LogInformation("Service has been halted"); + } + } + + private void BackgroundThread() + { + while (State != MemoryManagerState.Stopped) + { + State = MemoryManagerState.Idle; + + Thread.Sleep(CoreConfig.MemoryManagerInterval); + + if (State == MemoryManagerState.Halted) + continue; + + if (State == MemoryManagerState.Stopped) + break; + + int totalCount = CurrentResources; + + if (totalCount == 0) + continue; + + State = MemoryManagerState.InProgress; + + _logger.LogInformation("Releasing {0} disposable memory resources...", totalCount); + + // some may go to gen1 or even gen2, but even those are released when this nondeterministic wonder wants so + GC.Collect(0, GCCollectionMode.Forced, true); + + // this might be obsolete with 'blocking: false'? + GC.WaitForPendingFinalizers(); + + // this part is wrong here as there is a chance that new ones are instantiated during this time + // however, this is not used yet, not even sure it will be as it only serves statistics (TODO: fix) + int totalCountAfter = CurrentResources; + int difference = totalCountAfter - totalCount; + + LastReleased = totalCountAfter == 0 ? totalCount : difference; + TotalReleased += LastReleased; + + _logger.LogInformation("Released {0} disposable memory resources.", LastReleased); + LastUpdated = DateTime.UtcNow; + } + } + } +} diff --git a/managed/CounterStrikeSharp.API/CounterStrikeSharp.API.csproj b/managed/CounterStrikeSharp.API/CounterStrikeSharp.API.csproj index bd39b2bc7..d968451e8 100644 --- a/managed/CounterStrikeSharp.API/CounterStrikeSharp.API.csproj +++ b/managed/CounterStrikeSharp.API/CounterStrikeSharp.API.csproj @@ -23,34 +23,34 @@ .\ApiCompat\v202.dll - - + + - - - - - - - - - - - - + + + + + + + + + + + + - - + + - + - + - + true @@ -58,12 +58,8 @@ true - - + + diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs new file mode 100644 index 000000000..e8498a061 --- /dev/null +++ b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs @@ -0,0 +1,72 @@ +/* + * 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 . * + */ + +namespace CounterStrikeSharp.API.Modules.Memory +{ + public class DisposableMemory : NativeObject, IDisposableMemory + { + internal static int _instances; + + internal static int Instances + { + get { return _instances; } + set + { + _instances = value; + + // Should not happen? + if (_instances < 0) + { + _instances = 0; + } + } + } + + private bool _disposed; + + public bool IsDisposed + { + get => _disposed; + set => _disposed = value; + } + + public DisposableMemory(IntPtr ptr) : base(ptr) + { + Instances++; + } + + ~DisposableMemory() + { + (this as IDisposableMemory).DisposeInternal(); + Instances--; + } + + public virtual void Dispose() + { + // Dont call finalizer + GC.SuppressFinalize(this); + + (this as IDisposableMemory).DisposeInternal(); + Instances--; + } + + public virtual void ReleaseManaged() + { } + + public virtual void ReleaseUnmanaged() + { } + } +} diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/IDisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/IDisposableMemory.cs new file mode 100644 index 000000000..693ac8872 --- /dev/null +++ b/managed/CounterStrikeSharp.API/Modules/Memory/IDisposableMemory.cs @@ -0,0 +1,64 @@ +/* + * 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 . * + */ + +namespace CounterStrikeSharp.API.Modules.Memory +{ + /// + /// Provides mechanism to release managed and unmanaged memory. + /// Used for garbage collecting. + /// + public interface IDisposableMemory : IDisposable + { + /// + /// Is the resource disposed? + /// The class should implement a disposed field. + /// + public bool IsDisposed { get; set; } + + /// + /// Clean up unmanaged resources inside this. + /// This is always called on the main thread, its safe to use natives. + /// USED INTERNALLY BY + /// + public void ReleaseUnmanaged(); + + /// + /// Acts as the normal managed 'Dispose'. + /// USED INTERNALLY BY + /// + public void ReleaseManaged(); + + /// + /// Dispose internally + /// + internal void DisposeInternal() + { + if (!IsDisposed) + { + // Free managed resources + ReleaseManaged(); + + Server.NextWorldUpdate(() => + { + // Free unmanaged resources on the main thread + ReleaseUnmanaged(); + }); + + IsDisposed = true; + } + } + } +} diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs index 69eb7ce72..199201eee 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs @@ -14,6 +14,8 @@ * along with CounterStrikeSharp. If not, see . * */ +using CounterStrikeSharp.API.Modules.Memory; + namespace CounterStrikeSharp.API.Modules.Utils { /// @@ -25,10 +27,10 @@ namespace CounterStrikeSharp.API.Modules.Utils /// Zroll +right/-left /// /// - public class Angle : NativeObject + public class Angle : DisposableMemory { public static readonly Angle Zero = new(); - + public Angle(IntPtr pointer) : base(pointer) { } @@ -368,10 +370,25 @@ public override bool Equals(object obj) protected override void OnDispose() { }*/ - + + public override void ReleaseUnmanaged() + { + NativeAPI.AngleDelete(Handle); + } + + /// + /// Returns the total amount of instances. + /// + /// + public static int GetTotalCount() + { + // we only return 0 currently because this class is the same as 'QAngle' and this would mess up in 'MemoryManager'. + return 0; + } + public override string ToString() { return $"{X:n2} {Y:n2} {Z:n2}"; } } -} \ No newline at end of file +} diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs b/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs index e3a022083..f9556ad4c 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs @@ -16,12 +16,14 @@ using System.Runtime.CompilerServices; +using CounterStrikeSharp.API.Modules.Memory; + namespace CounterStrikeSharp.API.Modules.Utils { - public class QAngle : NativeObject + public class QAngle : DisposableMemory { public static readonly QAngle Zero = new(); - + public QAngle(IntPtr pointer) : base(pointer) { } @@ -36,10 +38,24 @@ public QAngle(float? x = null, float? y = null, float? z = null) : this(NativeAP 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 override void ReleaseUnmanaged() + { + NativeAPI.AngleDelete(Handle); + } + + /// + /// Returns the total amount of instances. + /// + /// + public static int GetTotalCount() + { + return NativeAPI.AngleCount(); + } + public override string ToString() { return $"{X:n2} {Y:n2} {Z:n2}"; } } -} \ No newline at end of file +} diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs index 7dfe83990..3cf1f1566 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -17,6 +17,8 @@ using System.Numerics; using System.Runtime.CompilerServices; +using CounterStrikeSharp.API.Modules.Memory; + namespace CounterStrikeSharp.API.Modules.Utils { /// @@ -28,7 +30,7 @@ namespace CounterStrikeSharp.API.Modules.Utils /// Z+up/-down /// /// - public class Vector : NativeObject, + public class Vector : DisposableMemory, IAdditionOperators, ISubtractionOperators, IMultiplyOperators, @@ -370,6 +372,20 @@ public override bool Equals(object obj) */ + public override void ReleaseUnmanaged() + { + NativeAPI.VectorDelete(Handle); + } + + /// + /// Returns the total amount of instances. + /// + /// + public static int GetTotalCount() + { + return NativeAPI.VectorCount(); + } + public override string ToString() { return $"{X:n2} {Y:n2} {Z:n2}"; From 50741aaac750b556fc7bf1bcb00b223ef241257f Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 19:53:56 +0200 Subject: [PATCH 04/35] tweak: reordered --- .../CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 77794df24..b28ea1619 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -14,7 +14,6 @@ * along with CounterStrikeSharp. If not, see . * */ -using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -156,7 +155,7 @@ private void BackgroundThread() _logger.LogInformation("Releasing {0} disposable memory resources...", totalCount); // some may go to gen1 or even gen2, but even those are released when this nondeterministic wonder wants so - GC.Collect(0, GCCollectionMode.Forced, true); + GC.Collect(0, GCCollectionMode.Default, true); // this might be obsolete with 'blocking: false'? GC.WaitForPendingFinalizers(); @@ -168,9 +167,9 @@ private void BackgroundThread() LastReleased = totalCountAfter == 0 ? totalCount : difference; TotalReleased += LastReleased; - - _logger.LogInformation("Released {0} disposable memory resources.", LastReleased); LastUpdated = DateTime.UtcNow; + + _logger.LogInformation("Released {0} disposable memory resources. ({1} remains)", LastReleased, CurrentResources); } } } From c8b17afa71b54f401d8ad59636a3ad1287eb27f9 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 19:54:43 +0200 Subject: [PATCH 05/35] fix: call `MemAlloc_Free` instead of `delete` --- src/scripting/natives/natives_vector.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/scripting/natives/natives_vector.cpp b/src/scripting/natives/natives_vector.cpp index 28cd6dd3f..b7e7ca9de 100644 --- a/src/scripting/natives/natives_vector.cpp +++ b/src/scripting/natives/natives_vector.cpp @@ -63,14 +63,16 @@ void VectorDel(ScriptContext& script_context) #ifdef _WIN32 _try { - delete *it; + vec->~Vector(); + MemAlloc_Free(vec); } _except (EXCEPTION_ACCESS_VIOLATION) { script_context.ThrowNativeError("Invalid Vector reference: %p (EXCEPTION_ACCESS_VIOLATION)", vec); } #else - delete *it; + vec->~Vector(); + MemAlloc_Free(vec); #endif } else { @@ -108,14 +110,16 @@ void AngleDel(ScriptContext& script_context) #ifdef _WIN32 _try { - delete *it; + ang->~QAngle(); + MemAlloc_Free(ang); } _except (EXCEPTION_ACCESS_VIOLATION) { script_context.ThrowNativeError("Invalid QAngle reference: %p (EXCEPTION_ACCESS_VIOLATION)", ang); } #else - delete *it; + ang->~QAngle(); + MemAlloc_Free(ang); #endif } else { From 5b4849e0cb5019a692efe96e345e1763d7b498a3 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 20:02:03 +0200 Subject: [PATCH 06/35] tweak: now returns the correct value --- managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs index 199201eee..fa7f45179 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs @@ -382,8 +382,7 @@ public override void ReleaseUnmanaged() /// public static int GetTotalCount() { - // we only return 0 currently because this class is the same as 'QAngle' and this would mess up in 'MemoryManager'. - return 0; + return NativeAPI.AngleCount(); } public override string ToString() From 09a83a10b246518faa86170c002845f2b487d657 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 20:15:00 +0200 Subject: [PATCH 07/35] tweak: adjustments --- .../CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index b28ea1619..7ad24e0f4 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -152,7 +152,7 @@ private void BackgroundThread() State = MemoryManagerState.InProgress; - _logger.LogInformation("Releasing {0} disposable memory resources...", totalCount); + _logger.LogInformation("Running garbage collector ({0} disposable memory in total)", totalCount); // some may go to gen1 or even gen2, but even those are released when this nondeterministic wonder wants so GC.Collect(0, GCCollectionMode.Default, true); @@ -163,13 +163,16 @@ private void BackgroundThread() // this part is wrong here as there is a chance that new ones are instantiated during this time // however, this is not used yet, not even sure it will be as it only serves statistics (TODO: fix) int totalCountAfter = CurrentResources; - int difference = totalCountAfter - totalCount; + int difference = Math.Abs(totalCountAfter - totalCount); LastReleased = totalCountAfter == 0 ? totalCount : difference; TotalReleased += LastReleased; LastUpdated = DateTime.UtcNow; - _logger.LogInformation("Released {0} disposable memory resources. ({1} remains)", LastReleased, CurrentResources); + if (LastReleased > 0) + { + _logger.LogInformation("Released {0} leaking memory resources. ({1} remains)", LastReleased, CurrentResources); + } } } } From 06998d9d5870639450c2a37cabff121d5763e909 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 20:18:34 +0200 Subject: [PATCH 08/35] feat: calculate GC speed --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 7ad24e0f4..831432a7d 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -153,6 +153,7 @@ private void BackgroundThread() State = MemoryManagerState.InProgress; _logger.LogInformation("Running garbage collector ({0} disposable memory in total)", totalCount); + DateTime startTime = DateTime.UtcNow; // some may go to gen1 or even gen2, but even those are released when this nondeterministic wonder wants so GC.Collect(0, GCCollectionMode.Default, true); @@ -171,7 +172,7 @@ private void BackgroundThread() if (LastReleased > 0) { - _logger.LogInformation("Released {0} leaking memory resources. ({1} remains)", LastReleased, CurrentResources); + _logger.LogInformation("Released {0} leaking memory resources in {1}ms ({2} remains)", LastReleased, (LastUpdated - startTime).TotalMilliseconds, CurrentResources); } } } From 42338caa7bc3745e3a5fc77d01f44b52055042f5 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 20:20:04 +0200 Subject: [PATCH 09/35] feat: sleep thread if 0 was released --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 831432a7d..4055c1e6a 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -173,6 +173,9 @@ private void BackgroundThread() if (LastReleased > 0) { _logger.LogInformation("Released {0} leaking memory resources in {1}ms ({2} remains)", LastReleased, (LastUpdated - startTime).TotalMilliseconds, CurrentResources); + } else + { + Thread.Sleep(CoreConfig.MemoryManagerInterval); } } } From 3b09a85bd1ce73c7013c1878a8ac35a1b5aceee9 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 20:32:20 +0200 Subject: [PATCH 10/35] change `TotalReleased` type to `ulong` --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 4055c1e6a..c12309a7b 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -63,7 +63,7 @@ public class MemoryManager : IMemoryManager, IStartupService /// /// Returns the total amount of released resources since startup. /// - public int TotalReleased { get; private set; } = 0; + public ulong TotalReleased { get; private set; } = 0; /// /// Returns how much resources were released last time. @@ -167,7 +167,7 @@ private void BackgroundThread() int difference = Math.Abs(totalCountAfter - totalCount); LastReleased = totalCountAfter == 0 ? totalCount : difference; - TotalReleased += LastReleased; + TotalReleased += (ulong)LastReleased; LastUpdated = DateTime.UtcNow; if (LastReleased > 0) From 3b7f75a26e6b437bd6ec6a6be04c9d75b60d35b2 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 21:29:35 +0200 Subject: [PATCH 11/35] feat: commands and adjustments --- .../Core/Memory/MemoryManager.cs | 137 ++++++++++++++++-- 1 file changed, 124 insertions(+), 13 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index c12309a7b..9511075c9 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -18,6 +18,8 @@ using System.Threading.Tasks; using CounterStrikeSharp.API.Core.Commands; +using CounterStrikeSharp.API.Modules.Admin; +using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Memory; using Microsoft.Extensions.Logging; @@ -35,7 +37,7 @@ public enum MemoryManagerState : int Idle, /// - /// Currently releasing resources. Normally after this it will enter the idle state if there is nothing else going on. + /// Currently releasing resources. Normally after this it will enter if there is nothing else going on. /// InProgress, @@ -45,7 +47,7 @@ public enum MemoryManagerState : int Stopped, /// - /// Halted, once it can continue it will enter the state. + /// Halted, once it can continue it will enter the or state. /// Halted, @@ -89,7 +91,7 @@ public class MemoryManager : IMemoryManager, IStartupService private readonly ICommandManager _commandManager; - private readonly Thread _thread; + private Thread _thread; public MemoryManager(ILogger logger, ICommandManager commandManager) { @@ -99,24 +101,108 @@ public MemoryManager(ILogger logger, ICommandManager commandManag _thread = new Thread(BackgroundThread); } - public void Load() + [RequiresPermissions("@css/generic")] + private void OnCommand(CCSPlayerController? caller, CommandInfo info) { - if (CoreConfig.EnableMemoryManager) + switch (info.GetArg(1)) { - Start(); + case "stats": + { + PrintStatistics(info); + } break; + + case "start": + { + Start(); + } break; + + case "stop": + { + Stop(true); + } break; + + case "pause": + { + Stop(false); + } break; + + case "resume": + { + Resume(); + } break; + + default: + { + info.ReplyToCommand("Valid usage: css_memorymanager [option]\n" + + " stats - Print garbage collector statistics.\n" + + " start - Starts the memory manager that handles leaking resources.\n" + + " stop - Stops the memory manager.\n" + + " pause - Stops the memory manager.\n" + + " resume - Resumes the memory manager.\n"); + } break; } } + public void Load() + { + _commandManager.RegisterCommand(new("css_memorymanager", "Counter-Strike Sharp Memory Manager options.", + OnCommand) + { + ExecutableBy = CommandUsage.CLIENT_AND_SERVER, + MinArgs = 1, + UsageHint = "[option]\n" + + " stats - Print garbage collector statistics.\n" + + " start - Starts the memory manager that handles leaking resources.\n" + + " stop - Stops the memory manager.\n" + + " pause - Stops the memory manager.\n" + + " resume - Resumes the memory manager.\n", + }); + + Start(); + } + + public void PrintStatistics(CommandInfo info) + { + info.ReplyToCommand("Memory Manager Statistics:\n" + + $"State: {State}\n" + + $"Total Released: {TotalReleased}\n" + + $"Last Released: {LastReleased}\n" + + $"Current Resources: {CurrentResources}\n" + + $"Last Updated: {LastUpdated.ToString("yyyy.MM.dd hh:mm:ss tt")} (UTC)"); + } + public void Start() { - _thread.Start(); - _logger.LogInformation("Service has been started"); + if (CoreConfig.EnableMemoryManager) + { + _logger.LogInformation("Starting service..."); + + try + { + _thread.Start(); + } catch (ThreadStateException) + { + _thread = new Thread(BackgroundThread); + _thread.Start(); + } catch (Exception e) + { + _logger.LogCritical("Exception occured: '{0}' ({1})", e.Message, e); + } + } } public void Stop(bool forceStop = false) { if (forceStop) { + if (State == MemoryManagerState.Stopped) + { + _logger.LogInformation("Service is already stopped"); + return; + } + + _logger.LogInformation("Stopping service... (might take a while)"); + Task.Run(() => { State = MemoryManagerState.Stopped; @@ -126,25 +212,44 @@ public void Stop(bool forceStop = false) }); } else { + if (State == MemoryManagerState.Halted) + { + _logger.LogInformation("Service is already paused"); + return; + } + State = MemoryManagerState.Halted; _logger.LogInformation("Service has been halted"); } } + public void Resume() + { + if (State != MemoryManagerState.Halted) + { + _logger.LogWarning("Unable to resume service (state: {0})", State); + return; + } + + State = MemoryManagerState.Idle; + _logger.LogInformation("Service has been resumed"); + } + private void BackgroundThread() { + _logger.LogInformation("Service has been started"); + State = MemoryManagerState.Idle; + while (State != MemoryManagerState.Stopped) { - State = MemoryManagerState.Idle; - Thread.Sleep(CoreConfig.MemoryManagerInterval); - if (State == MemoryManagerState.Halted) - continue; - if (State == MemoryManagerState.Stopped) break; + if (State == MemoryManagerState.Halted) + continue; + int totalCount = CurrentResources; if (totalCount == 0) @@ -169,6 +274,7 @@ private void BackgroundThread() LastReleased = totalCountAfter == 0 ? totalCount : difference; TotalReleased += (ulong)LastReleased; LastUpdated = DateTime.UtcNow; + State = MemoryManagerState.Idle; if (LastReleased > 0) { @@ -177,6 +283,11 @@ private void BackgroundThread() { Thread.Sleep(CoreConfig.MemoryManagerInterval); } + + if (State == MemoryManagerState.InProgress) + { + State = MemoryManagerState.Idle; + } } } } From c943cd2d8bd6c6d43f188a079ea65ef3724978a8 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 21:36:14 +0200 Subject: [PATCH 12/35] tweak: add error message --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 9511075c9..5ab7ba012 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -188,6 +188,9 @@ public void Start() { _logger.LogCritical("Exception occured: '{0}' ({1})", e.Message, e); } + } else + { + _logger.LogError("Unable to start 'MemoryManager' with CoreConfig option '{0}' disabled.", "EnableMemoryManager"); } } From 711319d325daebf490b9ae9c522a946755227ede Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 21:48:21 +0200 Subject: [PATCH 13/35] feat: allocation / total memory --- .../Core/Memory/MemoryManager.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 5ab7ba012..8bc62a0a9 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -87,6 +87,14 @@ public class MemoryManager : IMemoryManager, IStartupService /// public MemoryManagerState State { get; private set; } = MemoryManagerState.Unknown; + public long TotalMemory => GC.GetTotalMemory(false); + + public long TotalAllocated => GC.GetTotalAllocatedBytes(); + + public double TotalMemoryMB => (TotalMemory / (1024.0 * 1024.0)); + + public double TotalAllocatedMB => (TotalAllocated / (1024.0 * 1024.0)); + private readonly ILogger _logger; private readonly ICommandManager _commandManager; @@ -168,7 +176,9 @@ public void PrintStatistics(CommandInfo info) $"Total Released: {TotalReleased}\n" + $"Last Released: {LastReleased}\n" + $"Current Resources: {CurrentResources}\n" + - $"Last Updated: {LastUpdated.ToString("yyyy.MM.dd hh:mm:ss tt")} (UTC)"); + $"Last Updated: {LastUpdated.ToString("yyyy.MM.dd hh:mm:ss tt")} (UTC)\n" + + $"Heap Memory Usage: ~{TotalMemoryMB:F5} MB ({TotalMemory} bytes)\n" + + $"Total Allocated Bytes: ~{TotalAllocatedMB:F5} MB ({TotalAllocated} bytes)"); } public void Start() From d3788d9b47767c217ba2ecd0cdb9c7077a59c700 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 22:07:13 +0200 Subject: [PATCH 14/35] tweak: more log --- .../Core/Memory/MemoryManager.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 8bc62a0a9..528a016bd 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -185,6 +185,18 @@ public void Start() { if (CoreConfig.EnableMemoryManager) { + if (State == MemoryManagerState.Idle || State == MemoryManagerState.InProgress) + { + _logger.LogInformation("Service is already running"); + return; + } + + if (State == MemoryManagerState.Halted) + { + _logger.LogInformation("Service should be resumed, not started"); + return; + } + _logger.LogInformation("Starting service..."); try From 288c2ba7692e8843cc8f3315e87bf61d9ac86ea8 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Sun, 6 Oct 2024 22:08:37 +0200 Subject: [PATCH 15/35] fix: remove state change --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 528a016bd..2d9dc6910 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -299,7 +299,6 @@ private void BackgroundThread() LastReleased = totalCountAfter == 0 ? totalCount : difference; TotalReleased += (ulong)LastReleased; LastUpdated = DateTime.UtcNow; - State = MemoryManagerState.Idle; if (LastReleased > 0) { From 47475b0412dfbee845d743b74eb88931ad986344 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 18:04:29 +0200 Subject: [PATCH 16/35] tweak: rename command to `css_memory` --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 2d9dc6910..9435af37f 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -141,7 +141,7 @@ private void OnCommand(CCSPlayerController? caller, CommandInfo info) default: { - info.ReplyToCommand("Valid usage: css_memorymanager [option]\n" + + info.ReplyToCommand("Valid usage: css_memory [option]\n" + " stats - Print garbage collector statistics.\n" + " start - Starts the memory manager that handles leaking resources.\n" + " stop - Stops the memory manager.\n" + @@ -153,7 +153,7 @@ private void OnCommand(CCSPlayerController? caller, CommandInfo info) public void Load() { - _commandManager.RegisterCommand(new("css_memorymanager", "Counter-Strike Sharp Memory Manager options.", + _commandManager.RegisterCommand(new("css_memory", "Counter-Strike Sharp Memory Manager options.", OnCommand) { ExecutableBy = CommandUsage.CLIENT_AND_SERVER, From af2c922b19681b8dbf3c0ddb968eb9f9b7232c01 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 18:23:56 +0200 Subject: [PATCH 17/35] feat: added `Resume` --- managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs index e2c9e103c..f647e2e51 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs @@ -23,5 +23,7 @@ public interface IMemoryManager public void Start(); public void Stop(bool forceStop = false); + + public void Resume(); } } From 00eb0213568020ec5b0ddc2d1a62e6633be6fe21 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 18:24:11 +0200 Subject: [PATCH 18/35] tweak: this should be private --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 9435af37f..643fb2eb3 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -169,7 +169,7 @@ public void Load() Start(); } - public void PrintStatistics(CommandInfo info) + private void PrintStatistics(CommandInfo info) { info.ReplyToCommand("Memory Manager Statistics:\n" + $"State: {State}\n" + From 8320d2924e5c5eb3fefec3e86e22c189f924ebf1 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:16:37 +0200 Subject: [PATCH 19/35] tweak: native for `MemAlloc_Free` --- managed/CounterStrikeSharp.API/Core/API.cs | 50 +++--------- src/scripting/natives/natives_memory.cpp | 23 ++++++ src/scripting/natives/natives_memory.yaml | 3 +- src/scripting/natives/natives_vector.cpp | 94 +--------------------- src/scripting/natives/natives_vector.yaml | 4 - 5 files changed, 37 insertions(+), 137 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/API.cs b/managed/CounterStrikeSharp.API/Core/API.cs index 2b3ce3a59..ba9951e19 100644 --- a/managed/CounterStrikeSharp.API/Core/API.cs +++ b/managed/CounterStrikeSharp.API/Core/API.cs @@ -1150,6 +1150,16 @@ public static void RemoveAllNetworkVectorElements(IntPtr vec){ } } + public static void MemAllocFreePointer(IntPtr pointer){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(pointer); + ScriptContext.GlobalScriptContext.SetIdentifier(0xC2100D1D); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + public static short GetSchemaOffset(string classname, string propname){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); @@ -1645,46 +1655,6 @@ public static IntPtr AngleNew(){ } } - public static void VectorDelete(IntPtr vector){ - lock (ScriptContext.GlobalScriptContext.Lock) { - ScriptContext.GlobalScriptContext.Reset(); - ScriptContext.GlobalScriptContext.Push(vector); - ScriptContext.GlobalScriptContext.SetIdentifier(0x82187C1A); - ScriptContext.GlobalScriptContext.Invoke(); - ScriptContext.GlobalScriptContext.CheckErrors(); - } - } - - public static void AngleDelete(IntPtr angle){ - lock (ScriptContext.GlobalScriptContext.Lock) { - ScriptContext.GlobalScriptContext.Reset(); - ScriptContext.GlobalScriptContext.Push(angle); - ScriptContext.GlobalScriptContext.SetIdentifier(0xC622DA22); - ScriptContext.GlobalScriptContext.Invoke(); - ScriptContext.GlobalScriptContext.CheckErrors(); - } - } - - public static int VectorCount(){ - lock (ScriptContext.GlobalScriptContext.Lock) { - ScriptContext.GlobalScriptContext.Reset(); - ScriptContext.GlobalScriptContext.SetIdentifier(0x2A4502C0); - ScriptContext.GlobalScriptContext.Invoke(); - ScriptContext.GlobalScriptContext.CheckErrors(); - return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int)); - } - } - - public static int AngleCount(){ - lock (ScriptContext.GlobalScriptContext.Lock) { - ScriptContext.GlobalScriptContext.Reset(); - ScriptContext.GlobalScriptContext.SetIdentifier(0xB7A887F8); - ScriptContext.GlobalScriptContext.Invoke(); - ScriptContext.GlobalScriptContext.CheckErrors(); - return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int)); - } - } - public static float VectorGetX(IntPtr vector){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); diff --git a/src/scripting/natives/natives_memory.cpp b/src/scripting/natives/natives_memory.cpp index b7d4b3898..d8fecd83d 100644 --- a/src/scripting/natives/natives_memory.cpp +++ b/src/scripting/natives/natives_memory.cpp @@ -153,6 +153,27 @@ void RemoveAllNetworkVectorElements(ScriptContext& script_context) vec->RemoveAll(); } +void MemAlloc_FreePointer(ScriptContext& script_context) +{ + void* ptr = script_context.GetArgument(0); + + // TODO: replace with CSSHARP_CORE_TRACE + CSSHARP_CORE_INFO("Releasing pointer: {}", ptr); + +#ifdef _WIN32 + _try + { + MemAlloc_Free(ptr); + } + _except (EXCEPTION_ACCESS_VIOLATION) + { + script_context.ThrowNativeError("Invalid pointer reference: %p (EXCEPTION_ACCESS_VIOLATION)", ptr); + } +#else + MemAlloc_Free(ptr); +#endif +} + REGISTER_NATIVES(memory, { ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION", CreateVirtualFunction); ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION_BY_SIGNATURE", @@ -164,5 +185,7 @@ REGISTER_NATIVES(memory, { ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_SIZE", GetNetworkVectorSize); ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_ELEMENT_AT", GetNetworkVectorElementAt); ScriptEngine::RegisterNativeHandler("REMOVE_ALL_NETWORK_VECTOR_ELEMENTS", RemoveAllNetworkVectorElements); + + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_FREE_POINTER", MemAlloc_FreePointer); }) } // namespace counterstrikesharp diff --git a/src/scripting/natives/natives_memory.yaml b/src/scripting/natives/natives_memory.yaml index 7d8c9610b..553eb1476 100644 --- a/src/scripting/natives/natives_memory.yaml +++ b/src/scripting/natives/natives_memory.yaml @@ -6,4 +6,5 @@ EXECUTE_VIRTUAL_FUNCTION: function:pointer,arguments:object[] -> any FIND_SIGNATURE: modulePath:string, signature:string -> pointer GET_NETWORK_VECTOR_SIZE: vec:pointer -> int GET_NETWORK_VECTOR_ELEMENT_AT: vec:pointer, index:int -> pointer -REMOVE_ALL_NETWORK_VECTOR_ELEMENTS: vec:pointer -> void \ No newline at end of file +REMOVE_ALL_NETWORK_VECTOR_ELEMENTS: vec:pointer -> void +MEM_ALLOC_FREE_POINTER: pointer:pointer -> void \ No newline at end of file diff --git a/src/scripting/natives/natives_vector.cpp b/src/scripting/natives/natives_vector.cpp index b7e7ca9de..b233cbb99 100644 --- a/src/scripting/natives/natives_vector.cpp +++ b/src/scripting/natives/natives_vector.cpp @@ -36,98 +36,14 @@ CREATE_SETTER_FUNCTION(Vector, float, X, Vector*, obj->x = value); CREATE_SETTER_FUNCTION(Vector, float, Y, Vector*, obj->y = value); CREATE_SETTER_FUNCTION(Vector, float, Z, Vector*, obj->z = value); -std::vector managed_vectors; - Vector* VectorNew(ScriptContext& script_context) { - auto vec = new Vector(); - managed_vectors.push_back(vec); - return vec; -} - -int VectorCount(ScriptContext& script_context) -{ - return managed_vectors.size(); + return new Vector(); } -void VectorDel(ScriptContext& script_context) -{ - auto vec = script_context.GetArgument(0); - auto it = std::find(managed_vectors.begin(), managed_vectors.end(), vec); - - if (it != managed_vectors.end()) - { - CSSHARP_CORE_INFO("Releasing Vector: {}", (void*)vec); - managed_vectors.erase(it); - -#ifdef _WIN32 - _try - { - vec->~Vector(); - MemAlloc_Free(vec); - } - _except (EXCEPTION_ACCESS_VIOLATION) - { - script_context.ThrowNativeError("Invalid Vector reference: %p (EXCEPTION_ACCESS_VIOLATION)", vec); - } -#else - vec->~Vector(); - MemAlloc_Free(vec); -#endif - } else - { - if (managed_vectors.size() != 0) - { - script_context.ThrowNativeError("Invalid Vector reference: %p", vec); - } - } -} - -std::vector managed_angles; - QAngle* AngleNew(ScriptContext& script_context) { - auto ang = new QAngle(); - managed_angles.push_back(ang); - return ang; -} - -int AngleCount(ScriptContext& script_context) -{ - return managed_angles.size(); -} - -void AngleDel(ScriptContext& script_context) -{ - auto ang = script_context.GetArgument(0); - auto it = std::find(managed_angles.begin(), managed_angles.end(), ang); - - if (it != managed_angles.end()) - { - CSSHARP_CORE_INFO("Releasing QAngle: {}", (void*)ang); - managed_angles.erase(it); - -#ifdef _WIN32 - _try - { - ang->~QAngle(); - MemAlloc_Free(ang); - } - _except (EXCEPTION_ACCESS_VIOLATION) - { - script_context.ThrowNativeError("Invalid QAngle reference: %p (EXCEPTION_ACCESS_VIOLATION)", ang); - } -#else - ang->~QAngle(); - MemAlloc_Free(ang); -#endif - } else - { - if (managed_angles.size() != 0) - { - script_context.ThrowNativeError("Invalid angle reference: %p", ang); - } - } + return new QAngle(); } void NativeVectorAngles(ScriptContext& script_context) @@ -160,12 +76,6 @@ REGISTER_NATIVES(vector, { ScriptEngine::RegisterNativeHandler("VECTOR_NEW", VectorNew); ScriptEngine::RegisterNativeHandler("ANGLE_NEW", AngleNew); - ScriptEngine::RegisterNativeHandler("VECTOR_DELETE", VectorDel); - ScriptEngine::RegisterNativeHandler("ANGLE_DELETE", AngleDel); - - ScriptEngine::RegisterNativeHandler("VECTOR_COUNT", VectorCount); - ScriptEngine::RegisterNativeHandler("ANGLE_COUNT", AngleCount); - ScriptEngine::RegisterNativeHandler("VECTOR_SET_X", VectorSetX); ScriptEngine::RegisterNativeHandler("VECTOR_SET_Y", VectorSetY); ScriptEngine::RegisterNativeHandler("VECTOR_SET_Z", VectorSetZ); diff --git a/src/scripting/natives/natives_vector.yaml b/src/scripting/natives/natives_vector.yaml index 07ff43bf2..e594c773b 100644 --- a/src/scripting/natives/natives_vector.yaml +++ b/src/scripting/natives/natives_vector.yaml @@ -1,9 +1,5 @@ VECTOR_NEW: -> pointer ANGLE_NEW: -> pointer -VECTOR_DELETE: vector:pointer -> void -ANGLE_DELETE: angle:pointer -> void -VECTOR_COUNT: -> int -ANGLE_COUNT: -> int VECTOR_GET_X: vector:pointer -> float VECTOR_GET_Y: vector:pointer -> float VECTOR_GET_Z: vector:pointer -> float From a8ca731238932980b569698f96658570a12dce1a Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:16:48 +0200 Subject: [PATCH 20/35] tweak: adjust changes with the new native --- .../Modules/Memory/DisposableMemory.cs | 4 +++- .../CounterStrikeSharp.API/Modules/Utils/Angle.cs | 14 -------------- .../CounterStrikeSharp.API/Modules/Utils/QAngle.cs | 14 -------------- .../CounterStrikeSharp.API/Modules/Utils/Vector.cs | 14 -------------- 4 files changed, 3 insertions(+), 43 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs index e8498a061..d28626454 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs @@ -67,6 +67,8 @@ public virtual void ReleaseManaged() { } public virtual void ReleaseUnmanaged() - { } + { + NativeAPI.MemAllocFreePointer(Handle); + } } } diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs index fa7f45179..2993075a6 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Angle.cs @@ -371,20 +371,6 @@ protected override void OnDispose() { }*/ - public override void ReleaseUnmanaged() - { - NativeAPI.AngleDelete(Handle); - } - - /// - /// Returns the total amount of instances. - /// - /// - public static int GetTotalCount() - { - return NativeAPI.AngleCount(); - } - public override string ToString() { return $"{X:n2} {Y:n2} {Z:n2}"; diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs b/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs index f9556ad4c..8afb04506 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/QAngle.cs @@ -39,20 +39,6 @@ public QAngle(float? x = null, float? y = null, float? z = null) : this(NativeAP 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 override void ReleaseUnmanaged() - { - NativeAPI.AngleDelete(Handle); - } - - /// - /// Returns the total amount of instances. - /// - /// - public static int GetTotalCount() - { - return NativeAPI.AngleCount(); - } - public override string ToString() { return $"{X:n2} {Y:n2} {Z:n2}"; diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs index 3cf1f1566..e4382797a 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Vector.cs @@ -372,20 +372,6 @@ public override bool Equals(object obj) */ - public override void ReleaseUnmanaged() - { - NativeAPI.VectorDelete(Handle); - } - - /// - /// Returns the total amount of instances. - /// - /// - public static int GetTotalCount() - { - return NativeAPI.VectorCount(); - } - public override string ToString() { return $"{X:n2} {Y:n2} {Z:n2}"; From 7190ea5924ded2d1f9e3963c8c3764df35d60948 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:23:53 +0200 Subject: [PATCH 21/35] feat: native `MemAllocAllocate` --- managed/CounterStrikeSharp.API/Core/API.cs | 11 +++++++++++ src/scripting/natives/natives_memory.cpp | 7 +++++++ src/scripting/natives/natives_memory.yaml | 1 + 3 files changed, 19 insertions(+) diff --git a/managed/CounterStrikeSharp.API/Core/API.cs b/managed/CounterStrikeSharp.API/Core/API.cs index ba9951e19..eba2bf52b 100644 --- a/managed/CounterStrikeSharp.API/Core/API.cs +++ b/managed/CounterStrikeSharp.API/Core/API.cs @@ -1150,6 +1150,17 @@ public static void RemoveAllNetworkVectorElements(IntPtr vec){ } } + public static IntPtr MemAllocAllocate(int size){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(size); + ScriptContext.GlobalScriptContext.SetIdentifier(0x9F81F710); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + public static void MemAllocFreePointer(IntPtr pointer){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); diff --git a/src/scripting/natives/natives_memory.cpp b/src/scripting/natives/natives_memory.cpp index d8fecd83d..e6c8ecb08 100644 --- a/src/scripting/natives/natives_memory.cpp +++ b/src/scripting/natives/natives_memory.cpp @@ -174,6 +174,12 @@ void MemAlloc_FreePointer(ScriptContext& script_context) #endif } +void MemAlloc_Allocate(ScriptContext& script_context) +{ + size_t size = script_context.GetArgument(0); + return MemAlloc_Alloc(size); +} + REGISTER_NATIVES(memory, { ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION", CreateVirtualFunction); ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION_BY_SIGNATURE", @@ -186,6 +192,7 @@ REGISTER_NATIVES(memory, { ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_ELEMENT_AT", GetNetworkVectorElementAt); ScriptEngine::RegisterNativeHandler("REMOVE_ALL_NETWORK_VECTOR_ELEMENTS", RemoveAllNetworkVectorElements); + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_ALLOCATE", MemAlloc_Allocate); ScriptEngine::RegisterNativeHandler("MEM_ALLOC_FREE_POINTER", MemAlloc_FreePointer); }) } // namespace counterstrikesharp diff --git a/src/scripting/natives/natives_memory.yaml b/src/scripting/natives/natives_memory.yaml index 553eb1476..95228601a 100644 --- a/src/scripting/natives/natives_memory.yaml +++ b/src/scripting/natives/natives_memory.yaml @@ -7,4 +7,5 @@ FIND_SIGNATURE: modulePath:string, signature:string -> pointer GET_NETWORK_VECTOR_SIZE: vec:pointer -> int GET_NETWORK_VECTOR_ELEMENT_AT: vec:pointer, index:int -> pointer REMOVE_ALL_NETWORK_VECTOR_ELEMENTS: vec:pointer -> void +MEM_ALLOC_ALLOCATE: size:int -> pointer MEM_ALLOC_FREE_POINTER: pointer:pointer -> void \ No newline at end of file From f2250c62f0bf065bf9575b521f4b55ad42d70c28 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:25:40 +0200 Subject: [PATCH 22/35] fix: return void* --- src/scripting/natives/natives_memory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/natives/natives_memory.cpp b/src/scripting/natives/natives_memory.cpp index e6c8ecb08..394f07b0b 100644 --- a/src/scripting/natives/natives_memory.cpp +++ b/src/scripting/natives/natives_memory.cpp @@ -174,7 +174,7 @@ void MemAlloc_FreePointer(ScriptContext& script_context) #endif } -void MemAlloc_Allocate(ScriptContext& script_context) +void* MemAlloc_Allocate(ScriptContext& script_context) { size_t size = script_context.GetArgument(0); return MemAlloc_Alloc(size); From 83916a697671de7bba9e561547df9aaa207b5827 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:31:55 +0200 Subject: [PATCH 23/35] tweak: `DisposableMemory` is now abstract --- .../CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs index d28626454..b24e65dff 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs @@ -16,7 +16,7 @@ namespace CounterStrikeSharp.API.Modules.Memory { - public class DisposableMemory : NativeObject, IDisposableMemory + public abstract class DisposableMemory : NativeObject, IDisposableMemory { internal static int _instances; From d3907966280ef6f8a671f71e4f55088568871da0 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:37:10 +0200 Subject: [PATCH 24/35] fix: stuck on `InProgress` state when it should be idle --- .../Core/Memory/MemoryManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 643fb2eb3..6005743a8 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -300,6 +300,11 @@ private void BackgroundThread() TotalReleased += (ulong)LastReleased; LastUpdated = DateTime.UtcNow; + if (State == MemoryManagerState.InProgress) + { + State = MemoryManagerState.Idle; + } + if (LastReleased > 0) { _logger.LogInformation("Released {0} leaking memory resources in {1}ms ({2} remains)", LastReleased, (LastUpdated - startTime).TotalMilliseconds, CurrentResources); @@ -307,11 +312,6 @@ private void BackgroundThread() { Thread.Sleep(CoreConfig.MemoryManagerInterval); } - - if (State == MemoryManagerState.InProgress) - { - State = MemoryManagerState.Idle; - } } } } From c1ba9a95e7d9abed6a70973e1cfa7cc6e87dc185 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:39:28 +0200 Subject: [PATCH 25/35] tweak: this is no longer needed --- src/scripting/natives/natives_vector.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/scripting/natives/natives_vector.cpp b/src/scripting/natives/natives_vector.cpp index b233cbb99..f0710ab4c 100644 --- a/src/scripting/natives/natives_vector.cpp +++ b/src/scripting/natives/natives_vector.cpp @@ -16,7 +16,6 @@ #include -#include "core/log.h" #include "scripting/autonative.h" #include "scripting/script_engine.h" From 2ce4dddf54689305858bbf3f191b357a6996591e Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:53:31 +0200 Subject: [PATCH 26/35] feat: throw error for nullptr --- src/scripting/natives/natives_memory.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/scripting/natives/natives_memory.cpp b/src/scripting/natives/natives_memory.cpp index 394f07b0b..99938e4fd 100644 --- a/src/scripting/natives/natives_memory.cpp +++ b/src/scripting/natives/natives_memory.cpp @@ -157,6 +157,12 @@ void MemAlloc_FreePointer(ScriptContext& script_context) { void* ptr = script_context.GetArgument(0); + if (!ptr) + { + script_context.ThrowNativeError("Null pointer reference: %p", ptr); + return; + } + // TODO: replace with CSSHARP_CORE_TRACE CSSHARP_CORE_INFO("Releasing pointer: {}", ptr); From b7975d8804ed96fb3a57510b9fd12878892c56e6 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 22:58:40 +0200 Subject: [PATCH 27/35] feat: `MemAlloc` class --- .../Modules/Memory/DisposableMemory.cs | 2 +- .../Modules/Memory/MemAlloc.cs | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs index b24e65dff..8271fa953 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs @@ -68,7 +68,7 @@ public virtual void ReleaseManaged() public virtual void ReleaseUnmanaged() { - NativeAPI.MemAllocFreePointer(Handle); + MemAlloc.Free(Handle); } } } diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs b/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs new file mode 100644 index 000000000..b89aa02da --- /dev/null +++ b/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs @@ -0,0 +1,40 @@ +/* + * 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 . * + */ + +namespace CounterStrikeSharp.API.Modules.Memory +{ + public static class MemAlloc + { + /// + /// Indirect allocation using 'MemAlloc' + /// + /// + /// + public static nint Allocate(int size) + { + return NativeAPI.MemAllocAllocate(size); + } + + /// + /// Release pointer using 'MemAlloc' + /// + /// + public static void Free(nint ptr) + { + NativeAPI.MemAllocFreePointer(ptr); + } + } +} From f253c66222b62e4e9d38f8eecd63c502cdb38471 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 23:24:07 +0200 Subject: [PATCH 28/35] feat: more memalloc natives --- managed/CounterStrikeSharp.API/Core/API.cs | 46 ++++++++++++++++ .../Modules/Memory/MemAlloc.cs | 30 +++++++++++ src/scripting/natives/natives_memory.cpp | 53 ++++++++++++++++--- src/scripting/natives/natives_memory.yaml | 6 ++- 4 files changed, 126 insertions(+), 9 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/API.cs b/managed/CounterStrikeSharp.API/Core/API.cs index eba2bf52b..10189d837 100644 --- a/managed/CounterStrikeSharp.API/Core/API.cs +++ b/managed/CounterStrikeSharp.API/Core/API.cs @@ -1161,6 +1161,42 @@ public static IntPtr MemAllocAllocate(int size){ } } + public static IntPtr MemAllocAllocateAligned(int size, int align){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(size); + ScriptContext.GlobalScriptContext.Push(align); + ScriptContext.GlobalScriptContext.SetIdentifier(0x7D6BFE63); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + + public static IntPtr MemAllocReallocateAligned(IntPtr pointer, int size, int align){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(pointer); + ScriptContext.GlobalScriptContext.Push(size); + ScriptContext.GlobalScriptContext.Push(align); + ScriptContext.GlobalScriptContext.SetIdentifier(0x3C513734); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr)); + } + } + + public static int MemAllocGetsizeAligned(IntPtr pointer){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(pointer); + ScriptContext.GlobalScriptContext.SetIdentifier(0xA438740D); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int)); + } + } + public static void MemAllocFreePointer(IntPtr pointer){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); @@ -1171,6 +1207,16 @@ public static void MemAllocFreePointer(IntPtr pointer){ } } + public static void MemAllocFreePointerAligned(IntPtr pointer){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(pointer); + ScriptContext.GlobalScriptContext.SetIdentifier(0x88571A2E); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + public static short GetSchemaOffset(string classname, string propname){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs b/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs index b89aa02da..8ba115a47 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs @@ -28,6 +28,27 @@ public static nint Allocate(int size) return NativeAPI.MemAllocAllocate(size); } + /// + /// Indirect allocation using 'MemAlloc' + /// + /// + /// + /// + public static nint AllocateAligned(int size, int align) + { + return NativeAPI.MemAllocAllocateAligned(size, align); + } + + public static nint ReallocateAligned(nint pointer, int size, int align) + { + return NativeAPI.MemAllocReallocateAligned(pointer, size, align); + } + + public static int GetSizeAligned(nint pointer) + { + return NativeAPI.MemAllocGetsizeAligned(pointer); + } + /// /// Release pointer using 'MemAlloc' /// @@ -36,5 +57,14 @@ public static void Free(nint ptr) { NativeAPI.MemAllocFreePointer(ptr); } + + /// + /// Release pointer using 'MemAlloc' + /// + /// + public static void FreeAligned(nint ptr) + { + NativeAPI.MemAllocFreePointerAligned(ptr); + } } } diff --git a/src/scripting/natives/natives_memory.cpp b/src/scripting/natives/natives_memory.cpp index 99938e4fd..c0fbbf157 100644 --- a/src/scripting/natives/natives_memory.cpp +++ b/src/scripting/natives/natives_memory.cpp @@ -153,10 +153,10 @@ void RemoveAllNetworkVectorElements(ScriptContext& script_context) vec->RemoveAll(); } -void MemAlloc_FreePointer(ScriptContext& script_context) -{ - void* ptr = script_context.GetArgument(0); +typedef void (*Release_Func)(void* ptr); +static void ReleaseHelper(Release_Func func, ScriptContext& script_context, void* ptr) +{ if (!ptr) { script_context.ThrowNativeError("Null pointer reference: %p", ptr); @@ -169,23 +169,56 @@ void MemAlloc_FreePointer(ScriptContext& script_context) #ifdef _WIN32 _try { - MemAlloc_Free(ptr); + func(ptr); } _except (EXCEPTION_ACCESS_VIOLATION) { script_context.ThrowNativeError("Invalid pointer reference: %p (EXCEPTION_ACCESS_VIOLATION)", ptr); } #else - MemAlloc_Free(ptr); + func(ptr); #endif } -void* MemAlloc_Allocate(ScriptContext& script_context) +void Native_MemAlloc_FreePointer(ScriptContext& script_context) +{ + void* ptr = script_context.GetArgument(0); + ReleaseHelper(MemAlloc_Free, script_context, ptr); +} + +void Native_MemAlloc_FreePointerAligned(ScriptContext& script_context) +{ + void* ptr = script_context.GetArgument(0); + ReleaseHelper(MemAlloc_FreeAligned, script_context, ptr); +} + +void* Native_MemAlloc_Allocate(ScriptContext& script_context) { size_t size = script_context.GetArgument(0); return MemAlloc_Alloc(size); } +void* Native_MemAlloc_AllocateAligned(ScriptContext& script_context) +{ + size_t size = script_context.GetArgument(0); + size_t align = script_context.GetArgument(1); + return MemAlloc_AllocAlignedUnattributed(size, align); +} + +void* Native_MemAlloc_ReallocateAligned(ScriptContext& script_context) +{ + void* ptr = script_context.GetArgument(0); + size_t size = script_context.GetArgument(1); + size_t align = script_context.GetArgument(2); + return MemAlloc_ReallocAligned(ptr, size, align); +} + +size_t Native_MemAlloc_GetSizeAligned(ScriptContext& script_context) +{ + void* ptr = script_context.GetArgument(0); + return MemAlloc_GetSizeAligned(ptr); +} + REGISTER_NATIVES(memory, { ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION", CreateVirtualFunction); ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION_BY_SIGNATURE", @@ -198,7 +231,11 @@ REGISTER_NATIVES(memory, { ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_ELEMENT_AT", GetNetworkVectorElementAt); ScriptEngine::RegisterNativeHandler("REMOVE_ALL_NETWORK_VECTOR_ELEMENTS", RemoveAllNetworkVectorElements); - ScriptEngine::RegisterNativeHandler("MEM_ALLOC_ALLOCATE", MemAlloc_Allocate); - ScriptEngine::RegisterNativeHandler("MEM_ALLOC_FREE_POINTER", MemAlloc_FreePointer); + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_ALLOCATE", Native_MemAlloc_Allocate); + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_ALLOCATE_ALIGNED", Native_MemAlloc_Allocate); + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_REALLOCATE_ALIGNED", Native_MemAlloc_ReallocateAligned); + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_GETSIZE_ALIGNED", Native_MemAlloc_GetSizeAligned); + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_FREE_POINTER", Native_MemAlloc_FreePointer); + ScriptEngine::RegisterNativeHandler("MEM_ALLOC_FREE_POINTER_ALIGNED", Native_MemAlloc_FreePointerAligned); }) } // namespace counterstrikesharp diff --git a/src/scripting/natives/natives_memory.yaml b/src/scripting/natives/natives_memory.yaml index 95228601a..c21e18296 100644 --- a/src/scripting/natives/natives_memory.yaml +++ b/src/scripting/natives/natives_memory.yaml @@ -8,4 +8,8 @@ GET_NETWORK_VECTOR_SIZE: vec:pointer -> int GET_NETWORK_VECTOR_ELEMENT_AT: vec:pointer, index:int -> pointer REMOVE_ALL_NETWORK_VECTOR_ELEMENTS: vec:pointer -> void MEM_ALLOC_ALLOCATE: size:int -> pointer -MEM_ALLOC_FREE_POINTER: pointer:pointer -> void \ No newline at end of file +MEM_ALLOC_ALLOCATE_ALIGNED: size:int, align:int -> pointer +MEM_ALLOC_REALLOCATE_ALIGNED: pointer:pointer, size:int, align:int -> pointer +MEM_ALLOC_GETSIZE_ALIGNED: pointer:pointer -> int +MEM_ALLOC_FREE_POINTER: pointer:pointer -> void +MEM_ALLOC_FREE_POINTER_ALIGNED: pointer:pointer -> void \ No newline at end of file From c03e157403730d187320d6d377a8dc1d253d28fb Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Mon, 7 Oct 2024 23:25:21 +0200 Subject: [PATCH 29/35] chore: fixed inconvenient name --- managed/CounterStrikeSharp.API/Core/API.cs | 4 ++-- managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs | 2 +- src/scripting/natives/natives_memory.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Core/API.cs b/managed/CounterStrikeSharp.API/Core/API.cs index 10189d837..7ebf4750b 100644 --- a/managed/CounterStrikeSharp.API/Core/API.cs +++ b/managed/CounterStrikeSharp.API/Core/API.cs @@ -1186,11 +1186,11 @@ public static IntPtr MemAllocReallocateAligned(IntPtr pointer, int size, int ali } } - public static int MemAllocGetsizeAligned(IntPtr pointer){ + public static int MemAllocGetSizeAligned(IntPtr pointer){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); ScriptContext.GlobalScriptContext.Push(pointer); - ScriptContext.GlobalScriptContext.SetIdentifier(0xA438740D); + ScriptContext.GlobalScriptContext.SetIdentifier(0x814A9CB2); ScriptContext.GlobalScriptContext.Invoke(); ScriptContext.GlobalScriptContext.CheckErrors(); return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int)); diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs b/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs index 8ba115a47..b8804bcb7 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/MemAlloc.cs @@ -46,7 +46,7 @@ public static nint ReallocateAligned(nint pointer, int size, int align) public static int GetSizeAligned(nint pointer) { - return NativeAPI.MemAllocGetsizeAligned(pointer); + return NativeAPI.MemAllocGetSizeAligned(pointer); } /// diff --git a/src/scripting/natives/natives_memory.yaml b/src/scripting/natives/natives_memory.yaml index c21e18296..46bf3a4ab 100644 --- a/src/scripting/natives/natives_memory.yaml +++ b/src/scripting/natives/natives_memory.yaml @@ -10,6 +10,6 @@ REMOVE_ALL_NETWORK_VECTOR_ELEMENTS: vec:pointer -> void MEM_ALLOC_ALLOCATE: size:int -> pointer MEM_ALLOC_ALLOCATE_ALIGNED: size:int, align:int -> pointer MEM_ALLOC_REALLOCATE_ALIGNED: pointer:pointer, size:int, align:int -> pointer -MEM_ALLOC_GETSIZE_ALIGNED: pointer:pointer -> int +MEM_ALLOC_GET_SIZE_ALIGNED: pointer:pointer -> int MEM_ALLOC_FREE_POINTER: pointer:pointer -> void MEM_ALLOC_FREE_POINTER_ALIGNED: pointer:pointer -> void \ No newline at end of file From 4fca1996a237ec38d5beba8aa5541c8f20d99a78 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Tue, 8 Oct 2024 00:04:28 +0200 Subject: [PATCH 30/35] chore: added todo marks --- managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 6005743a8..32e7be9d5 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -282,6 +282,7 @@ private void BackgroundThread() State = MemoryManagerState.InProgress; + // TODO: replace with 'LogTrace' _logger.LogInformation("Running garbage collector ({0} disposable memory in total)", totalCount); DateTime startTime = DateTime.UtcNow; @@ -307,6 +308,7 @@ private void BackgroundThread() if (LastReleased > 0) { + // TODO: replace with 'LogTrace' _logger.LogInformation("Released {0} leaking memory resources in {1}ms ({2} remains)", LastReleased, (LastUpdated - startTime).TotalMilliseconds, CurrentResources); } else { From edadc86c848468e3a0bbf7c974ebbb78795ef3ad Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Tue, 8 Oct 2024 11:41:03 +0200 Subject: [PATCH 31/35] fix: dont release pure pointers --- .../Modules/Memory/DisposableMemory.cs | 15 +++++++++++++-- .../Modules/Memory/Schema.cs | 17 ++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs index 8271fa953..6ff5b07b9 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs @@ -43,6 +43,11 @@ public bool IsDisposed set => _disposed = value; } + /// + /// This is only if the under the hood is purely from the game. + /// + internal bool PurePointer { get; set; } = false; + public DisposableMemory(IntPtr ptr) : base(ptr) { Instances++; @@ -50,12 +55,18 @@ public DisposableMemory(IntPtr ptr) : base(ptr) ~DisposableMemory() { - (this as IDisposableMemory).DisposeInternal(); - Instances--; + if (!PurePointer) + { + (this as IDisposableMemory).DisposeInternal(); + Instances--; + } } public virtual void Dispose() { + if (PurePointer) + return; + // Dont call finalizer GC.SuppressFinalize(this); diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs b/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs index f754b67a8..d06f734a4 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs @@ -100,6 +100,21 @@ public static T GetDeclaredClass(IntPtr pointer, string className, string mem { if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null."); + if (typeof(DisposableMemory).IsAssignableFrom(typeof(T))) + { + object? instance = Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName)); + + if (instance is DisposableMemory disposable) + { + disposable.PurePointer = true; + + // we should not count these as they are not handled by us. + DisposableMemory.Instances--; + } + + return (T)instance; + } + return (T)Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName)); } @@ -214,4 +229,4 @@ public static void SetCustomMarshalledType(IntPtr pointer, string className, throw new NotSupportedException(); } } -} \ No newline at end of file +} From 8df7f05e1c1d1e1c57ebc35b9133512cf338add8 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Tue, 8 Oct 2024 12:01:41 +0200 Subject: [PATCH 32/35] tweak: cleanup and handle generics aswell --- .../Modules/Memory/DisposableMemory.cs | 11 ++++++++++ .../Modules/Memory/Schema.cs | 21 ++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs index 6ff5b07b9..5f2be64f6 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs @@ -81,5 +81,16 @@ public virtual void ReleaseUnmanaged() { MemAlloc.Free(Handle); } + + internal static void MarkAsPure(object? instance) + { + if (instance != null && instance is DisposableMemory disposable) + { + disposable.PurePointer = true; + + // we should not count these as they are not handled by us. + Instances--; + } + } } } diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs b/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs index d06f734a4..5a9386e4e 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs @@ -100,18 +100,10 @@ public static T GetDeclaredClass(IntPtr pointer, string className, string mem { if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null."); - if (typeof(DisposableMemory).IsAssignableFrom(typeof(T))) + if (typeof(DisposableMemory).IsAssignableFrom(typeof(T)) || (typeof(T).IsGenericType && typeof(DisposableMemory).IsAssignableFrom(typeof(T).GetGenericTypeDefinition()))) { object? instance = Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName)); - - if (instance is DisposableMemory disposable) - { - disposable.PurePointer = true; - - // we should not count these as they are not handled by us. - DisposableMemory.Instances--; - } - + DisposableMemory.MarkAsPure(instance); return (T)instance; } @@ -154,6 +146,15 @@ public static unsafe Span GetFixedArray(IntPtr pointer, string className, if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null."); Span span = new((void*)(pointer + GetSchemaOffset(className, memberName)), count); + + if (typeof(DisposableMemory).IsAssignableFrom(typeof(T)) || (typeof(T).IsGenericType && typeof(DisposableMemory).IsAssignableFrom(typeof(T).GetGenericTypeDefinition()))) + { + foreach (T instance in span) + { + DisposableMemory.MarkAsPure(instance); + } + } + return span; } From ce27ee7610c9d6d60ae0de10319081aa5237e208 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Tue, 8 Oct 2024 13:38:13 +0200 Subject: [PATCH 33/35] feat: these should be `DisposableMemory` --- .../Modules/Utils/Quaternion.cs | 7 +++--- .../Modules/Utils/Vector2D.cs | 7 +++--- .../Modules/Utils/Vector4D.cs | 7 +++--- .../Modules/Utils/matrix3x4_t.cs | 25 ++++++++++++++++--- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs b/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs index 84622c298..608dba033 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Quaternion.cs @@ -14,13 +14,12 @@ * along with CounterStrikeSharp. If not, see . * */ -using System; using System.Runtime.CompilerServices; -using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; namespace CounterStrikeSharp.API.Modules.Utils { - public class Quaternion : NativeObject + public class Quaternion : DisposableMemory { public Quaternion(IntPtr pointer) : base(pointer) { @@ -28,4 +27,4 @@ public Quaternion(IntPtr pointer) : base(pointer) public unsafe ref float Value => ref Unsafe.Add(ref *(float*)Handle.ToPointer(), 0); } -} \ 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..817b94ecd 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Vector2D.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Vector2D.cs @@ -14,13 +14,12 @@ * along with CounterStrikeSharp. If not, see . * */ -using System; using System.Runtime.CompilerServices; -using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; namespace CounterStrikeSharp.API.Modules.Utils { - public class Vector2D : NativeObject + public class Vector2D : DisposableMemory { public Vector2D(IntPtr pointer) : base(pointer) { @@ -29,4 +28,4 @@ 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); } -} \ 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..abacf07e9 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/Vector4D.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/Vector4D.cs @@ -14,13 +14,12 @@ * along with CounterStrikeSharp. If not, see . * */ -using System; using System.Runtime.CompilerServices; -using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; namespace CounterStrikeSharp.API.Modules.Utils { - public class Vector4D : NativeObject + public class Vector4D : DisposableMemory { public Vector4D(IntPtr pointer) : base(pointer) { @@ -31,4 +30,4 @@ public Vector4D(IntPtr pointer) : base(pointer) public unsafe ref float Z => ref Unsafe.Add(ref *(float*)Handle, 2); public unsafe ref float W => ref Unsafe.Add(ref *(float*)Handle, 3); } -} \ 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..5804e25b9 100644 --- a/managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs +++ b/managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs @@ -1,13 +1,30 @@ -using System; -using System.Runtime.CompilerServices; +/* + * 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; + +using CounterStrikeSharp.API.Modules.Memory; namespace CounterStrikeSharp.API.Modules.Utils; -public partial class matrix3x4_t : NativeObject +public partial class matrix3x4_t : DisposableMemory { 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); -} \ No newline at end of file +} From 6d1af887938d9d3c91ba83e8be62a8d862a1b8a8 Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Tue, 8 Oct 2024 13:38:19 +0200 Subject: [PATCH 34/35] feat: rework --- .../Modules/Memory/DisposableMemory.cs | 112 +++++++++++++++++- .../Modules/Memory/Schema.cs | 16 +-- 2 files changed, 113 insertions(+), 15 deletions(-) diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs index 5f2be64f6..68091075d 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/DisposableMemory.cs @@ -14,10 +14,17 @@ * along with CounterStrikeSharp. If not, see . * */ +using System; + +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; + namespace CounterStrikeSharp.API.Modules.Memory { public abstract class DisposableMemory : NativeObject, IDisposableMemory { + internal static Type DisposableType = typeof(DisposableMemory); + internal static int _instances; internal static int Instances @@ -82,14 +89,111 @@ public virtual void ReleaseUnmanaged() MemAlloc.Free(Handle); } + /// + /// Recursively checks if the given type is (or contains) a + /// + /// + /// + internal static bool IsDisposableType(Type type) + { + /* This part will be only needed if we have a correct implementation for `NetworkedVector<>` or any class that has any `DisposableMemory` as generic. + * Until that, this would be overkill + if (type == DisposableType || DisposableType.IsAssignableFrom(type)) + return true; + + if (type.IsGenericType) + { + foreach (Type arg in type.GetGenericArguments()) + { + if (DisposableType.IsAssignableFrom(arg)) + { + return true; + } + + if (IsDisposableType(arg)) + { + return true; + } + } + } + + return type.BaseType != null && type.BaseType.Namespace!.StartsWith("CounterStrikeSharp") && IsDisposableType(type.BaseType); + */ + + return type == DisposableType || DisposableType.IsAssignableFrom(type); + } + + internal static void MarkAsPure(DisposableMemory disposable) + { + disposable.PurePointer = true; + + // we should not count these as they are not handled by us. + Instances--; + } + + /* + internal static void MarkCollectionAsPure(IEnumerable collection) where T: DisposableMemory + { + foreach (DisposableMemory disposable in collection) + { + MarkAsPure(disposable); + } + } + */ + + /* Span where T has pointer or reference: Only value types without pointers or references are supported. + internal static void MarkSpanAsPure(Span collection) + { + foreach (T instance in collection) + { + if (instance is DisposableMemory disposable) + { + MarkAsPure(disposable); + } + } + } + */ + internal static void MarkAsPure(object? instance) { - if (instance != null && instance is DisposableMemory disposable) + if (instance == null) + return; + + switch (instance) { - disposable.PurePointer = true; + case DisposableMemory disposable: + MarkAsPure(disposable); + break; - // we should not count these as they are not handled by us. - Instances--; + /* since 'Networked vectors currently only support CHandle' lets stab ourselves in the back in the future + case NetworkedVector vectors: + MarkCollectionAsPure(vectors); + break; + + case NetworkedVector vector2ds: + MarkCollectionAsPure(vector2ds); + break; + + case NetworkedVector vector4ds: + MarkCollectionAsPure(vector4ds); + break; + + // 'Angle' is only referenced inside the 'Vector' class + case NetworkedVector angles: + MarkCollectionAsPure(angles); + break; + + case NetworkedVector quaternions: + MarkCollectionAsPure(quaternions); + break; + + case NetworkedVector matrixes: + MarkCollectionAsPure(matrixes); + break; + + default: throw new NotSupportedException($"'MarkAsPure': type '{instance.GetType().Name}' is not supported."); + */ + default: return; } } } diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs b/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs index 5a9386e4e..9b0f4d854 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/Schema.cs @@ -100,14 +100,14 @@ public static T GetDeclaredClass(IntPtr pointer, string className, string mem { if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null."); - if (typeof(DisposableMemory).IsAssignableFrom(typeof(T)) || (typeof(T).IsGenericType && typeof(DisposableMemory).IsAssignableFrom(typeof(T).GetGenericTypeDefinition()))) + object? instance = Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName)); + + if (DisposableMemory.IsDisposableType(typeof(T))) { - object? instance = Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName)); DisposableMemory.MarkAsPure(instance); - return (T)instance; } - return (T)Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName)); + return (T)instance; } public static unsafe ref T GetRef(IntPtr pointer, string className, string memberName) @@ -147,13 +147,7 @@ public static unsafe Span GetFixedArray(IntPtr pointer, string className, Span span = new((void*)(pointer + GetSchemaOffset(className, memberName)), count); - if (typeof(DisposableMemory).IsAssignableFrom(typeof(T)) || (typeof(T).IsGenericType && typeof(DisposableMemory).IsAssignableFrom(typeof(T).GetGenericTypeDefinition()))) - { - foreach (T instance in span) - { - DisposableMemory.MarkAsPure(instance); - } - } + // TODO: once we get a correct implementation for this method check for `DisposableMemory` instances and mark them as pure return span; } From 01eac8b3aa51d550d931588000f5c07ed7c2204d Mon Sep 17 00:00:00 2001 From: KillStr3aK Date: Wed, 9 Oct 2024 11:51:40 +0200 Subject: [PATCH 35/35] feat: `ForceCollect` --- .../CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs | 2 ++ .../CounterStrikeSharp.API/Core/Memory/MemoryManager.cs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs index f647e2e51..b5be3af8b 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/IMemoryManager.cs @@ -25,5 +25,7 @@ public interface IMemoryManager public void Stop(bool forceStop = false); public void Resume(); + + public void ForceCollect(int generation, GCCollectionMode mode, bool blocking); } } diff --git a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs index 32e7be9d5..de28e0ab7 100644 --- a/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs +++ b/managed/CounterStrikeSharp.API/Core/Memory/MemoryManager.cs @@ -260,6 +260,11 @@ public void Resume() _logger.LogInformation("Service has been resumed"); } + public void ForceCollect(int generation, GCCollectionMode mode, bool blocking) + { + GC.Collect(generation, mode, blocking); + } + private void BackgroundThread() { _logger.LogInformation("Service has been started"); @@ -287,7 +292,7 @@ private void BackgroundThread() DateTime startTime = DateTime.UtcNow; // some may go to gen1 or even gen2, but even those are released when this nondeterministic wonder wants so - GC.Collect(0, GCCollectionMode.Default, true); + ForceCollect(0, GCCollectionMode.Default, true); // this might be obsolete with 'blocking: false'? GC.WaitForPendingFinalizers();