mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
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:
@@ -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();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -79,8 +79,8 @@ namespace Robust.Server.GameObjects
|
||||
/// <inheritdoc />
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
EntitySystemManager.Initialize();
|
||||
base.Startup();
|
||||
Started = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>())
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
466
Robust.Shared/GameObjects/Systems/SharedEntityLookup.cs
Normal file
466
Robust.Shared/GameObjects/Systems/SharedEntityLookup.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user