Split entity management from entity queries (#1665)

* Split entity lookups from entitymanager

* Helps if you subscribe dingus

* Handle map changes

* Stacks instead

* Make mapchanges use a queue because it's probably better

Moves likely only care about the latest position

* IoC what you did there

* IoC refactor

* Minor optimisations

* Apply feedback

* My IQ dropped 3 sizes that day

* Rest of acruid's feedback

* final_no_actual commit

* enlightenment?

* Liftoff

* final_commit_v2_actual

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2021-04-06 13:29:48 +10:00
committed by GitHub
parent 2a349eb023
commit d8aad89c2f
24 changed files with 534 additions and 515 deletions

View File

@@ -8,6 +8,7 @@ using Robust.Client.Utility;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -193,6 +194,7 @@ namespace Robust.Client
IoCManager.Resolve<INetConfigurationManager>().FlushMessages();
_gameStates.Reset();
_playMan.Shutdown();
IoCManager.Resolve<IEntityLookup>().Shutdown();
_entityManager.Shutdown();
_mapManager.Shutdown();
_discord.ClearPresence();

View File

@@ -38,6 +38,7 @@ namespace Robust.Client
IoCManager.Register<IPrototypeManager, ClientPrototypeManager>();
IoCManager.Register<IEntityManager, ClientEntityManager>();
IoCManager.Register<IEntityLookup, SharedEntityLookup>();
IoCManager.Register<IComponentFactory, ClientComponentFactory>();
IoCManager.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
IoCManager.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();

View File

@@ -119,6 +119,7 @@ namespace Robust.Client
_prototypeManager.Resync();
_mapManager.Initialize();
_entityManager.Initialize();
IoCManager.Resolve<IEntityLookup>().Initialize();
_gameStateManager.Initialize();
_placementManager.Initialize();
_viewVariablesManager.Initialize();
@@ -400,6 +401,7 @@ namespace Robust.Client
{
_networkManager.Shutdown("Client shutting down");
_midiManager.Shutdown();
IoCManager.Resolve<IEntityLookup>().Shutdown();
_entityManager.Shutdown();
_clyde.Shutdown();
}

View File

@@ -25,14 +25,13 @@ namespace Robust.Client.GameObjects
public override void Startup()
{
base.Startup();
if (Started)
{
throw new InvalidOperationException("Startup() called multiple times");
}
EntitySystemManager.Initialize();
base.Startup();
Started = true;
}
@@ -96,11 +95,6 @@ namespace Robust.Client.GameObjects
kvStates.Value.Item2);
}
foreach (var kvp in toApply)
{
UpdateEntityTree(kvp.Key);
}
foreach (var id in deletions)
{
DeleteEntity(id);
@@ -130,7 +124,7 @@ namespace Robust.Client.GameObjects
foreach (var entity in toInitialize)
{
#if EXCEPTION_TOLERANCE
if(brokenEnts.Contains(entity))
if (brokenEnts.Contains(entity))
continue;
try
@@ -150,10 +144,9 @@ namespace Robust.Client.GameObjects
foreach (var entity in toInitialize)
{
#if EXCEPTION_TOLERANCE
if(brokenEnts.Contains(entity))
if (brokenEnts.Contains(entity))
continue;
#endif
UpdateEntityTree(entity);
}
#if EXCEPTION_TOLERANCE
foreach (var entity in brokenEnts)
@@ -199,7 +192,6 @@ namespace Robust.Client.GameObjects
{
var newEnt = CreateEntityUninitialized(protoName, coordinates);
InitializeAndStartEntity((Entity) newEnt);
UpdateEntityTree(newEnt);
return newEnt;
}
@@ -208,7 +200,6 @@ namespace Robust.Client.GameObjects
{
var entity = CreateEntityUninitialized(protoName, coordinates);
InitializeAndStartEntity((Entity) entity);
UpdateEntityTree(entity);
return entity;
}

View File

@@ -32,6 +32,7 @@ namespace Robust.Client.GameStates
= new();
[Dependency] private readonly IClientEntityManager _entities = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IClientNetManager _network = default!;
[Dependency] private readonly IBaseClient _client = default!;
@@ -303,6 +304,8 @@ namespace Robust.Client.GameStates
}
_entities.TickUpdate((float) _timing.TickPeriod.TotalSeconds);
_lookup.Update();
}
private void ResetPredictedEntities(GameTick curTick)

View File

@@ -1,5 +1,7 @@
using System.Linq;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -28,7 +30,7 @@ namespace Robust.Client.Placement.Modes
var mapId = MouseCoords.GetMapId(pManager.EntityManager);
var snapToEntities = pManager.EntityManager.GetEntitiesInRange(MouseCoords, SnapToRange)
var snapToEntities = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(MouseCoords, SnapToRange)
.Where(entity => entity.Prototype == pManager.CurrentPrototype && entity.Transform.MapID == mapId)
.OrderBy(entity => (entity.Transform.WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager)).LengthSquared)
.ToList();

View File

@@ -1,4 +1,6 @@
using Robust.Shared.Map;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.Client.Placement.Modes
@@ -51,7 +53,7 @@ namespace Robust.Client.Placement.Modes
var topRight = new Vector2(CurrentTile.X + 0.99f, CurrentTile.Y + 0.99f);
var box = new Box2(bottomLeft, topRight);
return !pManager.EntityManager.AnyEntitiesIntersecting(map, box);
return !IoCManager.Resolve<IEntityLookup>().AnyEntitiesIntersecting(map, box);
}
}
}

View File

@@ -67,7 +67,8 @@ namespace Robust.Server
[Dependency] private readonly IConfigurationManagerInternal _config = default!;
[Dependency] private readonly IComponentManager _components = default!;
[Dependency] private readonly IServerEntityManager _entities = default!;
[Dependency] private readonly IServerEntityManager _entityManager = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly ILogManager _log = default!;
[Dependency] private readonly IRobustSerializer _serializer = default!;
[Dependency] private readonly IGameTiming _time = default!;
@@ -292,8 +293,6 @@ namespace Robust.Server
IoCManager.Resolve<IGameTiming>().InSimulation = true;
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
_stateManager.Initialize();
IoCManager.Resolve<IPlayerManager>().Initialize(MaxPlayers);
_mapManager.Initialize();
_mapManager.Startup();
@@ -303,8 +302,8 @@ namespace Robust.Server
// Call Init in game assemblies.
_modLoader.BroadcastRunLevel(ModRunLevel.Init);
_entities.Initialize();
_entityManager.Initialize();
IoCManager.Resolve<IEntityLookup>().Initialize();
IoCManager.Resolve<ISerializationManager>().Initialize();
@@ -316,7 +315,8 @@ namespace Robust.Server
prototypeManager.Resync();
IoCManager.Resolve<IServerConsoleHost>().Initialize();
_entities.Startup();
_entityManager.Startup();
_stateManager.Initialize();
_scriptHost.Initialize();
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);
@@ -493,7 +493,8 @@ namespace Robust.Server
_network.Shutdown($"Server shutting down: {_shutdownReason}");
// shutdown entities
_entities.Shutdown();
IoCManager.Resolve<IEntityLookup>().Shutdown();
_entityManager.Shutdown();
if (_config.GetCVar(CVars.LogRuntimeLog))
{
@@ -573,7 +574,9 @@ namespace Robust.Server
}
// Pass Histogram into the IEntityManager.Update so it can do more granular measuring.
_entities.TickUpdate(frameEventArgs.DeltaSeconds, TickUsage);
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds, TickUsage);
_lookup.Update();
using (TickUsage.WithLabels("PostEngine").NewTimer())
{

View File

@@ -79,8 +79,8 @@ namespace Robust.Server.GameObjects
/// <inheritdoc />
public override void Startup()
{
base.Startup();
EntitySystemManager.Initialize();
base.Startup();
Started = true;
}

View File

@@ -5,6 +5,7 @@ using Microsoft.Extensions.ObjectPool;
using Robust.Server.GameObjects;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
@@ -18,12 +19,13 @@ namespace Robust.Server.GameStates
private const int ViewSetCapacity = 128; // starting number of entities that are in view
private const int PlayerSetSize = 64; // Starting number of players
private const int MaxVisPoolSize = 1024; // Maximum number of pooled objects
private static readonly Vector2 Vector2NaN = new(float.NaN, float.NaN);
private readonly IServerEntityManager _entMan;
private readonly IComponentManager _compMan;
private readonly IMapManager _mapManager;
private IEntityLookup _lookup;
private readonly Dictionary<ICommonSession, HashSet<EntityUid>> _playerVisibleSets = new(PlayerSetSize);
@@ -47,12 +49,13 @@ namespace Robust.Server.GameStates
/// </summary>
public float ViewSize { get; set; }
public EntityViewCulling(IServerEntityManager entMan, IMapManager mapManager)
public EntityViewCulling(IServerEntityManager entMan, IMapManager mapManager, IEntityLookup lookup)
{
_entMan = entMan;
_compMan = entMan.ComponentManager;
_mapManager = mapManager;
_compMan = _entMan.ComponentManager;
_lookup = lookup;
}
// Not thread safe
@@ -248,7 +251,7 @@ namespace Robust.Server.GameStates
private HashSet<EntityUid> CalcCurrentViewSet(ICommonSession session)
{
var visibleEnts = _visSetPool.Get();
//TODO: Refactor map system to not require every map and grid entity to function.
IncludeMapCriticalEntities(visibleEnts);
@@ -274,7 +277,7 @@ namespace Robust.Server.GameStates
// grid entity should be added through this
// assume there are no deleted ents in here, cull them first in ent/comp manager
_entMan.FastEntitiesIntersecting(in mapId, ref viewBox, entity => RecursiveAdd((TransformComponent)entity.Transform, visibleEnts, visMask));
_lookup.FastEntitiesIntersecting(in mapId, ref viewBox, entity => RecursiveAdd((TransformComponent) entity.Transform, visibleEnts, visMask));
}
viewers.Clear();
@@ -321,7 +324,7 @@ namespace Robust.Server.GameStates
}
var xformParentUid = xform.ParentUid;
// this is the world entity, it is always visible
if (!xformParentUid.IsValid())
{

View File

@@ -30,6 +30,7 @@ namespace Robust.Server.GameStates
private EntityViewCulling _entityView = null!;
[Dependency] private readonly IServerEntityManager _entityManager = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IServerNetManager _networkManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -55,7 +56,7 @@ namespace Robust.Server.GameStates
public void PostInject()
{
_logger = Logger.GetSawmill("PVS");
_entityView = new EntityViewCulling(_entityManager, _mapManager);
_entityView = new EntityViewCulling(_entityManager, _mapManager, _lookup);
}
/// <inheritdoc />
@@ -216,7 +217,7 @@ namespace Robust.Server.GameStates
.Where(s => s.Status == SessionStatus.InGame)
.AsParallel()
.Select(SafeGenerateMail);
foreach (var (msg, chan) in mailBag)
{
// see session.Status != SessionStatus.InGame above

View File

@@ -588,20 +588,10 @@ namespace Robust.Server.Maps
private void FinishEntitiesStartup()
{
foreach (var entity in Entities)
{
_serverEntityManager.UpdateEntityTree(entity);
}
foreach (var entity in Entities)
{
_serverEntityManager.FinishEntityStartup(entity);
}
foreach (var entity in Entities)
{
_serverEntityManager.UpdateEntityTree(entity);
}
}
// Serialization

View File

@@ -209,7 +209,7 @@ namespace Robust.Server.Placement
{
EntityCoordinates start = msg.EntityCoordinates;
Vector2 rectSize = msg.RectSize;
foreach (IEntity entity in _entityManager.GetEntitiesIntersecting(start.GetMapId(_entityManager),
foreach (IEntity entity in IoCManager.Resolve<IEntityLookup>().GetEntitiesIntersecting(start.GetMapId(_entityManager),
new Box2(start.Position, start.Position + rectSize)))
{
if (entity.Deleted || entity.HasComponent<IMapGridComponent>() || entity.HasComponent<IActorComponent>())

View File

@@ -41,6 +41,7 @@ namespace Robust.Server
IoCManager.Register<IServerConsoleHost, ServerConsoleHost>();
IoCManager.Register<IConsoleHost, ServerConsoleHost>();
IoCManager.Register<IEntityManager, ServerEntityManager>();
IoCManager.Register<IEntityLookup, SharedEntityLookup>();
IoCManager.Register<IEntityNetworkManager, ServerEntityNetworkManager>();
IoCManager.Register<IServerEntityNetworkManager, ServerEntityNetworkManager>();
IoCManager.Register<IMapLoader, MapLoader>();

View File

@@ -118,8 +118,6 @@ namespace Robust.Shared.GameObjects
/// </summary>
GridId GridID { get; }
bool UpdateEntityTree(Box2? worldAABB = null);
void AttachToGridOrMap();
void AttachParent(ITransformComponent parent);
void AttachParent(IEntity parent);

View File

@@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Robust.Shared.Animations;
using Robust.Shared.Containers;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -125,7 +123,6 @@ namespace Robust.Shared.GameObjects
if (!DeferUpdates)
{
RebuildMatrices();
UpdateEntityTree();
Owner.EntityManager.EventBus.RaiseEvent(
EventSource.Local, new RotateEvent(Owner, oldRotation, _localRotation));
}
@@ -286,8 +283,6 @@ namespace Robust.Shared.GameObjects
Owner.EntityManager.EventBus.RaiseEvent(
EventSource.Local, new MoveEvent(Owner, oldPosition, Coordinates));
}
UpdateEntityTree();
}
else
{
@@ -319,7 +314,6 @@ namespace Robust.Shared.GameObjects
if (!DeferUpdates)
{
RebuildMatrices();
UpdateEntityTree();
Owner.EntityManager.EventBus.RaiseEvent(
EventSource.Local, new MoveEvent(Owner, oldGridPos, Coordinates));
}
@@ -418,8 +412,6 @@ namespace Robust.Shared.GameObjects
// so duplicate additions (which will happen) don't matter.
((TransformComponent) Parent!)._children.Add(Owner.Uid);
}
UpdateEntityTree();
}
/// <inheritdoc />
@@ -430,7 +422,6 @@ namespace Robust.Shared.GameObjects
// Keep the cached matrices in sync with the fields.
RebuildMatrices();
Dirty();
UpdateEntityTree();
}
public void RunDeferred(Box2 worldAABB)
@@ -443,7 +434,6 @@ namespace Robust.Shared.GameObjects
}
RebuildMatrices();
UpdateEntityTree(worldAABB);
if (_oldCoords != null)
{
@@ -579,7 +569,6 @@ namespace Robust.Shared.GameObjects
SetPosition(newParent.InvWorldMatrix.Transform(_localPosition));
RebuildMatrices();
Dirty();
UpdateEntityTree();
}
internal void ChangeMapId(MapId newMapId)
@@ -613,11 +602,6 @@ namespace Robust.Shared.GameObjects
private void MapIdChanged(MapId oldId)
{
if (oldId != MapId.Nullspace)
{
Owner.EntityManager.RemoveFromEntityTree(Owner, oldId);
}
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new EntMapIdChangedMessage(Owner, oldId));
}
@@ -723,7 +707,6 @@ namespace Robust.Shared.GameObjects
}
Dirty();
UpdateEntityTree();
}
if (nextState is TransformComponentState nextTransform)
@@ -788,8 +771,6 @@ namespace Robust.Shared.GameObjects
_invLocalMatrix = itransMat;
}
public bool UpdateEntityTree(Box2? worldAABB = null) => Owner.EntityManager.UpdateEntityTree(Owner, worldAABB);
public string GetDebugString()
{
return $"pos/rot/wpos/wrot: {Coordinates}/{LocalRotation}/{WorldPosition}/{WorldRotation}";

View File

@@ -30,16 +30,7 @@ namespace Robust.Shared.GameObjects
internal EntityLifeStage LifeStage
{
get => _lifeStage;
set
{
_lifeStage = value;
switch (value)
{
case EntityLifeStage.Initializing:
EntityManager.UpdateEntityTree(this);
break;
}
}
set => _lifeStage = value;
}
/// <inheritdoc />
@@ -189,8 +180,6 @@ namespace Robust.Shared.GameObjects
comp.Running = true;
}
}
EntityManager.UpdateEntityTree(this);
}
#endregion Initialization

View File

@@ -28,7 +28,6 @@ namespace Robust.Shared.GameObjects
[IoC.Dependency] private readonly IComponentFactory ComponentFactory = default!;
[IoC.Dependency] private readonly IComponentManager _componentManager = default!;
[IoC.Dependency] private readonly IGameTiming _gameTiming = default!;
[IoC.Dependency] private readonly IMapManager _mapManager = default!;
#endregion Dependencies
@@ -58,6 +57,7 @@ namespace Robust.Shared.GameObjects
public event EventHandler<EntityUid>? EntityAdded;
public event EventHandler<EntityUid>? EntityInitialized;
public event EventHandler<EntityUid>? EntityStarted;
public event EventHandler<EntityUid>? EntityDeleted;
public bool Started { get; protected set; }
@@ -81,9 +81,7 @@ namespace Robust.Shared.GameObjects
_componentManager.ComponentRemoved += (sender, args) => _eventBus.UnsubscribeEvents(args.Component);
}
public virtual void Startup()
{
}
public virtual void Startup() {}
public virtual void Shutdown()
{
@@ -179,40 +177,6 @@ namespace Robust.Shared.GameObjects
return query.Match(this);
}
public IEnumerable<IEntity> GetEntitiesInMap(MapId mapId)
{
if (!_entityTreesPerMap.TryGetValue(mapId, out var trees))
yield break;
foreach (var entity in trees)
{
if (!entity.Deleted)
yield return entity;
}
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesAt(MapId mapId, Vector2 position, bool approximate = false)
{
var list = new List<IEntity>();
var state = (list, position);
_entityTreesPerMap[mapId].QueryPoint(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
{
var transform = ent.Transform;
if (MathHelper.CloseTo(transform.Coordinates.X, state.position.X) &&
MathHelper.CloseTo(transform.Coordinates.Y, state.position.Y))
{
state.list.Add(ent);
}
return true;
}, position, approximate);
return list;
}
public IEnumerable<IEntity> GetEntities()
{
// Need to do an iterator loop to avoid issues with concurrent access.
@@ -282,13 +246,12 @@ namespace Robust.Shared.GameObjects
EntityDeleted?.Invoke(this, entity.Uid);
EventBus.RaiseEvent(EventSource.Local, new EntityDeletedMessage(entity));
}
public void DeleteEntity(EntityUid uid)
{
if (TryGetEntity(uid, out var entity))
{
DeleteEntity(entity);
UpdateEntityTree(entity);
}
}
@@ -409,9 +372,10 @@ namespace Robust.Shared.GameObjects
entity.InitializeComponents();
}
private protected static void StartEntity(Entity entity)
protected void StartEntity(Entity entity)
{
entity.StartAllComponents();
EntityStarted?.Invoke(this, entity.Uid);
}
private void CullDeletedEntities()
@@ -429,7 +393,6 @@ namespace Robust.Shared.GameObjects
AllEntities.RemoveSwap(i);
Entities.Remove(entity.Uid);
RemoveFromEntityTrees(entity);
// Process the one we just swapped next.
i--;
@@ -464,277 +427,6 @@ namespace Robust.Shared.GameObjects
/// Factory for generating a new EntityUid for an entity currently being created.
/// </summary>
protected abstract EntityUid GenerateEntityUid();
#region Spatial Queries
/// <inheritdoc />
public bool AnyEntitiesIntersecting(MapId mapId, Box2 box, bool approximate = false)
{
var found = false;
_entityTreesPerMap[mapId].QueryAabb(ref found, (ref bool found, in IEntity ent) =>
{
if (!ent.Deleted)
{
found = true;
return false;
}
return true;
}, box, approximate);
return found;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public void FastEntitiesIntersecting(in MapId mapId, ref Box2 position, EntityQueryCallback callback)
{
if (mapId == MapId.Nullspace)
return;
_entityTreesPerMap[mapId]._b2Tree
.FastQuery(ref position, (ref IEntity data) => callback(data));
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Box2 position, bool approximate = false)
{
if (mapId == MapId.Nullspace)
{
return Enumerable.Empty<IEntity>();
}
var list = new List<IEntity>();
_entityTreesPerMap[mapId].QueryAabb(ref list, (ref List<IEntity> list, in IEntity ent) =>
{
if (!ent.Deleted)
{
list.Add(ent);
}
return true;
}, position, approximate);
return list;
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Vector2 position, bool approximate = false)
{
const float range = .00001f / 2;
var aabb = new Box2(position, position).Enlarged(range);
if (mapId == MapId.Nullspace)
{
return Enumerable.Empty<IEntity>();
}
var list = new List<IEntity>();
var state = (list, position);
_entityTreesPerMap[mapId].QueryAabb(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
{
if (Intersecting(ent, state.position))
{
state.list.Add(ent);
}
return true;
}, aabb, approximate);
return list;
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(MapCoordinates position, bool approximate = false)
{
return GetEntitiesIntersecting(position.MapId, position.Position, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(EntityCoordinates position, bool approximate = false)
{
var mapPos = position.ToMap(this);
return GetEntitiesIntersecting(mapPos.MapId, mapPos.Position, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(IEntity entity, bool approximate = false)
{
if (entity.TryGetComponent<IPhysBody>(out var component))
{
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(), approximate);
}
return GetEntitiesIntersecting(entity.Transform.Coordinates, approximate);
}
/// <inheritdoc />
public bool IsIntersecting(IEntity entityOne, IEntity entityTwo)
{
var position = entityOne.Transform.MapPosition.Position;
return Intersecting(entityTwo, position);
}
private static bool Intersecting(IEntity entity, Vector2 mapPosition)
{
if (entity.TryGetComponent(out IPhysBody? component))
{
if (component.GetWorldAABB().Contains(mapPosition))
return true;
}
else
{
var transform = entity.Transform;
var entPos = transform.WorldPosition;
if (MathHelper.CloseTo(entPos.X, mapPosition.X)
&& MathHelper.CloseTo(entPos.Y, mapPosition.Y))
{
return true;
}
}
return false;
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(EntityCoordinates position, float range, bool approximate = false)
{
var mapCoordinates = position.ToMap(this);
var mapPosition = mapCoordinates.Position;
var aabb = new Box2(mapPosition - new Vector2(range / 2, range / 2),
mapPosition + new Vector2(range / 2, range / 2));
return GetEntitiesIntersecting(mapCoordinates.MapId, aabb, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(MapId mapId, Box2 box, float range, bool approximate = false)
{
var aabb = box.Enlarged(range);
return GetEntitiesIntersecting(mapId, aabb, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(MapId mapId, Vector2 point, float range, bool approximate = false)
{
var aabb = new Box2(point, point).Enlarged(range);
return GetEntitiesIntersecting(mapId, aabb, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(IEntity entity, float range, bool approximate = false)
{
if (entity.TryGetComponent<IPhysBody>(out var component))
{
return GetEntitiesInRange(entity.Transform.MapID, component.GetWorldAABB(), range, approximate);
}
return GetEntitiesInRange(entity.Transform.Coordinates, range, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInArc(EntityCoordinates coordinates, float range, Angle direction,
float arcWidth, bool approximate = false)
{
var position = coordinates.ToMap(this).Position;
foreach (var entity in GetEntitiesInRange(coordinates, range * 2, approximate))
{
var angle = new Angle(entity.Transform.WorldPosition - position);
if (angle.Degrees < direction.Degrees + arcWidth / 2 &&
angle.Degrees > direction.Degrees - arcWidth / 2)
yield return entity;
}
}
#endregion
#region Entity DynamicTree
private readonly Dictionary<MapId, DynamicTree<IEntity>> _entityTreesPerMap =
new();
public virtual bool UpdateEntityTree(IEntity entity, Box2? worldAABB = null)
{
if (entity.Deleted)
{
RemoveFromEntityTrees(entity);
return true;
}
if (!entity.Initialized || !Entities.ContainsKey(entity.Uid))
{
return false;
}
var transform = entity.Transform;
DebugTools.Assert(transform.Initialized);
var mapId = transform.MapID;
if (!_entityTreesPerMap.TryGetValue(mapId, out var entTree))
{
entTree = new DynamicTree<IEntity>(
GetWorldAabbFromEntity,
capacity: 16,
growthFunc: x => x == 16 ? 3840 : x + 256
);
_entityTreesPerMap.Add(mapId, entTree);
}
// for debugging
var necessary = 0;
if (entTree.AddOrUpdate(entity, worldAABB))
{
++necessary;
}
foreach (var childTx in entity.Transform.ChildEntityUids)
{
if (UpdateEntityTree(GetEntity(childTx)))
{
++necessary;
}
}
return necessary > 0;
}
public bool RemoveFromEntityTree(IEntity entity, MapId mapId)
{
if (_entityTreesPerMap.TryGetValue(mapId, out var tree))
{
return tree.Remove(entity);
}
return false;
}
private void RemoveFromEntityTrees(IEntity entity)
{
foreach (var mapId in _mapManager.GetAllMapIds())
{
if (_entityTreesPerMap.TryGetValue(mapId, out var entTree))
{
entTree.Remove(entity);
}
}
}
protected Box2 GetWorldAabbFromEntity(in IEntity ent)
{
if (ent.Deleted)
return new Box2(0, 0, 0, 0);
if (ent.TryGetComponent(out IPhysBody? collider))
return collider.GetWorldAABB(_mapManager);
var pos = ent.Transform.WorldPosition;
return new Box2(pos, pos);
}
#endregion
}
public enum EntityMessageType : byte

View File

@@ -34,6 +34,7 @@ namespace Robust.Shared.GameObjects
event EventHandler<EntityUid>? EntityAdded;
event EventHandler<EntityUid>? EntityInitialized;
event EventHandler<EntityUid>? EntityStarted;
event EventHandler<EntityUid>? EntityDeleted;
IEntity CreateEntityUninitialized(string? prototypeName);
@@ -82,13 +83,6 @@ namespace Robust.Shared.GameObjects
IEnumerable<IEntity> GetEntities();
/// <summary>
/// Yields all of the entities on a particular map. faster than GetEntities()
/// </summary>
/// <param name="mapId"></param>
/// <returns></returns>
IEnumerable<IEntity> GetEntitiesInMap(MapId mapId);
/// <summary>
/// Shuts-down and removes given <see cref="IEntity"/>. This is also broadcast to all clients.
/// </summary>
@@ -107,116 +101,5 @@ namespace Robust.Shared.GameObjects
bool EntityExists(EntityUid uid);
#endregion Entity Management
#region Spatial Queries
/// <summary>
/// Gets entities with a origin at the position.
/// </summary>
/// <param name="mapId"></param>
/// <param name="position"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
/// <returns></returns>
IEnumerable<IEntity> GetEntitiesAt(MapId mapId, Vector2 position, bool approximate = false);
/// <summary>
/// Checks if any entity is intersecting the box
/// </summary>
/// <param name="mapId"></param>
/// <param name="box"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
bool AnyEntitiesIntersecting(MapId mapId, Box2 box, bool approximate = false);
void FastEntitiesIntersecting(in MapId mapId, ref Box2 position, EntityQueryCallback callback);
/// <summary>
/// Gets entities with a bounding box that intersects this box
/// </summary>
/// <param name="mapId"></param>
/// <param name="position"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Box2 position, bool approximate = false);
/// <summary>
/// Gets entities with a bounding box that intersects this point
/// </summary>
/// <param name="mapId"></param>
/// <param name="position"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Vector2 position, bool approximate = false);
/// <summary>
/// Gets entities with a bounding box that intersects this point
/// </summary>
/// <param name="position"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesIntersecting(MapCoordinates position, bool approximate = false);
/// <summary>
/// Gets entities with a bounding box that intersects this point in coordinate form
/// </summary>
/// <param name="position"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesIntersecting(EntityCoordinates position, bool approximate = false);
/// <summary>
/// Gets entities that intersect with this entity
/// </summary>
/// <param name="entity"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesIntersecting(IEntity entity, bool approximate = false);
/// <summary>
/// Gets entities within a certain *square* range of this local coordinate
/// </summary>
/// <param name="position"></param>
/// <param name="range"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesInRange(EntityCoordinates position, float range, bool approximate = false);
/// <summary>
/// Gets entities within a certain *square* range of this entity
/// </summary>
/// <param name="entity"></param>
/// <param name="range"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesInRange(IEntity entity, float range, bool approximate = false);
/// <summary>
/// Gets whether two entities are intersecting each other
/// </summary>
/// <param name="entityOne"></param>
/// <param name="entityTwo"></param>
/// <returns></returns>
public bool IsIntersecting(IEntity entityOne, IEntity entityTwo);
/// <summary>
/// Gets entities within a certain *square* range of this bounding box
/// </summary>
/// <param name="mapID"></param>
/// <param name="box"></param>
/// <param name="range"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
IEnumerable<IEntity> GetEntitiesInRange(MapId mapID, Box2 box, float range, bool approximate = false);
/// <summary>
/// Get entities with bounding box in range of this whose center is within a certain directional arc, angle specifies center bisector of arc
/// </summary>
/// <param name="coordinates"></param>
/// <param name="range"></param>
/// <param name="direction"></param>
/// <param name="arcWidth"></param>
/// <param name="approximate">If true, will not recalculate precise entity AABBs, resulting in a perf increase. </param>
/// <returns></returns>
IEnumerable<IEntity> GetEntitiesInArc(EntityCoordinates coordinates, float range, Angle direction, float arcWidth, bool approximate = false);
#endregion
#region Spatial Updates
bool UpdateEntityTree(IEntity entity, Box2? worldAABB = null);
bool RemoveFromEntityTree(IEntity entity, MapId mapId);
#endregion
}
}

View File

@@ -0,0 +1,466 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects
{
public interface IEntityLookup
{
// Not an EntitySystem given EntityManager has a dependency on it which means it's just easier to IoC it for tests.
void Initialize();
void Shutdown();
void Update();
bool AnyEntitiesIntersecting(MapId mapId, Box2 box, bool approximate = false);
IEnumerable<IEntity> GetEntitiesInMap(MapId mapId);
IEnumerable<IEntity> GetEntitiesAt(MapId mapId, Vector2 position, bool approximate = false);
IEnumerable<IEntity> GetEntitiesInArc(EntityCoordinates coordinates, float range, Angle direction,
float arcWidth, bool approximate = false);
IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Box2 position, bool approximate = false);
IEnumerable<IEntity> GetEntitiesIntersecting(IEntity entity, bool approximate = false);
IEnumerable<IEntity> GetEntitiesIntersecting(MapCoordinates position, bool approximate = false);
IEnumerable<IEntity> GetEntitiesIntersecting(EntityCoordinates position, bool approximate = false);
IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Vector2 position, bool approximate = false);
void FastEntitiesIntersecting(in MapId mapId, ref Box2 position, EntityQueryCallback callback);
IEnumerable<IEntity> GetEntitiesInRange(EntityCoordinates position, float range, bool approximate = false);
IEnumerable<IEntity> GetEntitiesInRange(IEntity entity, float range, bool approximate = false);
IEnumerable<IEntity> GetEntitiesInRange(MapId mapId, Vector2 point, float range, bool approximate = false);
IEnumerable<IEntity> GetEntitiesInRange(MapId mapId, Box2 box, float range, bool approximate = false);
bool IsIntersecting(IEntity entityOne, IEntity entityTwo);
/// <summary>
/// Updates the lookup for this entity.
/// </summary>
/// <param name="entity"></param>
/// <param name="worldAABB">Pass in to avoid it being re-calculated</param>
/// <returns></returns>
bool UpdateEntityTree(IEntity entity, Box2? worldAABB = null);
void RemoveFromEntityTrees(IEntity entity);
Box2 GetWorldAabbFromEntity(in IEntity ent);
}
public class SharedEntityLookup : IEntityLookup, IEntityEventSubscriber
{
private readonly IEntityManager _entityManager;
private readonly IMapManager _mapManager;
private readonly Dictionary<MapId, DynamicTree<IEntity>> _entityTreesPerMap = new();
// Using stacks so we always use latest data (given we only run it once per entity).
private Stack<MoveEvent> _moveQueue = new();
private Stack<RotateEvent> _rotateQueue = new();
private Queue<EntMapIdChangedMessage> _mapChangeQueue = new();
/// <summary>
/// Move and rotate events generate the same update so no point duplicating work in the same tick.
/// </summary>
private HashSet<EntityUid> _handledThisTick = new();
// TODO: Should combine all of the methods that check for IPhysBody and just use the one GetWorldAabbFromEntity method
public SharedEntityLookup(IEntityManager entityManager, IMapManager mapManager)
{
_entityManager = entityManager;
_mapManager = mapManager;
}
public void Initialize()
{
var eventBus = _entityManager.EventBus;
eventBus.SubscribeEvent<MoveEvent>(EventSource.Local, this, ev => _moveQueue.Push(ev));
eventBus.SubscribeEvent<RotateEvent>(EventSource.Local, this, ev => _rotateQueue.Push(ev));
eventBus.SubscribeEvent<EntMapIdChangedMessage>(EventSource.Local, this, ev => _mapChangeQueue.Enqueue(ev));
_entityManager.EntityDeleted += HandleEntityDeleted;
_entityManager.EntityStarted += HandleEntityStarted;
_mapManager.MapCreated += HandleMapCreated;
_mapManager.MapDestroyed += HandleMapDestroyed;
}
public void Shutdown()
{
_entityManager.EventBus.UnsubscribeEvents(this);
_entityManager.EntityDeleted -= HandleEntityDeleted;
_entityManager.EntityStarted -= HandleEntityStarted;
_mapManager.MapCreated -= HandleMapCreated;
_mapManager.MapDestroyed -= HandleMapDestroyed;
}
private void HandleEntityDeleted(object? sender, EntityUid uid)
{
RemoveFromEntityTrees(_entityManager.GetEntity(uid));
}
private void HandleEntityStarted(object? sender, EntityUid uid)
{
UpdateEntityTree(_entityManager.GetEntity(uid));
}
private void HandleMapCreated(object? sender, MapEventArgs eventArgs)
{
_entityTreesPerMap[eventArgs.Map] = new DynamicTree<IEntity>(
GetWorldAabbFromEntity,
capacity: 16,
growthFunc: x => x == 16 ? 3840 : x + 256
);
}
private void HandleMapDestroyed(object? sender, MapEventArgs eventArgs)
{
_entityTreesPerMap.Remove(eventArgs.Map);
}
public void Update()
{
// Acruid said he'd deal with Update being called around IEntityManager later.
_handledThisTick.Clear();
while (_mapChangeQueue.TryDequeue(out var mapChangeEvent))
{
if (mapChangeEvent.Entity.Deleted) continue;
RemoveFromEntityTree(mapChangeEvent.Entity, mapChangeEvent.OldMapId);
UpdateEntityTree(mapChangeEvent.Entity, GetWorldAabbFromEntity(mapChangeEvent.Entity));
_handledThisTick.Add(mapChangeEvent.Entity.Uid);
}
while (_moveQueue.TryPop(out var moveEvent))
{
if (moveEvent.Sender.Deleted || _handledThisTick.Contains(moveEvent.Sender.Uid)) continue;
UpdateEntityTree(moveEvent.Sender, moveEvent.WorldAABB);
_handledThisTick.Add(moveEvent.Sender.Uid);
}
while (_rotateQueue.TryPop(out var rotateEvent))
{
if (rotateEvent.Sender.Deleted || _handledThisTick.Contains(rotateEvent.Sender.Uid)) continue;
UpdateEntityTree(rotateEvent.Sender, rotateEvent.WorldAABB);
}
}
#region Spatial Queries
/// <inheritdoc />
public bool AnyEntitiesIntersecting(MapId mapId, Box2 box, bool approximate = false)
{
var found = false;
_entityTreesPerMap[mapId].QueryAabb(ref found, (ref bool found, in IEntity ent) =>
{
if (!ent.Deleted)
{
found = true;
return false;
}
return true;
}, box, approximate);
return found;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public void FastEntitiesIntersecting(in MapId mapId, ref Box2 position, EntityQueryCallback callback)
{
if (mapId == MapId.Nullspace)
return;
_entityTreesPerMap[mapId]._b2Tree
.FastQuery(ref position, (ref IEntity data) => callback(data));
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Box2 position, bool approximate = false)
{
if (mapId == MapId.Nullspace)
{
return Enumerable.Empty<IEntity>();
}
var list = new List<IEntity>();
_entityTreesPerMap[mapId].QueryAabb(ref list, (ref List<IEntity> list, in IEntity ent) =>
{
if (!ent.Deleted)
{
list.Add(ent);
}
return true;
}, position, approximate);
return list;
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Vector2 position, bool approximate = false)
{
const float range = .00001f / 2;
var aabb = new Box2(position, position).Enlarged(range);
if (mapId == MapId.Nullspace)
{
return Enumerable.Empty<IEntity>();
}
var list = new List<IEntity>();
var state = (list, position);
_entityTreesPerMap[mapId].QueryAabb(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
{
if (Intersecting(ent, state.position))
{
state.list.Add(ent);
}
return true;
}, aabb, approximate);
return list;
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(MapCoordinates position, bool approximate = false)
{
return GetEntitiesIntersecting(position.MapId, position.Position, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(EntityCoordinates position, bool approximate = false)
{
var mapPos = position.ToMap(_entityManager);
return GetEntitiesIntersecting(mapPos.MapId, mapPos.Position, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesIntersecting(IEntity entity, bool approximate = false)
{
if (entity.TryGetComponent<IPhysBody>(out var component))
{
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(_mapManager), approximate);
}
return GetEntitiesIntersecting(entity.Transform.Coordinates, approximate);
}
/// <inheritdoc />
public bool IsIntersecting(IEntity entityOne, IEntity entityTwo)
{
var position = entityOne.Transform.MapPosition.Position;
return Intersecting(entityTwo, position);
}
private bool Intersecting(IEntity entity, Vector2 mapPosition)
{
if (entity.TryGetComponent(out IPhysBody? component))
{
if (component.GetWorldAABB(_mapManager).Contains(mapPosition))
return true;
}
else
{
var transform = entity.Transform;
var entPos = transform.WorldPosition;
if (MathHelper.CloseTo(entPos.X, mapPosition.X)
&& MathHelper.CloseTo(entPos.Y, mapPosition.Y))
{
return true;
}
}
return false;
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(EntityCoordinates position, float range, bool approximate = false)
{
var mapCoordinates = position.ToMap(_entityManager);
var mapPosition = mapCoordinates.Position;
var aabb = new Box2(mapPosition - new Vector2(range / 2, range / 2),
mapPosition + new Vector2(range / 2, range / 2));
return GetEntitiesIntersecting(mapCoordinates.MapId, aabb, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(MapId mapId, Box2 box, float range, bool approximate = false)
{
var aabb = box.Enlarged(range);
return GetEntitiesIntersecting(mapId, aabb, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(MapId mapId, Vector2 point, float range, bool approximate = false)
{
var aabb = new Box2(point, point).Enlarged(range);
return GetEntitiesIntersecting(mapId, aabb, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInRange(IEntity entity, float range, bool approximate = false)
{
if (entity.TryGetComponent<IPhysBody>(out var component))
{
return GetEntitiesInRange(entity.Transform.MapID, component.GetWorldAABB(_mapManager), range, approximate);
}
return GetEntitiesInRange(entity.Transform.Coordinates, range, approximate);
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInArc(EntityCoordinates coordinates, float range, Angle direction,
float arcWidth, bool approximate = false)
{
var position = coordinates.ToMap(_entityManager).Position;
foreach (var entity in GetEntitiesInRange(coordinates, range * 2, approximate))
{
var angle = new Angle(entity.Transform.WorldPosition - position);
if (angle.Degrees < direction.Degrees + arcWidth / 2 &&
angle.Degrees > direction.Degrees - arcWidth / 2)
yield return entity;
}
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesInMap(MapId mapId)
{
if (!_entityTreesPerMap.TryGetValue(mapId, out var trees))
yield break;
foreach (var entity in trees)
{
if (!entity.Deleted)
yield return entity;
}
}
/// <inheritdoc />
public IEnumerable<IEntity> GetEntitiesAt(MapId mapId, Vector2 position, bool approximate = false)
{
var list = new List<IEntity>();
var state = (list, position);
_entityTreesPerMap[mapId].QueryPoint(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
{
var transform = ent.Transform;
if (MathHelper.CloseTo(transform.Coordinates.X, state.position.X) &&
MathHelper.CloseTo(transform.Coordinates.Y, state.position.Y))
{
state.list.Add(ent);
}
return true;
}, position, approximate);
return list;
}
#endregion
#region Entity DynamicTree
/// <inheritdoc />
public virtual bool UpdateEntityTree(IEntity entity, Box2? worldAABB = null)
{
if (entity.Deleted)
{
RemoveFromEntityTrees(entity);
return true;
}
if (!entity.Initialized || !_entityManager.EntityExists(entity.Uid))
{
return false;
}
var transform = entity.Transform;
DebugTools.Assert(transform.Initialized);
var mapId = transform.MapID;
if (!_entityTreesPerMap.TryGetValue(mapId, out var entTree))
{
entTree = new DynamicTree<IEntity>(
GetWorldAabbFromEntity,
capacity: 16,
growthFunc: x => x == 16 ? 3840 : x + 256
);
_entityTreesPerMap.Add(mapId, entTree);
}
// for debugging
var necessary = 0;
if (entTree.AddOrUpdate(entity, worldAABB))
{
++necessary;
}
foreach (var childTx in entity.Transform.ChildEntityUids)
{
if (UpdateEntityTree(_entityManager.GetEntity(childTx)))
{
++necessary;
}
}
return necessary > 0;
}
public bool RemoveFromEntityTree(IEntity entity, MapId mapId)
{
if (_entityTreesPerMap.TryGetValue(mapId, out var tree))
{
return tree.Remove(entity);
}
return false;
}
/// <inheritdoc />
public void RemoveFromEntityTrees(IEntity entity)
{
foreach (var mapId in _mapManager.GetAllMapIds())
{
if (_entityTreesPerMap.TryGetValue(mapId, out var entTree))
{
entTree.Remove(entity);
}
}
}
public Box2 GetWorldAabbFromEntity(in IEntity ent)
{
if (ent.Deleted)
return new Box2(0, 0, 0, 0);
if (ent.TryGetComponent(out IPhysBody? collider))
return collider.GetWorldAABB(_mapManager);
var pos = ent.Transform.WorldPosition;
return new Box2(pos, pos);
}
#endregion
}
}

View File

@@ -132,15 +132,15 @@ namespace Robust.Shared.Physics
return false;
}
var box = _extractAabb(item);
aabb ??= _extractAabb(item);
if (CheckNaNs(box))
if (CheckNaNs(aabb.Value))
{
_nodeLookup[item] = Proxy.Free;
return true;
}
proxy = _b2Tree.CreateProxy(box, item);
proxy = _b2Tree.CreateProxy(aabb.Value, item);
_nodeLookup[item] = proxy;
return true;

View File

@@ -14,7 +14,6 @@ namespace Robust.Shared.Timing
internal sealed class PauseManager : IPauseManager, IPostInjectInit
{
[Dependency] private readonly IConsoleHost _conhost = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[ViewVariables] private readonly HashSet<MapId> _pausedMaps = new();
@@ -22,11 +21,13 @@ namespace Robust.Shared.Timing
public void SetMapPaused(MapId mapId, bool paused)
{
var lookupSystem = IoCManager.Resolve<IEntityLookup>();
if (paused)
{
_pausedMaps.Add(mapId);
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
foreach (var entity in lookupSystem.GetEntitiesInMap(mapId))
{
entity.Paused = true;
}
@@ -35,7 +36,7 @@ namespace Robust.Shared.Timing
{
_pausedMaps.Remove(mapId);
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
foreach (var entity in lookupSystem.GetEntitiesInMap(mapId))
{
entity.Paused = false;
}
@@ -49,7 +50,7 @@ namespace Robust.Shared.Timing
_unInitializedMaps.Remove(mapId);
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
foreach (var entity in IoCManager.Resolve<IEntityLookup>().GetEntitiesInMap(mapId))
{
entity.RunMapInit();
entity.Paused = false;
@@ -65,7 +66,7 @@ namespace Robust.Shared.Timing
{
var mapId = _mapManager.GetGrid(gridId).ParentMapId;
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
foreach (var entity in IoCManager.Resolve<IEntityLookup>().GetEntitiesInMap(mapId))
{
if (entity.Transform.GridID != gridId)
continue;

View File

@@ -73,6 +73,7 @@ namespace Robust.UnitTesting
entMan.Startup();
}
IoCManager.Resolve<IEntityLookup>().Initialize();
var mapMan = IoCManager.Resolve<IMapManager>();
mapMan.Initialize();
mapMan.Startup();
@@ -90,6 +91,11 @@ namespace Robust.UnitTesting
compFactory.Register<MetaDataComponent>();
compFactory.RegisterReference<MetaDataComponent, IMetaDataComponent>();
}
if(entMan.EventBus == null)
{
entMan.Startup();
}
}
[OneTimeTearDown]

View File

@@ -177,9 +177,10 @@ namespace Robust.UnitTesting.Server
//Tier 2: Simulation
container.Register<IServerEntityManager, ServerEntityManager>();
container.Register<IEntityManager, ServerEntityManager>();
container.Register<IMapManager, MapManager>();
container.Register<IEntityLookup, SharedEntityLookup>();
container.Register<ISerializationManager, SerializationManager>();
container.Register<IComponentManager, ComponentManager>();
container.Register<IMapManager, MapManager>();
container.Register<IPrototypeManager, PrototypeManager>();
container.Register<IComponentFactory, ComponentFactory>();
container.Register<IComponentDependencyManager, ComponentDependencyManager>();
@@ -219,6 +220,7 @@ namespace Robust.UnitTesting.Server
var entityMan = container.Resolve<IEntityManager>();
entityMan.Initialize();
IoCManager.Resolve<IEntityLookup>().Initialize();
_systemDelegate?.Invoke(container.Resolve<IEntitySystemManager>());
entityMan.Startup();