diff --git a/.gitignore b/.gitignore index 1841365..41dbaf0 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ project.fragment.lock.json *.userosscache # Logs, caches, temp +log/ *.log debug_*.txt perf_*.txt diff --git a/jRandomSkills - SRC Files/src/player/PerfLog.cs b/jRandomSkills - SRC Files/src/player/PerfLog.cs index 594bb93..6109ff0 100644 --- a/jRandomSkills - SRC Files/src/player/PerfLog.cs +++ b/jRandomSkills - SRC Files/src/player/PerfLog.cs @@ -1,4 +1,4 @@ -using src.utils; +using src.utils; using System.Collections.Concurrent; using System.Diagnostics; using static src.jRandomSkills; @@ -49,35 +49,72 @@ namespace src.player private static readonly double[] bucketBoundsMs = [0.05, 0.1, 0.25, 0.5, 1, 2, 4, 8, 16, 32, double.MaxValue]; + private const int maxRawSamples = 4096; + private sealed class Aggregate { public double TotalMs; public double MaxMs; public int Count; public readonly int[] Buckets = new int[bucketBoundsMs.Length]; + public readonly List Raw = []; public DateTime WindowStart = DateTime.Now; + private bool rawSorted; + public void Add(double ms) { TotalMs += ms; Count++; if (ms > MaxMs) MaxMs = ms; + if (Raw.Count < maxRawSamples) Raw.Add(ms); + for (int i = 0; i < bucketBoundsMs.Length; i++) if (ms <= bucketBoundsMs[i]) { Buckets[i]++; break; } } public double Percentile(double fraction) + { + if (Count == 0) return 0; + + double value = Raw.Count == Count ? ExactPercentile(fraction) : BucketPercentile(fraction); + return value > MaxMs ? MaxMs : value; + } + + private double ExactPercentile(double fraction) + { + if (!rawSorted) + { + Raw.Sort(); + rawSorted = true; + } + + int index = (int)Math.Ceiling(Raw.Count * fraction) - 1; + if (index < 0) index = 0; + if (index >= Raw.Count) index = Raw.Count - 1; + return Raw[index]; + } + + private double BucketPercentile(double fraction) { int target = (int)Math.Ceiling(Count * fraction); if (target < 1) target = 1; int seen = 0; + double lower = 0; for (int i = 0; i < bucketBoundsMs.Length; i++) { + double upper = bucketBoundsMs[i] == double.MaxValue || bucketBoundsMs[i] > MaxMs ? MaxMs : bucketBoundsMs[i]; + + if (seen + Buckets[i] >= target) + { + if (Buckets[i] <= 0) return upper; + return lower + (upper - lower) * ((target - seen) / (double)Buckets[i]); + } + seen += Buckets[i]; - if (seen >= target) - return bucketBoundsMs[i] == double.MaxValue ? MaxMs : bucketBoundsMs[i]; + lower = upper; } return MaxMs; } @@ -88,6 +125,8 @@ namespace src.player MaxMs = 0; Count = 0; Array.Clear(Buckets); + Raw.Clear(); + rawSorted = false; WindowStart = DateTime.Now; } } diff --git a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs index b145b01..a4362c8 100644 --- a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs +++ b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Utils; @@ -170,7 +170,7 @@ namespace src.player.skills { if (player == null || !player.IsValid) return; - var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index); + var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index, trackAs: "empty_prop"); if (emptyProp == null || !emptyProp.IsValid) return; var playerPawn = player.PlayerPawn?.Value; @@ -188,7 +188,6 @@ namespace src.player.skills Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent"); - EntityManager.RegisterExisting(emptyProp, player.Index, "empty_prop"); } public static void PlayerHurt(EventPlayerHurt @event) diff --git a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs index bc722ae..edf6de6 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Utils; @@ -114,7 +114,7 @@ namespace src.player.skills { if (player == null || !player.IsValid) return; - var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index); + var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index, trackAs: "empty_prop"); if (emptyProp == null || !emptyProp.IsValid) return; var playerPawn = player.PlayerPawn?.Value; @@ -132,7 +132,6 @@ namespace src.player.skills Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent"); - EntityManager.RegisterExisting(emptyProp, player.Index, "empty_prop"); } public static void DisableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs index 4e88516..dee0544 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Utils; @@ -143,7 +143,7 @@ namespace src.player.skills { if (player == null || !player.IsValid) return; - var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index); + var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index, trackAs: "empty_prop"); if (emptyProp == null || !emptyProp.IsValid) return; var playerPawn = player.PlayerPawn?.Value; @@ -161,7 +161,6 @@ namespace src.player.skills Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent"); - EntityManager.RegisterExisting(emptyProp, player.Index, "empty_prop"); } private static void UpdateNinja(CCSPlayerController? player) diff --git a/jRandomSkills - SRC Files/src/utils/EntityManager.cs b/jRandomSkills - SRC Files/src/utils/EntityManager.cs index 0581dfa..83a2068 100644 --- a/jRandomSkills - SRC Files/src/utils/EntityManager.cs +++ b/jRandomSkills - SRC Files/src/utils/EntityManager.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; @@ -42,6 +42,8 @@ namespace src.utils { if (entityIndex == 0) return; + bool alreadyTracked = trackedEntities.TryGetValue(entityIndex, out var previous); + trackedEntities[entityIndex] = new EntityData { EntityIndex = entityIndex, @@ -50,8 +52,12 @@ namespace src.utils CreatedAt = DateTime.UtcNow }; - if (Config.DebugEnabled(DebugCategory.Entity)) + if (!Config.DebugEnabled(DebugCategory.Entity)) return; + + if (!alreadyTracked) Debug.WriteToDebug($"[Entity] + {entityType} #{entityIndex} owner={DescribeOwner(playerIndex)} tracked={trackedEntities.Count}", DebugCategory.Entity); + else if (previous.EntityType != entityType) + Debug.WriteToDebug($"[Entity] ~ {previous.EntityType} -> {entityType} #{entityIndex} owner={DescribeOwner(playerIndex)} tracked={trackedEntities.Count}", DebugCategory.Entity); } private static string DescribeOwner(uint playerIndex) @@ -130,7 +136,7 @@ namespace src.utils } } - public static CDynamicProp? CreateTrackedDynamicProp(uint playerIndex, string designerName = "prop_dynamic") + public static CDynamicProp? CreateTrackedDynamicProp(uint playerIndex, string designerName = "prop_dynamic", string? trackAs = null) { try { @@ -138,7 +144,7 @@ namespace src.utils var prop = Utilities.CreateEntityByName(designerName); if (prop == null || !prop.IsValid) return null; - RegisterEntity(prop.Index, playerIndex, designerName); + RegisterEntity(prop.Index, playerIndex, trackAs ?? designerName); return prop; } catch (Exception ex) diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll index 58e1105..e69cf33 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll differ diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll index bd589b8..595e711 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll differ