|
|
|
|
@@ -68,28 +68,16 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const LookupFlags DefaultFlags = LookupFlags.Contained | LookupFlags.Dynamic | LookupFlags.Static | LookupFlags.Sundries;
|
|
|
|
|
|
|
|
|
|
private const int GrowthRate = 256;
|
|
|
|
|
|
|
|
|
|
private const float PointEnlargeRange = .00001f / 2;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Like RenderTree we need to enlarge our lookup range for EntityLookupComponent as an entity is only ever on
|
|
|
|
|
/// 1 EntityLookupComponent at a time (hence it may overlap without another lookup).
|
|
|
|
|
/// </summary>
|
|
|
|
|
private float _lookupEnlargementRange;
|
|
|
|
|
|
|
|
|
|
public override void Initialize()
|
|
|
|
|
{
|
|
|
|
|
base.Initialize();
|
|
|
|
|
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
|
|
|
|
configManager.OnValueChanged(CVars.LookupEnlargementRange, value => _lookupEnlargementRange = value, true);
|
|
|
|
|
|
|
|
|
|
SubscribeLocalEvent<BroadphaseComponent, ComponentAdd>(OnBroadphaseAdd);
|
|
|
|
|
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
|
|
|
|
SubscribeLocalEvent<MapChangedEvent>(OnMapChange);
|
|
|
|
|
|
|
|
|
|
SubscribeLocalEvent<MoveEvent>(OnMove);
|
|
|
|
|
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChange);
|
|
|
|
|
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(OnContainerInsert);
|
|
|
|
|
SubscribeLocalEvent<EntRemovedFromContainerMessage>(OnContainerRemove);
|
|
|
|
|
|
|
|
|
|
@@ -123,14 +111,16 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
DebugTools.Assert(!_container.IsEntityOrParentInContainer(uid, meta, xform, null, xformQuery));
|
|
|
|
|
|
|
|
|
|
var broadQuery = GetEntityQuery<BroadphaseComponent>();
|
|
|
|
|
|
|
|
|
|
// TODO combine mover-coordinate fetching with BroadphaseComponent fetching. They're kinda the same thing.
|
|
|
|
|
var lookup = GetBroadphase(uid, xform, broadQuery, xformQuery);
|
|
|
|
|
|
|
|
|
|
if (lookup == null) return;
|
|
|
|
|
|
|
|
|
|
var coordinates = _transform.GetMoverCoordinates(xform.Coordinates, xformQuery);
|
|
|
|
|
var lookupRotation = _transform.GetWorldRotation(lookup.Owner, xformQuery);
|
|
|
|
|
// If we're contained then LocalRotation should be 0 anyway.
|
|
|
|
|
var aabb = GetAABBNoContainer(xform.Owner, coordinates.Position, _transform.GetWorldRotation(xform, xformQuery) - lookupRotation);
|
|
|
|
|
var (coordinates, rotation) = _transform.GetMoverCoordinateRotation(xform, xformQuery);
|
|
|
|
|
var relativeRotation = rotation - lookupRotation;
|
|
|
|
|
var aabb = GetAABBNoContainer(xform.Owner, coordinates.Position, relativeRotation);
|
|
|
|
|
|
|
|
|
|
// TODO: Only container children need updating so could manually do this slightly better.
|
|
|
|
|
AddToEntityTree(lookup, xform, aabb, xformQuery, lookupRotation);
|
|
|
|
|
@@ -237,16 +227,70 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Entity Updating
|
|
|
|
|
private void UpdatePosition(EntityUid uid, TransformComponent xform, BroadphaseComponent? lookup, EntityQuery<TransformComponent> xformQuery)
|
|
|
|
|
{
|
|
|
|
|
if (lookup == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var lookupRotation = _transform.GetWorldRotation(lookup.Owner, xformQuery);
|
|
|
|
|
var (coordinates, rotation) = _transform.GetMoverCoordinateRotation(xform, xformQuery);
|
|
|
|
|
var relativeRotation = rotation - lookupRotation;
|
|
|
|
|
var aabb = GetAABBNoContainer(uid, coordinates.Position, relativeRotation);
|
|
|
|
|
AddToEntityTree(lookup, xform, aabb, xformQuery, lookupRotation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateParent(EntityUid uid,
|
|
|
|
|
TransformComponent xform,
|
|
|
|
|
BroadphaseComponent? lookup,
|
|
|
|
|
EntityQuery<TransformComponent> xformQuery,
|
|
|
|
|
EntityQuery<BroadphaseComponent> broadQuery,
|
|
|
|
|
EntityUid oldParent)
|
|
|
|
|
{
|
|
|
|
|
BroadphaseComponent? oldLookup = null;
|
|
|
|
|
if (oldParent.IsValid()
|
|
|
|
|
&& oldParent != uid // implies the entity was in null-space
|
|
|
|
|
&& xformQuery.TryGetComponent(oldParent, out var parentXform)
|
|
|
|
|
&& parentXform.MapID != MapId.Nullspace // see comment below
|
|
|
|
|
&& !broadQuery.TryGetComponent(oldParent, out oldLookup))
|
|
|
|
|
{
|
|
|
|
|
oldLookup = GetBroadphase(oldParent, parentXform, broadQuery, xformQuery);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Note that the parentXform.MapID != MapId.Nullspace is required because currently grids are not allowed to
|
|
|
|
|
// ever enter null-space. If they are in null-space, we assume that the grid is being deleted, as otherwise
|
|
|
|
|
// RemoveFromEntityTree() will explode. This may eventually have to change if we stop universally sending
|
|
|
|
|
// all grids to all players (i.e., out-of view grids will need to get sent to null-space)
|
|
|
|
|
//
|
|
|
|
|
// This also means the queries above can be reverted (check broadQuery, then xformQuery, as this will
|
|
|
|
|
// generally save a component lookup.
|
|
|
|
|
|
|
|
|
|
// If lookup remained unchanged we just update the position as normal
|
|
|
|
|
if (oldLookup == lookup)
|
|
|
|
|
{
|
|
|
|
|
UpdatePosition(uid, xform, lookup, xformQuery);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RemoveFromEntityTree(oldLookup, xform, xformQuery);
|
|
|
|
|
|
|
|
|
|
if (lookup != null)
|
|
|
|
|
AddToEntityTree(lookup, xform, xformQuery, _transform.GetWorldRotation(lookup.Owner, xformQuery));
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Entity events
|
|
|
|
|
|
|
|
|
|
private void OnPhysicsUpdate(ref CollisionChangeEvent ev)
|
|
|
|
|
{
|
|
|
|
|
if (HasComp<IMapGridComponent>(ev.Body.Owner))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
|
|
|
var xform = xformQuery.GetComponent(ev.Body.Owner);
|
|
|
|
|
|
|
|
|
|
if (xform.GridUid == ev.Body.Owner)
|
|
|
|
|
return;
|
|
|
|
|
DebugTools.Assert(!_mapManager.IsGrid(ev.Body.Owner));
|
|
|
|
|
|
|
|
|
|
if (!ev.CanCollide && _container.IsEntityOrParentInContainer(ev.Body.Owner, null, xform, null, xformQuery))
|
|
|
|
|
{
|
|
|
|
|
// getting inserted, skip sundries insertion and just let container insertion handle tree removal.
|
|
|
|
|
@@ -289,6 +333,7 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
|
|
|
|
|
if (xform.GridUid == uid)
|
|
|
|
|
return;
|
|
|
|
|
DebugTools.Assert(!_mapManager.IsGrid(uid));
|
|
|
|
|
|
|
|
|
|
// fun fact: container insertion tries to update the fucking lookups like 3 or more times, each time iterating through all of its parents.
|
|
|
|
|
if (_container.IsEntityOrParentInContainer(uid, null, xform, null, xformQuery))
|
|
|
|
|
@@ -317,15 +362,22 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
if (!Resolve(body.Owner, ref manager))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!TryComp<TransformComponent>(lookup.Owner, out var lookupXform) || lookupXform.MapUid == null)
|
|
|
|
|
if (!TryComp<TransformComponent>(lookup.Owner, out var lookupXform))
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
throw new InvalidOperationException("Lookup does not exist?");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var map = lookupXform.MapUid;
|
|
|
|
|
if (map == null)
|
|
|
|
|
{
|
|
|
|
|
// See the comments in UpdateParent()
|
|
|
|
|
throw new NotSupportedException("Nullspace lookups are not supported.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tree = bodyType == BodyType.Static ? lookup.StaticTree : lookup.DynamicTree;
|
|
|
|
|
var moveBuffer = Comp<SharedPhysicsMapComponent>(lookupXform.MapUid.Value).MoveBuffer;
|
|
|
|
|
var moveBuffer = Comp<SharedPhysicsMapComponent>(map.Value).MoveBuffer;
|
|
|
|
|
|
|
|
|
|
foreach (var (_, fixture) in manager.Fixtures)
|
|
|
|
|
foreach (var fixture in manager.Fixtures.Values)
|
|
|
|
|
{
|
|
|
|
|
DestroyProxies(fixture, tree, moveBuffer);
|
|
|
|
|
}
|
|
|
|
|
@@ -449,12 +501,12 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
// If nullspace or the likes.
|
|
|
|
|
if (lookup == null) return;
|
|
|
|
|
|
|
|
|
|
var coordinates = _transform.GetMoverCoordinates(xform.Coordinates, xformQuery);
|
|
|
|
|
DebugTools.Assert(coordinates.EntityId == lookup.Owner);
|
|
|
|
|
var lookupRotation = _transform.GetWorldRotation(lookup.Owner, xformQuery);
|
|
|
|
|
var (coordinates, rotation) = _transform.GetMoverCoordinateRotation(xform, xformQuery);
|
|
|
|
|
var relativeRotation = rotation - lookupRotation;
|
|
|
|
|
DebugTools.Assert(coordinates.EntityId == lookup.Owner);
|
|
|
|
|
|
|
|
|
|
// If we're contained then LocalRotation should be 0 anyway.
|
|
|
|
|
var aabb = GetAABBNoContainer(uid, coordinates.Position, _transform.GetWorldRotation(xform, xformQuery) - lookupRotation);
|
|
|
|
|
var aabb = GetAABBNoContainer(uid, coordinates.Position, relativeRotation);
|
|
|
|
|
|
|
|
|
|
// Any child entities should be handled by their own OnEntityInit
|
|
|
|
|
AddToEntityTree(lookup, xform, aabb, xformQuery, lookupRotation, false);
|
|
|
|
|
@@ -462,80 +514,50 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
|
|
|
|
|
private void OnMove(ref MoveEvent args)
|
|
|
|
|
{
|
|
|
|
|
// Even if the entity is contained it may have children that aren't so we still need to update.
|
|
|
|
|
if (!CanMoveUpdate(args.Sender))
|
|
|
|
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
|
|
|
|
|
|
|
|
// TODO remove this check after #3368 gets merged
|
|
|
|
|
if (args.Component.LifeStage < ComponentLifeStage.Initialized && args.Component.GridUid == null)
|
|
|
|
|
_transform.SetGridId(args.Component, args.Component.FindGridEntityId(xformQuery));
|
|
|
|
|
|
|
|
|
|
// Is this a grid?
|
|
|
|
|
if (args.Component.GridUid == args.Sender)
|
|
|
|
|
return;
|
|
|
|
|
DebugTools.Assert(!_mapManager.IsGrid(args.Sender));
|
|
|
|
|
|
|
|
|
|
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
|
|
|
|
var meta = metaQuery.GetComponent(args.Sender);
|
|
|
|
|
|
|
|
|
|
if (meta.EntityLifeStage < EntityLifeStage.Initialized)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_container.IsEntityOrParentInContainer(args.Sender, meta, args.Component, metaQuery, xformQuery))
|
|
|
|
|
{
|
|
|
|
|
// This move might be due to a parent change as a result of getting inserted into a container. In that
|
|
|
|
|
// case, we will just let the container insert event handle that. Note that the in-container flag gets
|
|
|
|
|
// set BEFORE insert parent change, and gets unset before the container removal parent-change. So if it
|
|
|
|
|
// is set here, this must mean we are getting inserted.
|
|
|
|
|
//
|
|
|
|
|
// However, this means that this method will still get run in full on container removal. Additionally,
|
|
|
|
|
// because not all container removals are guaranteed to result in a parent change, container removal
|
|
|
|
|
// events also need to add the entity to a tree. So if an entity gets ejected/teleported to some other
|
|
|
|
|
// grid this results in add-to-tree -> remove-from-tree -> add-to-tree.
|
|
|
|
|
//
|
|
|
|
|
// TODO IMPROVE CONTAINER REMOVAL HANDLING
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.Component.MapUid == args.Sender)
|
|
|
|
|
return;
|
|
|
|
|
DebugTools.Assert(!_mapManager.IsMap(args.Sender));
|
|
|
|
|
|
|
|
|
|
var broadQuery = GetEntityQuery<BroadphaseComponent>();
|
|
|
|
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
|
|
|
var lookup = GetBroadphase(args.Sender, args.Component, broadQuery, xformQuery);
|
|
|
|
|
|
|
|
|
|
if (lookup == null) return;
|
|
|
|
|
|
|
|
|
|
var xform = args.Component;
|
|
|
|
|
var coordinates = _transform.GetMoverCoordinates(xform.Coordinates, xformQuery);
|
|
|
|
|
var lookupRotation = _transform.GetWorldRotation(lookup.Owner, xformQuery);
|
|
|
|
|
var aabb = GetAABBNoContainer(args.Sender, coordinates.Position, _transform.GetWorldRotation(xform) - lookupRotation);
|
|
|
|
|
AddToEntityTree(lookup, xform, aabb, xformQuery, lookupRotation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool CanMoveUpdate(EntityUid uid)
|
|
|
|
|
{
|
|
|
|
|
return !_mapManager.IsMap(uid) &&
|
|
|
|
|
!_mapManager.IsGrid(uid) &&
|
|
|
|
|
!_container.IsEntityInContainer(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnParentChange(ref EntParentChangedMessage args)
|
|
|
|
|
{
|
|
|
|
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
|
|
|
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
|
|
|
|
var meta = metaQuery.GetComponent(args.Entity);
|
|
|
|
|
var xform = args.Transform;
|
|
|
|
|
|
|
|
|
|
// If our parent is changing due to a container-insert, we let the container insert event handle that. Note
|
|
|
|
|
// that the in-container flag gets set BEFORE insert parent change, and gets unset before the container
|
|
|
|
|
// removal parent-change. So if it is set here, this must mean we are getting inserted.
|
|
|
|
|
//
|
|
|
|
|
// However, this means that this method will still get run in full on container removal. Additionally,
|
|
|
|
|
// because not all container removals are guaranteed to result in a parent change, container removal events
|
|
|
|
|
// also need to add the entity to a tree. So this generally results in:
|
|
|
|
|
// add-to-tree -> remove-from-tree -> add-to-tree.
|
|
|
|
|
// Though usually, `oldLookup == newLookup` for the last step. Its still shit though.
|
|
|
|
|
//
|
|
|
|
|
// TODO IMPROVE CONTAINER REMOVAL HANDLING
|
|
|
|
|
|
|
|
|
|
if (_container.IsEntityOrParentInContainer(args.Entity, meta, xform, metaQuery, xformQuery))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (meta.EntityLifeStage < EntityLifeStage.Initialized ||
|
|
|
|
|
_mapManager.IsGrid(args.Entity) ||
|
|
|
|
|
_mapManager.IsMap(args.Entity))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var broadQuery = GetEntityQuery<BroadphaseComponent>();
|
|
|
|
|
BroadphaseComponent? oldLookup = null;
|
|
|
|
|
|
|
|
|
|
if (args.OldMapId != MapId.Nullspace && xformQuery.TryGetComponent(args.OldParent, out var parentXform))
|
|
|
|
|
{
|
|
|
|
|
// If the old parent has a broadphase return that, otherwise return the parent's broadphase.
|
|
|
|
|
if (!broadQuery.TryGetComponent(args.OldParent.Value, out oldLookup))
|
|
|
|
|
{
|
|
|
|
|
oldLookup = GetBroadphase(args.OldParent.Value, parentXform, broadQuery, xformQuery);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var newLookup = GetBroadphase(args.Entity, xform, broadQuery, xformQuery);
|
|
|
|
|
|
|
|
|
|
// If parent is the same then no need to do anything as position should stay the same.
|
|
|
|
|
if (oldLookup == newLookup) return;
|
|
|
|
|
|
|
|
|
|
RemoveFromEntityTree(oldLookup, xform, xformQuery);
|
|
|
|
|
|
|
|
|
|
if (newLookup != null)
|
|
|
|
|
AddToEntityTree(newLookup, xform, xformQuery, _transform.GetWorldRotation(newLookup.Owner, xformQuery));
|
|
|
|
|
if (args.ParentChanged)
|
|
|
|
|
UpdateParent(args.Sender, args.Component, lookup, xformQuery, broadQuery, args.OldPosition.EntityId);
|
|
|
|
|
else
|
|
|
|
|
UpdatePosition(args.Sender, args.Component, lookup, xformQuery);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnContainerRemove(EntRemovedFromContainerMessage ev)
|
|
|
|
|
@@ -577,9 +599,10 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
Angle lookupRotation,
|
|
|
|
|
bool recursive = true)
|
|
|
|
|
{
|
|
|
|
|
var coordinates = _transform.GetMoverCoordinates(xform.Coordinates, xformQuery);
|
|
|
|
|
// If we're contained then LocalRotation should be 0 anyway.
|
|
|
|
|
var aabb = GetAABBNoContainer(xform.Owner, coordinates.Position, _transform.GetWorldRotation(xform, xformQuery) - lookupRotation);
|
|
|
|
|
// TODO combine mover-coordinate fetching with BroadphaseComponent fetching. They're kinda the same thing.
|
|
|
|
|
var (coordinates, rotation) = _transform.GetMoverCoordinateRotation(xform, xformQuery);
|
|
|
|
|
var relativeRotation = rotation - lookupRotation;
|
|
|
|
|
var aabb = GetAABBNoContainer(xform.Owner, coordinates.Position, relativeRotation);
|
|
|
|
|
AddToEntityTree(lookup, xform, aabb, xformQuery, lookupRotation, recursive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -609,9 +632,10 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
if (conManager.ContainsEntity(child.Value)) continue;
|
|
|
|
|
|
|
|
|
|
var childXform = xformQuery.GetComponent(child.Value);
|
|
|
|
|
var coordinates = _transform.GetMoverCoordinates(childXform.Coordinates, xformQuery);
|
|
|
|
|
var (coordinates, rotation) = _transform.GetMoverCoordinateRotation(childXform, xformQuery);
|
|
|
|
|
var relativeRotation = rotation - lookupRotation;
|
|
|
|
|
// TODO: If we have 0 position and not contained can optimise these further, but future problem.
|
|
|
|
|
var childAABB = GetAABBNoContainer(child.Value, coordinates.Position, childXform.WorldRotation - lookupRotation);
|
|
|
|
|
var childAABB = GetAABBNoContainer(child.Value, coordinates.Position, relativeRotation);
|
|
|
|
|
AddToEntityTree(lookup, childXform, childAABB, xformQuery, lookupRotation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -620,9 +644,10 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
while (childEnumerator.MoveNext(out var child))
|
|
|
|
|
{
|
|
|
|
|
var childXform = xformQuery.GetComponent(child.Value);
|
|
|
|
|
var coordinates = _transform.GetMoverCoordinates(childXform.Coordinates, xformQuery);
|
|
|
|
|
var (coordinates, rotation) = _transform.GetMoverCoordinateRotation(childXform, xformQuery);
|
|
|
|
|
var relativeRotation = rotation - lookupRotation;
|
|
|
|
|
// TODO: If we have 0 position and not contained can optimise these further, but future problem.
|
|
|
|
|
var childAABB = GetAABBNoContainer(child.Value, coordinates.Position, childXform.WorldRotation - lookupRotation);
|
|
|
|
|
var childAABB = GetAABBNoContainer(child.Value, coordinates.Position, relativeRotation);
|
|
|
|
|
AddToEntityTree(lookup, childXform, childAABB, xformQuery, lookupRotation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -704,6 +729,7 @@ namespace Robust.Shared.GameObjects
|
|
|
|
|
|
|
|
|
|
// if it's map (or in null-space) return null. Grids should return the map's broadphase.
|
|
|
|
|
|
|
|
|
|
// TODO provide variant that also returns world rotation (and maybe position). Avoids having to iterate though parents twice.
|
|
|
|
|
while (parent.IsValid())
|
|
|
|
|
{
|
|
|
|
|
if (broadQuery.TryGetComponent(parent, out var comp))
|
|
|
|
|
|