diff --git a/Robust.Client/GameObjects/EntitySystems/AppearanceSystem.cs b/Robust.Client/GameObjects/EntitySystems/AppearanceSystem.cs index bde81cf36..994472457 100644 --- a/Robust.Client/GameObjects/EntitySystems/AppearanceSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/AppearanceSystem.cs @@ -17,6 +17,7 @@ namespace Robust.Client.GameObjects base.Initialize(); SubscribeLocalEvent(OnAppearanceInit); + SubscribeLocalEvent(OnAppearanceStartup); SubscribeLocalEvent(OnAppearanceHandleState); } @@ -32,7 +33,10 @@ namespace Robust.Client.GameObjects { visual.InitializeEntity(uid); } + } + private void OnAppearanceStartup(EntityUid uid, ClientAppearanceComponent component, ComponentStartup args) + { MarkDirty(component); } @@ -103,10 +107,9 @@ namespace Robust.Client.GameObjects var metaQuery = GetEntityQuery(); while (_queuedUpdates.TryDequeue(out var appearance)) { - if (appearance.Deleted) - continue; - appearance.AppearanceDirty = false; + if (!appearance.Running) + continue; // If the entity is no longer within the clients PVS, don't bother updating. if ((metaQuery.GetComponent(appearance.Owner).Flags & MetaDataFlags.Detached) != 0 && !appearance.UpdateDetached) diff --git a/Robust.Server/GameStates/PVSSystem.cs b/Robust.Server/GameStates/PVSSystem.cs index dd0a34029..abd6ddd78 100644 --- a/Robust.Server/GameStates/PVSSystem.cs +++ b/Robust.Server/GameStates/PVSSystem.cs @@ -16,6 +16,7 @@ using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Maths; +using Robust.Shared.Physics; using Robust.Shared.Players; using Robust.Shared.Threading; using Robust.Shared.Timing; @@ -253,8 +254,8 @@ internal sealed partial class PVSSystem : EntitySystem private void OnEntityMove(ref MoveEvent ev) { // GriddUid is only set after init. - if (ev.Component.LifeStage < ComponentLifeStage.Initialized && ev.Component.GridUid == null) - _transform.SetGridId(ev.Component, ev.Component.FindGridEntityId(GetEntityQuery())); + if (!ev.Component._gridInitialized) + _transform.InitializeGridUid(ev.Sender, ev.Component, GetEntityQuery(), GetEntityQuery()); // since elements are cached grid-/map-relative, we dont need to update a given grids/maps children if (ev.Component.GridUid == ev.Sender) diff --git a/Robust.Shared/ComponentTrees/ComponentTreeSystem.cs b/Robust.Shared/ComponentTrees/ComponentTreeSystem.cs index 1233f9ac7..6a6baf9d5 100644 --- a/Robust.Shared/ComponentTrees/ComponentTreeSystem.cs +++ b/Robust.Shared/ComponentTrees/ComponentTreeSystem.cs @@ -167,6 +167,9 @@ public abstract class ComponentTreeSystem : EntitySystem var (comp, xform) = entry; comp.TreeUpdateQueued = false; + if (!comp.Running) + continue; + if (!_updated.Add(comp.Owner)) continue; diff --git a/Robust.Shared/ComponentTrees/RecursiveMoveSystem.cs b/Robust.Shared/ComponentTrees/RecursiveMoveSystem.cs index 723507a7f..bba63cd3d 100644 --- a/Robust.Shared/ComponentTrees/RecursiveMoveSystem.cs +++ b/Robust.Shared/ComponentTrees/RecursiveMoveSystem.cs @@ -43,6 +43,8 @@ internal sealed class RecursiveMoveSystem : EntitySystem TransformComponent xform, EntityQuery xformQuery) { + // 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); diff --git a/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs b/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs index 90f2533cb..fb775466a 100644 --- a/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs +++ b/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs @@ -94,6 +94,7 @@ namespace Robust.Shared.GameObjects public MapId MapID { get; internal set; } internal bool _mapIdInitialized; + internal bool _gridInitialized; // TODO: Cache this. /// @@ -393,30 +394,6 @@ namespace Robust.Shared.GameObjects [ViewVariables] internal EntityUid LerpParent { get; set; } - internal EntityUid? FindGridEntityId(EntityQuery xformQuery) - { - if (_entMan.HasComponent(Owner)) - { - return null; - } - - if (_entMan.HasComponent(Owner)) - { - return Owner; - } - - if (_parent.IsValid()) - { - var parentXform = xformQuery.GetComponent(_parent); - if (parentXform.GridUid != null || parentXform.LifeStage >= ComponentLifeStage.Initialized) - return parentXform.GridUid; - else - return parentXform.FindGridEntityId(xformQuery); - } - - return _mapManager.TryFindGridAt(MapID, WorldPosition, out var mapgrid) ? mapgrid.Owner : null; - } - /// /// Detaches this entity from its parent. /// diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs index 420d57a1f..243c196dd 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs @@ -92,11 +92,17 @@ namespace Robust.Shared.GameObjects SubscribeLocalEvent(OnMove); SubscribeLocalEvent(OnBodyTypeChange); + SubscribeLocalEvent(OnBodyStartup); SubscribeLocalEvent(OnPhysicsUpdate); EntityManager.EntityInitialized += OnEntityInit; } + private void OnBodyStartup(EntityUid uid, PhysicsComponent component, ComponentStartup args) + { + UpdatePhysicsBroadphase(uid, Transform(uid), component); + } + public override void Shutdown() { base.Shutdown(); @@ -253,6 +259,7 @@ namespace Robust.Shared.GameObjects // ensure that the cached broadphase is correct. DebugTools.Assert(_timing.ApplyingState || xform.Broadphase == null + || ev.Body.LifeStage <= ComponentLifeStage.Initializing || !xform.Broadphase.Value.IsValid() || ((xform.Broadphase.Value.CanCollide == ev.Body.CanCollide) && (xform.Broadphase.Value.Static == (ev.Body.BodyType == BodyType.Static)))); @@ -269,6 +276,9 @@ namespace Robust.Shared.GameObjects private void UpdatePhysicsBroadphase(EntityUid uid, TransformComponent xform, PhysicsComponent body) { + if (body.LifeStage <= ComponentLifeStage.Initializing) + return; + if (xform.GridUid == uid) return; DebugTools.Assert(!_mapManager.IsGrid(uid)); diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs index 211f5a187..50fda58cd 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs @@ -289,7 +289,6 @@ public abstract partial class SharedMapSystem private void OnGridAdd(EntityUid uid, MapGridComponent component, ComponentAdd args) { - // GridID is not set yet so we don't include it. var msg = new GridAddEvent(uid); RaiseLocalEvent(uid, msg, true); } @@ -310,23 +309,8 @@ public abstract partial class SharedMapSystem component.LastTileModifiedTick = curTick; - var mapId = xform.MapID; - - if (MapManager.HasMapEntity(mapId)) - { - var mapUid = MapManager.GetMapEntityIdOrThrow(mapId); - - // Mapgrid moment - if (mapUid != uid) - _transform.SetParent(uid, xform, MapManager.GetMapEntityIdOrThrow(mapId), xformQuery); - - _transform.SetGridIdNoRecursive(xform, uid); - } - else - { - // Just in case. - _transform.SetGridId(xform, uid, xformQuery); - } + if (xform.MapUid != null && xform.MapUid != uid) + _transform.SetParent(uid, xform, xform.MapUid.Value, xformQuery); var msg = new GridInitializeEvent(uid); RaiseLocalEvent(uid, msg, true); diff --git a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs index c44c12469..36d89b500 100644 --- a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs +++ b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs @@ -46,7 +46,7 @@ public abstract partial class SharedTransformSystem xform._parent = newGridUid; xform._anchored = true; - SetGridId(xform, newGridUid, xformQuery); + SetGridId(uid, xform, newGridUid, xformQuery); var reParent = new EntParentChangedMessage(uid, oldGridUid, xform.MapID, xform); RaiseLocalEvent(uid, ref reParent, true); // TODO: Ideally shouldn't need to call the moveevent @@ -241,7 +241,7 @@ public abstract partial class SharedTransformSystem // Has to be done if _parent is set from ExposeData. if (component.ParentUid.IsValid()) { - // Note that _children is a SortedSet, + // Note that _children is a HashSet, // so duplicate additions (which will happen) don't matter. var parentXform = xformQuery.GetComponent(component.ParentUid); @@ -259,14 +259,14 @@ public abstract partial class SharedTransformSystem parentXform._children.Add(uid); } - if (component.GridUid == null) - SetGridId(component, component.FindGridEntityId(xformQuery)); - + InitializeGridUid(uid, component, xformQuery, GetEntityQuery()); component.MatricesDirty = true; + DebugTools.Assert(component._gridUid == uid || !HasComp(uid)); if (!component._anchored) return; + DebugTools.Assert(component.ParentUid == component.GridUid && component.ParentUid.IsValid()); MapGridComponent? grid; // First try find grid via parent: @@ -292,6 +292,32 @@ public abstract partial class SharedTransformSystem component._anchored = false; } + internal void InitializeGridUid( + EntityUid uid, + TransformComponent xform, + EntityQuery xformQuery, + EntityQuery gridQuery) + { + // Dont set pre-init, as the map grid component might not have been added yet. + if (xform._gridInitialized || xform.LifeStage < ComponentLifeStage.Initializing) + return; + + xform._gridInitialized = true; + DebugTools.Assert(xform.GridUid == null); + if (gridQuery.HasComponent(uid)) + { + xform._gridUid = uid; + return; + } + + if (!xform._parent.IsValid()) + return; + + var parentXform = xformQuery.GetComponent(xform._parent); + InitializeGridUid(xform._parent, parentXform, xformQuery, gridQuery); + xform._gridUid = parentXform._gridUid; + } + private void OnCompStartup(EntityUid uid, TransformComponent xform, ComponentStartup args) { // TODO PERFORMANCE remove AnchorStateChangedEvent and EntParentChangedMessage events here. @@ -324,38 +350,43 @@ public abstract partial class SharedTransformSystem /// /// Sets the for the transformcomponent without updating its children. Does not Dirty it. /// - internal void SetGridIdNoRecursive(TransformComponent xform, EntityUid? gridUid) + internal void SetGridIdNoRecursive(EntityUid uid, TransformComponent xform, EntityUid? gridUid) { + DebugTools.Assert(gridUid == uid || !HasComp(uid)); if (xform._gridUid == gridUid) return; DebugTools.Assert(gridUid == null || HasComp(gridUid)); - xform._gridUid = gridUid; } /// /// Sets the for the transformcomponent. Does not Dirty it. /// - public void SetGridId(TransformComponent xform, EntityUid? gridId, EntityQuery? xformQuery = null) + public void SetGridId(EntityUid uid, TransformComponent xform, EntityUid? gridId, EntityQuery? xformQuery = null) { - if (xform._gridUid == gridId) + if (!xform._gridInitialized || xform._gridUid == gridId || xform.GridUid == uid) return; + DebugTools.Assert(!HasComp(uid)); DebugTools.Assert(gridId == null || HasComp(gridId)); xformQuery ??= GetEntityQuery(); - SetGridIdRecursive(xform, gridId, xformQuery.Value); + SetGridIdRecursive(uid, xform, gridId, xformQuery.Value); } - private static void SetGridIdRecursive(TransformComponent xform, EntityUid? gridId, EntityQuery xformQuery) + private void SetGridIdRecursive(EntityUid uid, TransformComponent xform, EntityUid? gridId, EntityQuery xformQuery) { + if (!xform._gridInitialized || xform._gridUid == gridId || xform.GridUid == uid) + return; + + DebugTools.Assert(!HasComp(uid)); xform._gridUid = gridId; var childEnumerator = xform.ChildEnumerator; while (childEnumerator.MoveNext(out var child)) { - SetGridIdRecursive(xformQuery.GetComponent(child.Value), gridId, xformQuery); + SetGridIdRecursive(child.Value, xformQuery.GetComponent(child.Value), gridId, xformQuery); } } @@ -473,6 +504,7 @@ public abstract partial class SharedTransformSystem throw new InvalidOperationException($"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(uid)}, new parent: {ToPrettyString(value.EntityId)}"); } + // Check for recursive/circular transform hierarchies. if (xform.MapUid == newParent.MapUid) { var recursiveUid = value.EntityId; @@ -511,14 +543,23 @@ public abstract partial class SharedTransformSystem if (newParent != null) { xform.ChangeMapId(newParent.MapID, xformQuery); - if (xform.GridUid != uid) - SetGridId(xform, xform.FindGridEntityId(xformQuery), xformQuery); + + if (!xform._gridInitialized) + InitializeGridUid(uid, xform, xformQuery, GetEntityQuery()); + else + { + if (!newParent._gridInitialized) + InitializeGridUid(value.EntityId, newParent, xformQuery, GetEntityQuery()); + SetGridId(uid, xform, newParent.GridUid); + } } else { xform.ChangeMapId(MapId.Nullspace, xformQuery); - if (xform.GridUid != uid) - SetGridId(xform, null, xformQuery); + if (!xform._gridInitialized) + InitializeGridUid(uid, xform, xformQuery, GetEntityQuery()); + else + SetGridId(uid, xform, null, xformQuery); } if (xform.Initialized) @@ -1303,4 +1344,15 @@ public abstract partial class SharedTransformSystem DebugTools.Assert((MetaData(uid).Flags & MetaDataFlags.InContainer) == 0x0); } #endregion + + private void OnGridAdd(EntityUid uid, TransformComponent component, GridAddEvent args) + { + if (LifeStage(uid) > EntityLifeStage.Initialized) + { + SetGridId(uid, component, uid, GetEntityQuery()); + return; + } + component._gridInitialized = true; + component._gridUid = uid; + } } diff --git a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs index 7f9db725d..3249d86eb 100644 --- a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs @@ -36,6 +36,7 @@ namespace Robust.Shared.GameObjects SubscribeLocalEvent(OnCompStartup); SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnHandleState); + SubscribeLocalEvent(OnGridAdd); SubscribeLocalEvent(OnParentChange); } @@ -145,8 +146,8 @@ namespace Robust.Shared.GameObjects return xform.Coordinates; // GriddUid is only set after init. - if (xform.LifeStage < ComponentLifeStage.Initialized && xform.GridUid == null) - SetGridId(xform, xform.FindGridEntityId(xformQuery)); + if (!xform._gridInitialized) + InitializeGridUid(xform.Owner, xform, xformQuery, GetEntityQuery()); // Is the entity directly parented to the grid? if (xform.GridUid == xform.ParentUid) @@ -176,8 +177,8 @@ namespace Robust.Shared.GameObjects var parentXform = xformQuery.GetComponent(parentUid); // GriddUid is only set after init. - if (parentXform.LifeStage < ComponentLifeStage.Initialized && parentXform.GridUid == null) - SetGridId(parentXform, parentXform.FindGridEntityId(xformQuery)); + if (!parentXform._gridInitialized) + InitializeGridUid(parentUid, parentXform, xformQuery, GetEntityQuery()); // Is the entity directly parented to the grid? if (parentXform.GridUid == parentUid) @@ -208,8 +209,8 @@ namespace Robust.Shared.GameObjects return (xform.Coordinates, xform.LocalRotation); // GriddUid is only set after init. - if (xform.LifeStage < ComponentLifeStage.Initialized && xform.GridUid == null) - SetGridId(xform, xform.FindGridEntityId(xformQuery)); + if (!xform._gridInitialized) + InitializeGridUid(xform.Owner, xform, xformQuery, GetEntityQuery()); // Is the entity directly parented to the grid? if (xform.GridUid == xform.ParentUid) diff --git a/Robust.Shared/Map/MapManager.Queries.cs b/Robust.Shared/Map/MapManager.Queries.cs index 3bc691e70..db62101f9 100644 --- a/Robust.Shared/Map/MapManager.Queries.cs +++ b/Robust.Shared/Map/MapManager.Queries.cs @@ -1,9 +1,7 @@ -using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Robust.Shared.GameObjects; -using Robust.Shared.Log; using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Physics; @@ -16,6 +14,7 @@ internal partial class MapManager { public IEnumerable FindGridsIntersecting(MapId mapId, Box2Rotated bounds, bool approx = false) { + DebugTools.Assert(mapId != MapId.Nullspace); var aabb = bounds.CalcBoundingBox(); // TODO: We can do slower GJK checks to check if 2 bounds actually intersect, but WYCI. return FindGridsIntersecting(mapId, aabb, approx); @@ -23,6 +22,7 @@ internal partial class MapManager public void FindGridsIntersectingApprox(MapId mapId, Box2 worldAABB, GridCallback callback) { + DebugTools.Assert(mapId != MapId.Nullspace); if (!_gridTrees.TryGetValue(mapId, out var gridTree)) return; @@ -46,6 +46,7 @@ internal partial class MapManager public void FindGridsIntersectingApprox(MapId mapId, Box2 worldAABB, ref TState state, GridCallback callback) { + DebugTools.Assert(mapId != MapId.Nullspace); if (!_gridTrees.TryGetValue(mapId, out var gridTree)) return; @@ -71,6 +72,7 @@ internal partial class MapManager public IEnumerable FindGridsIntersecting(MapId mapId, Box2 worldAabb, bool approx = false) { + DebugTools.Assert(mapId != MapId.Nullspace); if (!_gridTrees.ContainsKey(mapId)) return Enumerable.Empty(); var xformQuery = EntityManager.GetEntityQuery(); @@ -89,6 +91,7 @@ internal partial class MapManager EntityQuery physicsQuery, bool approx = false) { + DebugTools.Assert(mapId != MapId.Nullspace); if (!_gridTrees.TryGetValue(mapId, out var gridTree)) return Enumerable.Empty(); DebugTools.Assert(grids.Count == 0); @@ -161,6 +164,7 @@ internal partial class MapManager EntityQuery xformQuery, [NotNullWhen(true)] out MapGridComponent? grid) { + DebugTools.Assert(mapId != MapId.Nullspace); // Need to enlarge the AABB by at least the grid shrinkage size. var aabb = new Box2(worldPos - 0.2f, worldPos + 0.2f);