diff --git a/Robust.Client/GameObjects/EntitySystems/DebugEntityLookupSystem.cs b/Robust.Client/GameObjects/EntitySystems/DebugEntityLookupSystem.cs index 5e4ddcff3a..99332f90e1 100644 --- a/Robust.Client/GameObjects/EntitySystems/DebugEntityLookupSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/DebugEntityLookupSystem.cs @@ -92,6 +92,12 @@ public sealed class EntityLookupOverlay : Overlay return true; }, lookupAABB); + lookup.StaticSundriesTree.QueryAabb(ref ents, static (ref List state, in EntityUid value) => + { + state.Add(value); + return true; + }, lookupAABB); + lookup.SundriesTree.QueryAabb(ref ents, static (ref List state, in EntityUid value) => { state.Add(value); diff --git a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs index 836a89e6dc..4435cd7158 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs @@ -53,6 +53,16 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + lookup.StaticSundriesTree.QueryAabb(ref intersecting, + static (ref HashSet state, in EntityUid value) => + { + state.Add(value); + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree.QueryAabb(ref intersecting, @@ -97,6 +107,16 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + lookup.StaticSundriesTree.QueryAabb(ref intersecting, + static (ref HashSet state, in EntityUid value) => + { + state.Add(value); + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree.QueryAabb(ref intersecting, @@ -131,7 +151,7 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } - if ((flags & LookupFlags.Dynamic) != 0x0) + if ((flags & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) { lookup.StaticTree.QueryAabb(ref state, (ref (EntityUid? ignored, bool found) tuple, in FixtureProxy value) => { @@ -143,6 +163,18 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + lookup.StaticSundriesTree.QueryAabb(ref state, static (ref (EntityUid? ignored, bool found) tuple, in EntityUid value) => + { + if (tuple.ignored == value) + return true; + + tuple.found = true; + return false; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree.QueryAabb(ref state, static (ref (EntityUid? ignored, bool found) tuple, in EntityUid value) => @@ -196,6 +228,18 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + lookup.StaticSundriesTree.QueryAabb(ref state, static (ref (EntityUid? ignored, bool found) tuple, in EntityUid value) => + { + if (tuple.ignored == value) + return true; + + tuple.found = true; + return false; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree.QueryAabb(ref state, static (ref (EntityUid? ignored, bool found) tuple, in EntityUid value) => @@ -580,6 +624,15 @@ public sealed partial class EntityLookupSystem }, aabb, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + lookup.StaticSundriesTree.QueryAabb(ref intersecting, static (ref HashSet intersecting, in EntityUid value) => + { + intersecting.Add(value); + return true; + }, aabb, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree.QueryAabb(ref intersecting, static (ref HashSet intersecting, in EntityUid value) => @@ -629,6 +682,15 @@ public sealed partial class EntityLookupSystem }, aabb, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + lookup.StaticSundriesTree._b2Tree.Query(ref state, static (ref (B2DynamicTree _b2Tree, HashSet intersecting) tuple, DynamicTree.Proxy proxy) => + { + tuple.intersecting.Add(tuple._b2Tree.GetUserData(proxy)); + return true; + }, aabb); + } + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree._b2Tree.Query(ref state, static (ref (B2DynamicTree _b2Tree, HashSet intersecting) tuple, DynamicTree.Proxy proxy) => @@ -706,6 +768,15 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + component.StaticSundriesTree.QueryAabb(ref intersecting, static (ref HashSet intersecting, in EntityUid value) => + { + intersecting.Add(value); + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { component.SundriesTree.QueryAabb(ref intersecting, static (ref HashSet intersecting, in EntityUid value) => @@ -742,6 +813,15 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + component.StaticSundriesTree.QueryAabb(ref intersecting, static (ref HashSet intersecting, in EntityUid value) => + { + intersecting.Add(value); + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { component.SundriesTree.QueryAabb(ref intersecting, static (ref HashSet intersecting, in EntityUid value) => diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs index 7b885bbf8d..cc23cb49cb 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs @@ -50,6 +50,18 @@ public sealed partial class EntityLookupSystem }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if ((flags & LookupFlags.Sundries & (LookupFlags.Static | LookupFlags.Anchored)) != 0x0) + { + lookup.StaticSundriesTree.QueryAabb(ref state, static (ref (HashSet intersecting, EntityQuery query) tuple, in EntityUid value) => + { + if (!tuple.query.TryGetComponent(value, out var comp)) + return true; + + tuple.intersecting.Add(comp); + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree.QueryAabb(ref state, static (ref (HashSet intersecting, EntityQuery query) tuple, in EntityUid value) => diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs index 677a17ea95..d4010929ea 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; using JetBrains.Annotations; using Robust.Shared.Configuration; using Robust.Shared.Containers; -using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Map; @@ -13,8 +10,9 @@ using Robust.Shared.Physics.BroadPhase; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; using Robust.Shared.Utility; +using System; +using System.Collections.Generic; namespace Robust.Shared.GameObjects { @@ -119,6 +117,9 @@ namespace Robust.Shared.GameObjects if (!xformQuery.Resolve(uid, ref xform)) return; + // also ensure that no parent is in a container. + DebugTools.Assert(!_container.IsEntityOrParentInContainer(uid, meta, xform, null, xformQuery)); + var broadQuery = GetEntityQuery(); var lookup = GetBroadphase(uid, xform, broadQuery, xformQuery); @@ -153,6 +154,8 @@ namespace Robust.Shared.GameObjects { component.DynamicTree = new DynamicTreeBroadPhase(); component.StaticTree = new DynamicTreeBroadPhase(); + component.StaticSundriesTree = new DynamicTree( + (in EntityUid value) => GetTreeAABB(value, component.Owner)); component.SundriesTree = new DynamicTree( (in EntityUid value) => GetTreeAABB(value, component.Owner)); } @@ -239,9 +242,23 @@ namespace Robust.Shared.GameObjects if (HasComp(ev.Body.Owner)) return; - var broadQuery = GetEntityQuery(); var xformQuery = GetEntityQuery(); var xform = xformQuery.GetComponent(ev.Body.Owner); + + if (!ev.CanCollide && _container.IsEntityOrParentInContainer(ev.Body.Owner, null, xform, null, xformQuery)) + { + // getting inserted, skip sundries insertion and just let container insertion handle tree removal. + + // TODO: for whatever fucking cursed reason, this is currently required. + // FIX THIS, this is a hotfix + var b = GetBroadphase(ev.Body.Owner, xform, GetEntityQuery(), xformQuery); + if (b != null) + RemoveBroadTree(ev.Body, b, ev.Body.BodyType); + + return; + } + + var broadQuery = GetEntityQuery(); var broadphase = GetBroadphase(ev.Body.Owner, xform, broadQuery, xformQuery); if (broadphase == null) @@ -249,31 +266,48 @@ namespace Robust.Shared.GameObjects if (ev.CanCollide) { - RemoveSundriesTree(ev.Body.Owner, broadphase); + RemoveSundriesTree(ev.Body.Owner, broadphase, ev.Body.BodyType); AddBroadTree(ev.Body, broadphase, ev.Body.BodyType, xform: xform); } else { RemoveBroadTree(ev.Body, broadphase, ev.Body.BodyType); - AddSundriesTree(ev.Body.Owner, broadphase); + AddSundriesTree(ev.Body.Owner, broadphase, ev.Body.BodyType); } } private void OnBodyTypeChange(EntityUid uid, PhysicsComponent component, ref PhysicsBodyTypeChangedEvent args) { - if (!component.CanCollide || HasComp(uid)) + // only matters if we swapped from static to non-static. + if (args.Old != BodyType.Static && args.New != BodyType.Static) return; - var broadphase = GetBroadphase(Transform(uid)); + var xformQuery = GetEntityQuery(); + var xform = xformQuery.GetComponent(uid); + + if (xform.GridUid == uid) + return; + + // fun fact: container insertion tries to update the fucking lookups like 3 or more times, each time iterating through all of its parents. + if (_container.IsEntityOrParentInContainer(uid, null, xform, null, xformQuery)) + return; + + var broadQuery = GetEntityQuery(); + var broadphase = GetBroadphase(uid, xform, broadQuery, xformQuery); if (broadphase == null) return; - if (args.Old != BodyType.Static && args.New != BodyType.Static) - return; - - RemoveBroadTree(component, broadphase, args.Old); - AddBroadTree(component, broadphase, component.BodyType); + if (component.CanCollide) + { + RemoveBroadTree(component, broadphase, args.Old); + AddBroadTree(component, broadphase, component.BodyType); + } + else + { + RemoveSundriesTree(uid, broadphase, args.Old); + AddSundriesTree(uid, broadphase, component.BodyType); + } } private void RemoveBroadTree(PhysicsComponent body, BroadphaseComponent lookup, BodyType bodyType, FixturesComponent? manager = null) @@ -316,6 +350,8 @@ namespace Robust.Shared.GameObjects var tree = bodyType == BodyType.Static ? lookup.StaticTree : lookup.DynamicTree; var xformQuery = GetEntityQuery(); + DebugTools.Assert(!_container.IsEntityOrParentInContainer(body.Owner, null, xform, null, xformQuery)); + if (!TryComp(lookup.Owner, out var lookupXform) || lookupXform.MapUid == null) { throw new InvalidOperationException(); @@ -374,23 +410,21 @@ namespace Robust.Shared.GameObjects fixture.ProxyCount = count; } - private void AddSundriesTree(EntityUid uid, BroadphaseComponent lookup) + private void AddSundriesTree(EntityUid uid, BroadphaseComponent lookup, BodyType bodyType) { - var tree = lookup.SundriesTree; + DebugTools.Assert(!_container.IsEntityOrParentInContainer(uid)); + var tree = bodyType == BodyType.Static ? lookup.StaticSundriesTree : lookup.SundriesTree; tree.Add(uid); } - private void RemoveSundriesTree(EntityUid uid, BroadphaseComponent lookup) + private void RemoveSundriesTree(EntityUid uid, BroadphaseComponent lookup, BodyType bodyType) { - var tree = lookup.SundriesTree; + var tree = bodyType == BodyType.Static ? lookup.StaticSundriesTree : lookup.SundriesTree; tree.Remove(uid); } private void OnEntityInit(EntityUid uid) { - if (_container.IsEntityInContainer(uid)) - return; - var xformQuery = GetEntityQuery(); if (!xformQuery.TryGetComponent(uid, out var xform)) @@ -398,6 +432,9 @@ namespace Robust.Shared.GameObjects return; } + if (_container.IsEntityOrParentInContainer(uid, null, xform, null, xformQuery)) + return; + if (_mapManager.IsMap(uid) || _mapManager.IsGrid(uid)) { @@ -449,7 +486,10 @@ namespace Robust.Shared.GameObjects private void OnParentChange(ref EntParentChangedMessage args) { - var meta = MetaData(args.Entity); + var xformQuery = GetEntityQuery(); + var metaQuery = GetEntityQuery(); + var meta = metaQuery.GetComponent(args.Entity); + var xform = args.Transform; // If our parent is changing due to a container-insert, we let the container insert event handle that. Note // that the in-container flag gets set BEFORE insert parent change, and gets unset before the container @@ -463,7 +503,7 @@ namespace Robust.Shared.GameObjects // // TODO IMPROVE CONTAINER REMOVAL HANDLING - if (_container.IsEntityInContainer(args.Entity, meta)) + if (_container.IsEntityOrParentInContainer(args.Entity, meta, xform, metaQuery, xformQuery)) return; if (meta.EntityLifeStage < EntityLifeStage.Initialized || @@ -473,9 +513,7 @@ namespace Robust.Shared.GameObjects return; } - var xformQuery = GetEntityQuery(); var broadQuery = GetEntityQuery(); - var xform = args.Transform; BroadphaseComponent? oldLookup = null; if (args.OldMapId != MapId.Nullspace && xformQuery.TryGetComponent(args.OldParent, out var parentXform)) @@ -513,12 +551,19 @@ namespace Robust.Shared.GameObjects private void OnContainerInsert(EntInsertedIntoContainerMessage ev) { var xformQuery = GetEntityQuery(); + var broadQuery = GetEntityQuery(); + BroadphaseComponent? lookup; - if (ev.OldParent == EntityUid.Invalid || !xformQuery.TryGetComponent(ev.OldParent, out var oldXform)) + if (ev.OldParent == EntityUid.Invalid) return; - var broadQuery = GetEntityQuery(); - var lookup = GetBroadphase(ev.OldParent, oldXform, broadQuery, xformQuery); + if (!broadQuery.TryGetComponent(ev.OldParent, out lookup)) + { + if (!xformQuery.TryGetComponent(ev.OldParent, out var parentXform)) + return; + + lookup = GetBroadphase(ev.OldParent, parentXform, broadQuery, xformQuery); + } RemoveFromEntityTree(lookup, xformQuery.GetComponent(ev.Entity), xformQuery); } @@ -585,7 +630,10 @@ namespace Robust.Shared.GameObjects { if (!Resolve(uid, ref body, false) || !body.CanCollide) { - broadphase.SundriesTree.AddOrUpdate(uid, aabb); + if (body?.BodyType == BodyType.Static) + broadphase.StaticSundriesTree.AddOrUpdate(uid, aabb); + else + broadphase.SundriesTree.AddOrUpdate(uid, aabb); return; } @@ -596,7 +644,10 @@ namespace Robust.Shared.GameObjects { if (!Resolve(uid, ref body, false) || !body.CanCollide) { - broadphase.SundriesTree.Remove(uid); + if (body?.BodyType == BodyType.Static) + broadphase.StaticSundriesTree.Remove(uid); + else + broadphase.SundriesTree.Remove(uid); return; } diff --git a/Robust.Shared/Physics/BroadphaseComponent.cs b/Robust.Shared/Physics/BroadphaseComponent.cs index 31ed5ec012..cfca7af5e0 100644 --- a/Robust.Shared/Physics/BroadphaseComponent.cs +++ b/Robust.Shared/Physics/BroadphaseComponent.cs @@ -19,8 +19,13 @@ namespace Robust.Shared.Physics public IBroadPhase StaticTree = default!; /// - /// Stores all entities not in another tree. + /// Stores all other non-static entities not in another tree. /// public DynamicTree SundriesTree = default!; + + /// + /// Stores all other static entities not in another tree. + /// + public DynamicTree StaticSundriesTree = default!; } } diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs index 4f1fd0a2ea..fc48098c30 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs @@ -47,7 +47,7 @@ public partial class SharedPhysicsSystem var xform = Transform(uid); - if (component.CanCollide && (_containerSystem.IsEntityInContainer(uid) || xform.MapID == MapId.Nullspace)) + if (component.CanCollide && (_containerSystem.IsEntityOrParentInContainer(uid) || xform.MapID == MapId.Nullspace)) { SetCanCollide(component, false, false); }