using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Prometheus; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Player; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Robust.Server.GameStates { /// /// Caching for dirty bodies /// internal sealed partial class PvsSystem { [Dependency] private IGameTiming _gameTiming = default!; /// /// if it's a new entity we need to GetEntityState from tick 0. /// private HashSet[] _addEntities = new HashSet[DirtyBufferSize]; private HashSet[] _dirtyEntities = new HashSet[DirtyBufferSize]; private int _currentIndex = 1; private void InitializeDirty() { for (var i = 0; i < DirtyBufferSize; i++) { _addEntities[i] = new HashSet(32); _dirtyEntities[i] = new HashSet(32); } EntityManager.EntityAdded += OnEntityAdd; EntityManager.EntityDirtied += OnEntityDirty; } private void ShutdownDirty() { EntityManager.EntityAdded -= OnEntityAdd; EntityManager.EntityDirtied -= OnEntityDirty; } private void OnEntityAdd(Entity e) { DebugTools.Assert(_currentIndex == _gameTiming.CurTick.Value % DirtyBufferSize || _gameTiming.GetType().Name == "IGameTimingProxy");// Look I have NFI how best to excuse this assert if the game timing isn't real (a Mock). _addEntities[_currentIndex].Add(e); } private void OnEntityDirty(Entity uid) { if (uid.Comp.PvsData != PvsIndex.Invalid) { ref var meta = ref _metadataMemory.GetRef(uid.Comp.PvsData.Index); meta.LastModifiedTick = uid.Comp.EntityLastModifiedTick; } if (!_addEntities[_currentIndex].Contains(uid)) _dirtyEntities[_currentIndex].Add(uid); } private bool TryGetDirtyEntities(GameTick tick, [NotNullWhen(true)] out HashSet? addEntities, [NotNullWhen(true)] out HashSet? dirtyEntities) { var currentTick = _gameTiming.CurTick; if (currentTick.Value - tick.Value >= DirtyBufferSize) { addEntities = null; dirtyEntities = null; return false; } var index = tick.Value % DirtyBufferSize; addEntities = _addEntities[index]; dirtyEntities = _dirtyEntities[index]; return true; } private void CleanupDirty() { using var _ = Histogram.WithLabels("Clean Dirty").NewTimer(); if (!CullingEnabled) { _seenAllEnts.Clear(); foreach (var player in _sessions) { _seenAllEnts.Add(player.Session); } } _currentIndex = ((int)_gameTiming.CurTick.Value + 1) % DirtyBufferSize; _addEntities[_currentIndex].Clear(); _dirtyEntities[_currentIndex].Clear(); } } }