Lookup movement handling changes. (#3369)

This commit is contained in:
Leon Friedrich
2022-10-29 12:44:48 +13:00
committed by GitHub
parent 508754f4bc
commit 3fba59eb6c
5 changed files with 156 additions and 109 deletions

View File

@@ -15,8 +15,10 @@ using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Robust.Server.GameStates;
@@ -317,8 +319,13 @@ internal sealed partial class PVSSystem : EntitySystem
_transform.SetGridId(ev.Component, ev.Component.FindGridEntityId(GetEntityQuery<TransformComponent>()));
// since elements are cached grid-/map-relative, we dont need to update a given grids/maps children
if (ev.Component.GridUid == ev.Sender || !ev.Component.ParentUid.IsValid())
if (ev.Component.GridUid == ev.Sender)
return;
DebugTools.Assert(!_mapManager.IsGrid(ev.Sender));
if (ev.Component.MapUid == ev.Sender)
return;
DebugTools.Assert(!_mapManager.IsMap(ev.Sender));
var xformQuery = GetEntityQuery<TransformComponent>();
@@ -342,8 +349,13 @@ internal sealed partial class PVSSystem : EntitySystem
// use Startup because GridId is not set during the eventbus init yet!
// since elements are cached grid-/map-relative, we dont need to update a given grids/maps children
if (component.GridUid == uid || _mapManager.IsMap(uid))
if (component.GridUid == uid)
return;
DebugTools.Assert(!_mapManager.IsGrid(uid));
if (component.MapUid == uid)
return;
DebugTools.Assert(!_mapManager.IsMap(uid));
var xformQuery = GetEntityQuery<TransformComponent>();
var coordinates = _transform.GetMoverCoordinates(component, xformQuery);

View File

@@ -91,7 +91,14 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// The EntityUid of the map which this object is on, if any.
/// </summary>
public EntityUid? MapUid => _mapManager.MapExists(MapID) ? _mapManager.GetMapEntityId(MapID) : null;
public EntityUid? MapUid
{
get
{
var id = _mapManager.GetMapEntityId(MapID);
return id.IsValid() ? id : null;
}
}
/// <summary>
/// Defer updates to the EntityTree and MoveEvent calls if toggled.

View File

@@ -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))

View File

@@ -790,8 +790,11 @@ public abstract partial class SharedTransformSystem
var meta = metaQuery.GetComponent(xform.Owner);
_metaSys.SetEntityPaused(xform.Owner, mapPaused, meta);
xform.MapID = newMapId;
xform.UpdateChildMapIdsRecursive(xform.MapID, mapPaused, xformQuery, metaQuery, _metaSys);
// Map entities retain their map Uids
if (xform.Owner != xform.MapUid)
xform.MapID = newMapId;
xform.UpdateChildMapIdsRecursive(newMapId, mapPaused, xformQuery, metaQuery, _metaSys);
}
public void DetachParentToNull(TransformComponent xform)
@@ -844,7 +847,7 @@ public abstract partial class SharedTransformSystem
// aaaaaaaaaaaaaaaa
ChangeMapId(xform, MapId.Nullspace, xformQuery, metaQuery);
if (xform.GridUid != null)
if (xform.GridUid != null && xform.GridUid != xform.Owner)
SetGridId(xform, null, xformQuery);
var entParentChangedMessage = new EntParentChangedMessage(xform.Owner, oldParent, oldMap, xform);

View File

@@ -1112,7 +1112,6 @@ namespace Robust.Shared.Physics
throw new InvalidOperationException(msg);
}
private IEnumerable<(Proxy, Node)> DebugAllocatedNodesEnumerable
{
get