Namespace update#40
Open
buttheadbob wants to merge 5 commits into
Open
Conversation
Removed the following: - using Sandbox.Game.Entities.Cube; - using Sandbox.Game.Entities.EnvironmentItems; Compiled. Server runs. !profile sim returns sim data. That was the extent of testing.
BUG FIXES:
FastConcurrentQueue: Delete entire file (132 lines). Replaced with
System.Collections.Concurrent.ConcurrentQueue<ProfilerResult> in
ProfilerResultQueue. The custom FastConcurrentQueue/QueuePair/
NullableArrayList implementation had unsynchronized Expand() in
NullableArrayList causing two threads to share a single non-thread-safe
Queue<T> instance, leading to Array.Copy failures and crashes under
heavy concurrent profiling load.
StringIndexer: Add lock around List<string> _mapping access.
IndexOf() and StringAt() can be called concurrently from multiple
threads via CustomProfiling.Profile() at runtime, corrupting the
internal list state.
ProfilerResultQueue consumer loop:
- Remove racy _profilers.Count == 0 early-exit (non-atomic check
allowed results to be silently dropped or accumulate unbounded)
- Wrap consumer body in try/catch with retry (1s backoff, logs
warning) to prevent silent consumer death causing unbounded
memory growth
- Only sleep when queue is empty (batch-drain, no forced 100ms
batching penalty)
- Drop Alternate() call (ConcurrentQueue handles concurrent
read/write natively)
FEATURES:
!profile diagnose command:
- Runs 7 profilers simultaneously: GameLoopProfiler, GridProfiler,
BlockTypeProfiler, PlayerProfiler, FactionProfiler, PhysicsProfiler,
SessionComponentsProfiler
- Tiered output: frame breakdown with percentages, top grids/block
types/players/factions, physics clusters with per-grid drill-down,
top session components
- Discord-native formatting: bold headers, '>' block quotes,
'—' separators, compact single-line frame breakdown; no code blocks
- In-game chat format: compact line-by-line with abbreviated labels
- Physics cluster drill-down queries HkWorld entities on game thread
to identify top grids within each cluster
- Player/faction grid counts computed on game thread for Discord output
BUILD:
csproj: Add packaging targets (matching Advanced PB Limiter pattern)
- Copy manifest.xml to output directory (PreserveNewest)
- Zip output (DLL, PDB, manifest.xml) to Build/Profiler.zip
- Uses PowerShell Compress-Archive for standard zip format (compatible
with Torch PluginManager)
- Remove unused packages.config reference
MISC:
ProfilerCommands: Use C#8 'using var' declarations in existing commands
- PlayerProfiler: Fix "Collection was modified" InvalidOperationException by copying grid.BigOwners with .ToList() before enumeration. The game thread can mutate BigOwners while the profiler worker thread iterates. - BaseProfiler: Capture _endFrameCount and _endTime at MarkEnd() time and freeze them, so GetResult() reports the snapshot at end-marker rather than at GetResult() call time (results were leaking extra frames/time between MarkEnd and GetResult). - MyPhysics_StepWorlds: Add volatile to _simulatesParallel field to prevent stale reads from JIT/CPU optimizations when accessed across threads. - ProfilerCommands: Add 150ms extra delay after profiling period to allow the queue of profiler results to drain before MarkEnd() is called. Prevents results from being missed at the tail end. - Build packaging updated.
…ugin consumption Extract the Diagnose() profiling logic into a standalone static API (ProfilerApi.RunDiagnoseAsync) that returns structured POCOs instead of formatted text. This enables external plugins (e.g. Orion) to invoke comprehensive diagnostics programmatically and receive serializable data. New files: - DiagnoseResult.cs: POCOs for all result categories (GridEntry, BlockTypeEntry, PlayerEntry, FactionEntry, ClusterEntry, SessionEntry). Primitive types only, no game object references, fully serializable. - ProfilerApi.cs: Static RunDiagnoseAsync(seconds, topCount) that mirrors the exact profiler lifecycle of ProfilerCommands.Diagnose() -- same 7 profilers, same ProfilerResultQueue.Profile() registration, same MarkStart/Delay/MarkEnd/GetResult cycle. Key design decisions: - FrameCategories returned as Dictionary<string,double> with named keys (Update, Physics, Network, Replication, Session, GPS, ParallelWait, Lock) - topCount governs all entity lists including clusters; nested grids per cluster hardcoded at 3 - Player/faction grid counts always computed (not gated by discordDisplay) - TotalFrameCount kept as ulong to match internal profiler type - ProfilerCommands.Diagnose() left untouched to avoid risk to existing command
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Removed the following:
Compiled. Server runs. !profile sim returns sim data. That was the extent of testing.