using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Utility; namespace Robust.Shared.ComponentTrees; /// /// This system will recursively raise events to update component tree positions any time any entity moves. /// /// /// This is used by some client-side systems (e.g., sprites, lights, etc). However this can be quite expensive and if possible should not be used by the server. /// internal sealed class RecursiveMoveSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; private EntityQuery _xformQuery; bool Subscribed = false; public override void Initialize() { base.Initialize(); _xformQuery = GetEntityQuery(); } internal void AddSubscription() { if (Subscribed) return; Subscribed = true; SubscribeLocalEvent(AnythingMoved); } private void AnythingMoved(ref MoveEvent args) { if (args.Component.MapUid == args.Sender || args.Component.GridUid == args.Sender) return; DebugTools.Assert(!_mapManager.IsMap(args.Sender)); DebugTools.Assert(!_mapManager.IsGrid(args.Sender)); AnythingMovedSubHandler(args.Sender, args.Component); } private void AnythingMovedSubHandler( EntityUid uid, TransformComponent xform) { // TODO maybe use a c# event? This event gets raised a lot. // Would probably help with server performance and is also the main bottleneck for replay scrubbing. var ev = new TreeRecursiveMoveEvent(xform); RaiseLocalEvent(uid, ref ev); // TODO only enumerate over entities in containers if necessary? // annoyingly, containers aren't guaranteed to occlude sprites & lights // but AFAIK thats currently unused??? var childEnumerator = xform.ChildEnumerator; while (childEnumerator.MoveNext(out var child)) { if (_xformQuery.TryGetComponent(child.Value, out var childXform)) AnythingMovedSubHandler(child.Value, childXform); } } }