mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Fix some physics bugs (#4746)
* Fix SetAwake() * Remove bad debug assert * EntityQuery & misc optimizations * Remove bad(?) code, and add new test * I love engine tests * Add debug assert
This commit is contained in:
@@ -99,8 +99,8 @@ public sealed partial class PhysicsSystem
|
||||
if (xform.MapUid is not { } map)
|
||||
continue;
|
||||
|
||||
if (maps.Add(map) && TryComp(map, out PhysicsMapComponent? physMap) &&
|
||||
TryComp(map, out MapComponent? mapComp))
|
||||
if (maps.Add(map) && PhysMapQuery.TryGetComponent(map, out var physMap) &&
|
||||
MapQuery.TryGetComponent(map, out var mapComp))
|
||||
_broadphase.FindNewContacts(physMap, mapComp.MapId);
|
||||
|
||||
contacts.AddRange(physics.Contacts);
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Robust.Shared.GameObjects
|
||||
public sealed class CollisionWakeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
private EntityQuery<CollisionWakeComponent> _query;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -19,18 +20,17 @@ namespace Robust.Shared.GameObjects
|
||||
SubscribeLocalEvent<CollisionWakeComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, ComponentHandleState>(OnHandleState);
|
||||
|
||||
SubscribeLocalEvent<CollisionWakeComponent, PhysicsWakeEvent>(OnWake);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, PhysicsSleepEvent>(OnSleep);
|
||||
|
||||
SubscribeLocalEvent<CollisionWakeComponent, JointAddedEvent>(OnJointAdd);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, JointRemovedEvent>(OnJointRemove);
|
||||
|
||||
SubscribeLocalEvent<CollisionWakeComponent, EntParentChangedMessage>(OnParentChange);
|
||||
|
||||
_query = GetEntityQuery<CollisionWakeComponent>();
|
||||
}
|
||||
|
||||
public void SetEnabled(EntityUid uid, bool enabled, CollisionWakeComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component) || component.Enabled == enabled)
|
||||
if (!_query.Resolve(uid, ref component) || component.Enabled == enabled)
|
||||
return;
|
||||
|
||||
component.Enabled = enabled;
|
||||
@@ -89,17 +89,13 @@ namespace Robust.Shared.GameObjects
|
||||
_physics.SetCanCollide(uid, true);
|
||||
}
|
||||
|
||||
private void OnWake(EntityUid uid, CollisionWakeComponent component, ref PhysicsWakeEvent args)
|
||||
internal void UpdateCanCollide(Entity<PhysicsComponent> entity, bool checkTerminating = true, bool dirty = true)
|
||||
{
|
||||
UpdateCanCollide(uid, component, args.Body, checkTerminating: false);
|
||||
if (_query.TryGetComponent(entity, out var wakeComp))
|
||||
UpdateCanCollide(entity.Owner, wakeComp, entity.Comp, checkTerminating: checkTerminating, dirty: dirty);
|
||||
}
|
||||
|
||||
private void OnSleep(EntityUid uid, CollisionWakeComponent component, ref PhysicsSleepEvent args)
|
||||
{
|
||||
UpdateCanCollide(uid, component, args.Body);
|
||||
}
|
||||
|
||||
private void UpdateCanCollide(
|
||||
internal void UpdateCanCollide(
|
||||
EntityUid uid,
|
||||
CollisionWakeComponent component,
|
||||
PhysicsComponent? body = null,
|
||||
|
||||
@@ -231,7 +231,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (!TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
return;
|
||||
|
||||
if (!TryComp(xform.MapUid, out PhysicsMapComponent? physMap))
|
||||
if (!_mapQuery.TryGetComponent(xform.MapUid, out var physMap))
|
||||
throw new InvalidOperationException();
|
||||
|
||||
var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform);
|
||||
@@ -304,14 +304,14 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
xform.Broadphase = null;
|
||||
|
||||
if (!TryComp(old.Uid, out BroadphaseComponent? broadphase))
|
||||
if (!_broadQuery.TryGetComponent(old.Uid, out var broadphase))
|
||||
return; // broadphase probably got deleted.
|
||||
|
||||
// remove from the old broadphase
|
||||
var fixtures = Comp<FixturesComponent>(uid);
|
||||
if (old.CanCollide)
|
||||
{
|
||||
TryComp(old.PhysicsMap, out PhysicsMapComponent? physicsMap);
|
||||
_mapQuery.TryGetComponent(old.PhysicsMap, out var physicsMap);
|
||||
RemoveBroadTree(broadphase, fixtures, old.Static, physicsMap);
|
||||
}
|
||||
else
|
||||
@@ -355,7 +355,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (broadphaseXform.MapID == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
if (!TryComp(broadphaseXform.MapUid, out PhysicsMapComponent? physMap))
|
||||
if (!_mapQuery.TryGetComponent(broadphaseXform.MapUid, out var physMap))
|
||||
throw new InvalidOperationException($"Physics Broadphase is missing physics map. {ToPrettyString(broadUid)}");
|
||||
|
||||
AddOrUpdatePhysicsTree(uid, broadUid, broadphase, broadphaseXform, physMap, xform, body, fixtures, xformQuery);
|
||||
@@ -488,9 +488,8 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
DebugTools.Assert(!newMap.IsValid() || HasComp<MapComponent>(newMap));
|
||||
DebugTools.Assert(!oldMap.IsValid() || HasComp<MapComponent>(oldMap));
|
||||
|
||||
var oldBuffer = CompOrNull<PhysicsMapComponent>(oldMap)?.MoveBuffer;
|
||||
var newBuffer = CompOrNull<PhysicsMapComponent>(newMap)?.MoveBuffer;
|
||||
|
||||
var oldBuffer = _mapQuery.CompOrNull(oldMap)?.MoveBuffer;
|
||||
var newBuffer = _mapQuery.CompOrNull(newMap)?.MoveBuffer;
|
||||
|
||||
foreach (var child in args.Component._children)
|
||||
{
|
||||
@@ -559,7 +558,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (!xform.Broadphase.Value.IsValid())
|
||||
return; // Entity is intentionally not on a broadphase (deferred updating?).
|
||||
|
||||
TryComp(xform.Broadphase.Value.PhysicsMap, out oldPhysMap);
|
||||
_mapQuery.TryGetComponent(xform.Broadphase.Value.PhysicsMap, out oldPhysMap);
|
||||
|
||||
if (!_broadQuery.TryGetComponent(xform.Broadphase.Value.Uid, out oldBroadphase))
|
||||
{
|
||||
@@ -580,18 +579,6 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
if (oldBroadphase != null && _xformQuery.GetComponent(oldParent).MapID == MapId.Nullspace)
|
||||
{
|
||||
oldBroadphase = null;
|
||||
// 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.
|
||||
}
|
||||
|
||||
TryFindBroadphase(xform, out var newBroadphase);
|
||||
|
||||
if (oldBroadphase != null && oldBroadphase != newBroadphase)
|
||||
@@ -603,7 +590,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
return;
|
||||
|
||||
var newBroadphaseXform = _xformQuery.GetComponent(newBroadphase.Owner);
|
||||
if (!TryComp(newBroadphaseXform.MapUid, out PhysicsMapComponent? physMap))
|
||||
if (!_mapQuery.TryGetComponent(newBroadphaseXform.MapUid, out var physMap))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Broadphase's map is missing a physics map comp. Broadphase: {ToPrettyString(newBroadphase.Owner)}");
|
||||
@@ -648,7 +635,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
bool recursive = true)
|
||||
{
|
||||
var broadphaseXform = _xformQuery.GetComponent(broadphase.Owner);
|
||||
if (!TryComp(broadphaseXform.MapUid, out PhysicsMapComponent? physMap))
|
||||
if (!_mapQuery.TryGetComponent(broadphaseXform.MapUid, out var physMap))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Broadphase's map is missing a physics map comp. Broadphase: {ToPrettyString(broadphase.Owner)}");
|
||||
@@ -730,7 +717,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
return;
|
||||
|
||||
PhysicsMapComponent? physMap = null;
|
||||
if (xform.Broadphase!.Value.PhysicsMap is { Valid: true } map && !TryComp(map, out physMap))
|
||||
if (xform.Broadphase!.Value.PhysicsMap is { Valid: true } map && !_mapQuery.TryGetComponent(map, out physMap))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Broadphase's map is missing a physics map comp. Broadphase: {ToPrettyString(broadphase.Owner)}");
|
||||
@@ -768,7 +755,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
if (old.PhysicsMap.IsValid() && physicsMap?.Owner != old.PhysicsMap)
|
||||
{
|
||||
if (!TryComp(old.PhysicsMap, out physicsMap))
|
||||
if (!_mapQuery.TryGetComponent(old.PhysicsMap, out physicsMap))
|
||||
Log.Error($"Entity {ToPrettyString(uid)} has missing physics map?");
|
||||
}
|
||||
|
||||
@@ -803,12 +790,12 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (xform.Broadphase is not { Valid: true } old)
|
||||
return false;
|
||||
|
||||
if (!TryComp(old.Uid, out broadphase))
|
||||
if (!_broadQuery.TryGetComponent(old.Uid, out broadphase))
|
||||
{
|
||||
// broadphase was probably deleted
|
||||
DebugTools.Assert("Encountered deleted broadphase.");
|
||||
|
||||
if (TryComp(xform.Owner, out FixturesComponent? fixtures))
|
||||
if (_fixturesQuery.TryGetComponent(xform.Owner, out FixturesComponent? fixtures))
|
||||
{
|
||||
foreach (var fixture in fixtures.Fixtures.Values)
|
||||
{
|
||||
|
||||
@@ -227,8 +227,6 @@ public abstract partial class SharedMapSystem
|
||||
"Can't modify chunk size of an existing grid.");
|
||||
|
||||
component.ChunkSize = state.ChunkSize;
|
||||
DebugTools.Assert(state.ChunkData != null || state.FullGridData != null);
|
||||
|
||||
if (state.ChunkData == null && state.FullGridData == null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ public record struct PhysicsSleepEvent(EntityUid Entity, PhysicsComponent Body)
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the entity as still being awake and cancels sleeping.
|
||||
/// This only works if <see cref="PhysicsComponent.CanCollide"/> is still enabled and the object is not a static object..
|
||||
/// </summary>
|
||||
public bool Cancelled;
|
||||
};
|
||||
|
||||
@@ -21,6 +21,9 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
private EntityQuery<PhysicsMapComponent> _mapQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<FixturesComponent> _fixtureQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -29,6 +32,9 @@ namespace Robust.Shared.Physics.Systems
|
||||
SubscribeLocalEvent<FixturesComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<FixturesComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<FixturesComponent, ComponentHandleState>(OnHandleState);
|
||||
_mapQuery = GetEntityQuery<PhysicsMapComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_fixtureQuery = GetEntityQuery<FixturesComponent>();
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, FixturesComponent component, ComponentShutdown args)
|
||||
@@ -37,10 +43,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
// Yes it is actively making the game buggier but I would essentially double the size of this PR trying to fix it
|
||||
// my best solution rn is move the broadphase property onto FixturesComponent and then refactor
|
||||
// SharedBroadphaseSystem a LOT.
|
||||
if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? body))
|
||||
{
|
||||
if (!_physicsQuery.TryGetComponent(uid, out var body))
|
||||
return;
|
||||
}
|
||||
|
||||
// Can't just get physicscomp on shutdown as it may be touched completely independently.
|
||||
_physics.DestroyContacts(body);
|
||||
@@ -63,7 +67,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
PhysicsComponent? body = null,
|
||||
TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body, ref manager))
|
||||
if (!_physicsQuery.Resolve(uid, ref body) || !_fixtureQuery.Resolve(uid, ref manager))
|
||||
return false;
|
||||
|
||||
if (manager.Fixtures.ContainsKey(id))
|
||||
@@ -85,7 +89,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
DebugTools.Assert(MetaData(uid).EntityLifeStage < EntityLifeStage.Terminating);
|
||||
|
||||
if (!Resolve(uid, ref manager, ref body))
|
||||
if (!_physicsQuery.Resolve(uid, ref body) || !_fixtureQuery.Resolve(uid, ref manager))
|
||||
{
|
||||
DebugTools.Assert(false);
|
||||
return;
|
||||
@@ -120,10 +124,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
/// </summary>
|
||||
public Fixture? GetFixtureOrNull(EntityUid uid, string id, FixturesComponent? manager = null)
|
||||
{
|
||||
if (!Resolve(uid, ref manager, false))
|
||||
{
|
||||
if (!_fixtureQuery.Resolve(uid, ref manager))
|
||||
return null;
|
||||
}
|
||||
|
||||
return manager.Fixtures.TryGetValue(id, out var fixture) ? fixture : null;
|
||||
}
|
||||
@@ -142,11 +144,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
FixturesComponent? manager = null,
|
||||
TransformComponent? xform = null)
|
||||
{
|
||||
if (!_fixtureQuery.Resolve(uid, ref manager))
|
||||
return;
|
||||
|
||||
var fixture = GetFixtureOrNull(uid, id, manager);
|
||||
|
||||
if (fixture == null) return;
|
||||
|
||||
DestroyFixture(uid, id, fixture, updates, body, manager, xform);
|
||||
if (fixture != null)
|
||||
DestroyFixture(uid, id, fixture, updates, body, manager, xform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -183,8 +186,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
if (_lookup.TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
{
|
||||
var map = Transform(broadphase.Owner).MapUid;
|
||||
TryComp<PhysicsMapComponent>(map, out var physicsMap);
|
||||
DebugTools.Assert(xform.MapUid == Transform(broadphase.Owner).MapUid);
|
||||
_mapQuery.TryGetComponent(xform.MapUid, out var physicsMap);
|
||||
_lookup.DestroyProxies(uid, fixtureId, fixture, xform, broadphase, physicsMap);
|
||||
}
|
||||
|
||||
@@ -323,7 +326,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
/// </summary>
|
||||
public void FixtureUpdate(EntityUid uid, bool dirty = true, bool resetMass = true, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body, ref manager))
|
||||
if (!_physicsQuery.Resolve(uid, ref body) || !_fixtureQuery.Resolve(uid, ref manager))
|
||||
return;
|
||||
|
||||
var mask = 0;
|
||||
@@ -363,7 +366,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
public int GetFixtureCount(EntityUid uid, FixturesComponent? manager = null)
|
||||
{
|
||||
if (!Resolve(uid, ref manager, false))
|
||||
if (!_fixtureQuery.Resolve(uid, ref manager))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<PhysicsMapComponent> _mapQuery;
|
||||
|
||||
/*
|
||||
* Okay so Box2D has its own "MoveProxy" stuff so you can easily find new contacts when required.
|
||||
@@ -61,6 +62,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_mapQuery = GetEntityQuery<PhysicsMapComponent>();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
UpdatesAfter.Add(typeof(SharedTransformSystem));
|
||||
@@ -436,30 +438,32 @@ namespace Robust.Shared.Physics.Systems
|
||||
if (!Resolve(uid, ref xform, ref fixtures))
|
||||
return;
|
||||
|
||||
if (!_lookup.TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
if (xform.MapUid == null)
|
||||
return;
|
||||
|
||||
_physicsSystem.SetAwake(uid, body, true);
|
||||
if (!_xformQuery.TryGetComponent(xform.Broadphase?.Uid, out var broadphase))
|
||||
return;
|
||||
|
||||
_physicsSystem.SetAwake((uid, body), true);
|
||||
|
||||
var matrix = _transform.GetWorldMatrix(broadphase);
|
||||
foreach (var fixture in fixtures.Fixtures.Values)
|
||||
{
|
||||
TouchProxies(xform.MapID, broadphase, fixture);
|
||||
TouchProxies(xform.MapUid.Value, matrix, fixture);
|
||||
}
|
||||
}
|
||||
|
||||
private void TouchProxies(MapId mapId, BroadphaseComponent broadphase, Fixture fixture)
|
||||
private void TouchProxies(EntityUid mapId, Matrix3 broadphaseMatrix, Fixture fixture)
|
||||
{
|
||||
var broadphasePos = Transform(broadphase.Owner).WorldMatrix;
|
||||
|
||||
foreach (var proxy in fixture.Proxies)
|
||||
{
|
||||
AddToMoveBuffer(mapId, proxy, broadphasePos.TransformBox(proxy.AABB));
|
||||
AddToMoveBuffer(mapId, proxy, broadphaseMatrix.TransformBox(proxy.AABB));
|
||||
}
|
||||
}
|
||||
|
||||
private void AddToMoveBuffer(MapId mapId, FixtureProxy proxy, Box2 aabb)
|
||||
private void AddToMoveBuffer(EntityUid mapId, FixtureProxy proxy, Box2 aabb)
|
||||
{
|
||||
if (!TryComp<PhysicsMapComponent>(_mapManager.GetMapEntityId(mapId), out var physicsMap))
|
||||
if (!_mapQuery.TryGetComponent(mapId, out var physicsMap))
|
||||
return;
|
||||
|
||||
DebugTools.Assert(proxy.Body.CanCollide);
|
||||
@@ -477,10 +481,14 @@ namespace Robust.Shared.Physics.Systems
|
||||
if (!Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
if (!_lookup.TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
if (xform.MapUid == null)
|
||||
return;
|
||||
|
||||
TouchProxies(xform.MapID, broadphase, fixture);
|
||||
if (!_xformQuery.TryGetComponent(xform.Broadphase?.Uid, out var broadphase))
|
||||
return;
|
||||
|
||||
var matrix = _transform.GetWorldMatrix(broadphase);
|
||||
TouchProxies(xform.MapUid.Value, matrix, fixture);
|
||||
}
|
||||
|
||||
// TODO: The below is slow and should just query the map's broadphase directly. The problem is that
|
||||
|
||||
@@ -202,7 +202,7 @@ public partial class SharedPhysicsSystem
|
||||
/// <summary>
|
||||
/// Completely resets a dynamic body.
|
||||
/// </summary>
|
||||
public void ResetDynamics(PhysicsComponent body)
|
||||
public void ResetDynamics(PhysicsComponent body, bool dirty = true)
|
||||
{
|
||||
var updated = false;
|
||||
|
||||
@@ -230,7 +230,7 @@ public partial class SharedPhysicsSystem
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated)
|
||||
if (updated && dirty)
|
||||
Dirty(body);
|
||||
}
|
||||
|
||||
@@ -380,12 +380,16 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void SetAwake(Entity<PhysicsComponent> ent, bool value, bool updateSleepTime = true)
|
||||
{
|
||||
var uid = ent.Owner;
|
||||
var body = ent.Comp;
|
||||
if (body.Awake == value)
|
||||
return;
|
||||
var (uid, body) = ent;
|
||||
var canWake = body.BodyType != BodyType.Static && body.CanCollide;
|
||||
|
||||
if (value && (body.BodyType == BodyType.Static || !body.CanCollide))
|
||||
if (body.Awake == value)
|
||||
{
|
||||
DebugTools.Assert(!body.Awake || canWake);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value && !canWake)
|
||||
return;
|
||||
|
||||
body.Awake = value;
|
||||
@@ -397,21 +401,30 @@ public partial class SharedPhysicsSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO C# event?
|
||||
var ev = new PhysicsSleepEvent(uid, body);
|
||||
RaiseLocalEvent(uid, ref ev, true);
|
||||
|
||||
// Reset the sleep timer.
|
||||
if (ev.Cancelled)
|
||||
if (ev.Cancelled && canWake)
|
||||
{
|
||||
body.Awake = true;
|
||||
// TODO C# event?
|
||||
var wakeEv = new PhysicsWakeEvent(uid, body);
|
||||
RaiseLocalEvent(uid, ref wakeEv, true);
|
||||
|
||||
if (updateSleepTime)
|
||||
SetSleepTime(body, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ResetDynamics(body);
|
||||
ResetDynamics(body, dirty: false);
|
||||
}
|
||||
|
||||
// Update wake system after we are sure that the wake/sleep event wasn't cancelled.
|
||||
_wakeSystem.UpdateCanCollide(ent, checkTerminating: false, dirty: false);
|
||||
|
||||
if (updateSleepTime)
|
||||
SetSleepTime(body, 0);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
internal void AddAwakeBody(EntityUid uid, PhysicsComponent body, EntityUid mapUid, PhysicsMapComponent? map = null)
|
||||
{
|
||||
Resolve(mapUid, ref map, false);
|
||||
PhysMapQuery.Resolve(mapUid, ref map, false);
|
||||
AddAwakeBody(uid, body, map);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
internal void RemoveSleepBody(EntityUid uid, PhysicsComponent body, EntityUid mapUid, PhysicsMapComponent? map = null)
|
||||
{
|
||||
Resolve(mapUid, ref map, false);
|
||||
PhysMapQuery.Resolve(mapUid, ref map, false);
|
||||
RemoveSleepBody(uid, body, map);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
[Dependency] private readonly SharedGridTraversalSystem _traversal = default!;
|
||||
[Dependency] private readonly SharedJointSystem _joints = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly CollisionWakeSystem _wakeSystem = default!;
|
||||
|
||||
private int _substeps;
|
||||
|
||||
@@ -65,6 +66,9 @@ namespace Robust.Shared.Physics.Systems
|
||||
private EntityQuery<FixturesComponent> _fixturesQuery;
|
||||
protected EntityQuery<PhysicsComponent> PhysicsQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<CollideOnAnchorComponent> _anchorQuery;
|
||||
protected EntityQuery<PhysicsMapComponent> PhysMapQuery;
|
||||
protected EntityQuery<MapComponent> MapQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -73,6 +77,9 @@ namespace Robust.Shared.Physics.Systems
|
||||
_fixturesQuery = GetEntityQuery<FixturesComponent>();
|
||||
PhysicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_anchorQuery = GetEntityQuery<CollideOnAnchorComponent>();
|
||||
PhysMapQuery = GetEntityQuery<PhysicsMapComponent>();
|
||||
MapQuery = GetEntityQuery<MapComponent>();
|
||||
|
||||
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
||||
SubscribeLocalEvent<CollisionChangeEvent>(OnCollisionChange);
|
||||
@@ -180,8 +187,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var jointQuery = GetEntityQuery<JointComponent>();
|
||||
|
||||
TryComp(_mapManager.GetMapEntityId(oldMapId), out PhysicsMapComponent? oldMap);
|
||||
TryComp(_mapManager.GetMapEntityId(newMapId), out PhysicsMapComponent? newMap);
|
||||
PhysMapQuery.TryGetComponent(_mapManager.GetMapEntityId(oldMapId), out var oldMap);
|
||||
PhysMapQuery.TryGetComponent(_mapManager.GetMapEntityId(newMapId), out var newMap);
|
||||
|
||||
RecursiveMapUpdate(uid, xform, body, newMap, oldMap, bodyQuery, xformQuery, jointQuery);
|
||||
}
|
||||
@@ -254,18 +261,13 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
private void UpdateMapAwakeState(EntityUid uid, PhysicsComponent body)
|
||||
{
|
||||
var mapId = EntityManager.GetComponent<TransformComponent>(uid).MapID;
|
||||
if (mapId == MapId.Nullspace)
|
||||
if (Transform(uid).MapUid is not {} map)
|
||||
return;
|
||||
var tempQualifier = _mapManager.GetMapEntityId(mapId);
|
||||
|
||||
if (body.Awake)
|
||||
{
|
||||
AddAwakeBody(uid, body, tempQualifier);
|
||||
}
|
||||
AddAwakeBody(uid, body, map);
|
||||
else
|
||||
{
|
||||
RemoveSleepBody(uid, body, tempQualifier);
|
||||
}
|
||||
RemoveSleepBody(uid, body, map);
|
||||
}
|
||||
|
||||
private void HandleContainerRemoved(EntityUid uid, PhysicsComponent physics, EntGotRemovedFromContainerMessage message)
|
||||
@@ -274,7 +276,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating) return;
|
||||
|
||||
// If this entity is only meant to collide when anchored, return early.
|
||||
if (TryComp(uid, out CollideOnAnchorComponent? collideComp) && collideComp.Enable)
|
||||
if (_anchorQuery.TryGetComponent(uid, out var collideComp) && collideComp.Enable)
|
||||
return;
|
||||
|
||||
WakeBody(uid, body: physics);
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace Robust.UnitTesting
|
||||
systems.LoadExtraSystemType<SharedGridTraversalSystem>();
|
||||
systems.LoadExtraSystemType<FixtureSystem>();
|
||||
systems.LoadExtraSystemType<Gravity2DController>();
|
||||
systems.LoadExtraSystemType<CollisionWakeSystem>();
|
||||
|
||||
if (Project == UnitTestProject.Client)
|
||||
{
|
||||
@@ -224,6 +225,16 @@ namespace Robust.UnitTesting
|
||||
compFactory.RegisterClass<Gravity2DComponent>();
|
||||
}
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(CollisionWakeComponent)))
|
||||
{
|
||||
compFactory.RegisterClass<CollisionWakeComponent>();
|
||||
}
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(CollideOnAnchorComponent)))
|
||||
{
|
||||
compFactory.RegisterClass<CollideOnAnchorComponent>();
|
||||
}
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(ActorComponent)))
|
||||
{
|
||||
compFactory.RegisterClass<ActorComponent>();
|
||||
|
||||
@@ -284,6 +284,7 @@ namespace Robust.UnitTesting.Server
|
||||
compFactory.RegisterClass<OccluderComponent>();
|
||||
compFactory.RegisterClass<OccluderTreeComponent>();
|
||||
compFactory.RegisterClass<Gravity2DComponent>();
|
||||
compFactory.RegisterClass<CollideOnAnchorComponent>();
|
||||
|
||||
_regDelegate?.Invoke(compFactory);
|
||||
|
||||
|
||||
142
Robust.UnitTesting/Shared/Map/SingleTileRemoveTest.cs
Normal file
142
Robust.UnitTesting/Shared/Map/SingleTileRemoveTest.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Map;
|
||||
|
||||
public sealed class GridDeleteSingleTileRemoveTestTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns a simple 1-tile grid with an entity on it, and then sets the tile to "space".
|
||||
/// This should delete the grid without deleting the entity.
|
||||
/// This also checks the networking to players, as previously this caused clients to crash.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestRemoveSingleTile()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sEntMan = server.ResolveDependency<IEntityManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var xforms = sEntMan.System<SharedTransformSystem>();
|
||||
var stateMan = (ClientGameStateManager) client.ResolveDependency<IClientGameStateManager>();
|
||||
|
||||
var cEntMan = client.ResolveDependency<IEntityManager>();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Ensure client & server ticks are synced.
|
||||
// Client runs 1 tick ahead
|
||||
{
|
||||
var sTick = (int)server.Timing.CurTick.Value;
|
||||
var cTick = (int)client.Timing.CurTick.Value;
|
||||
var delta = cTick - sTick;
|
||||
|
||||
if (delta > 1)
|
||||
await server.WaitRunTicks(delta - 1);
|
||||
else if (delta < 1)
|
||||
await client.WaitRunTicks(1 - delta);
|
||||
|
||||
sTick = (int)server.Timing.CurTick.Value;
|
||||
cTick = (int)client.Timing.CurTick.Value;
|
||||
delta = cTick - sTick;
|
||||
Assert.That(delta, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
// Set up map, grid, entity, and player
|
||||
Entity<MapGridComponent> grid = default;
|
||||
EntityUid sEntity = default;
|
||||
EntityUid sMap = default;
|
||||
EntityUid sPlayer = default;
|
||||
var sys = sEntMan.System<SharedMapSystem>();
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapId = mapMan.CreateMap();
|
||||
sMap = mapMan.GetMapEntityId(mapId);
|
||||
|
||||
var comp = mapMan.CreateGridEntity(mapId);
|
||||
grid = (comp.Owner, comp);
|
||||
sys.SetTile(grid, grid, new Vector2i(0, 0), new Tile(1, (TileRenderFlag)1, 1));
|
||||
var coords = new EntityCoordinates(grid, 0.5f, 0.5f);
|
||||
|
||||
sPlayer = sEntMan.SpawnEntity(null, coords);
|
||||
sEntity = sEntMan.SpawnEntity(null, coords);
|
||||
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, sPlayer);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
var nEntity = sEntMan.GetNetEntity(sEntity);
|
||||
var nPlayer = sEntMan.GetNetEntity(sPlayer);
|
||||
var nGrid = sEntMan.GetNetEntity(grid);
|
||||
var nMap = sEntMan.GetNetEntity(sMap);
|
||||
|
||||
// Check player got properly attached, and has received the other entity.
|
||||
Assert.That(cEntMan.TryGetEntity(nEntity, out var cEntity));
|
||||
Assert.That(cEntMan.TryGetEntity(nPlayer, out var cPlayerUid));
|
||||
Assert.That(cEntMan.TryGetEntity(nGrid, out var cGrid));
|
||||
Assert.That(cEntMan.TryGetEntity(nMap, out var cMap));
|
||||
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
|
||||
|
||||
var sQuery = sEntMan.GetEntityQuery<TransformComponent>();
|
||||
Assert.That(sQuery.GetComponent(sEntity).ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(sQuery.GetComponent(grid.Owner).ParentUid, Is.EqualTo(sMap));
|
||||
|
||||
var cQuery = cEntMan.GetEntityQuery<TransformComponent>();
|
||||
Assert.That(cQuery.GetComponent(cEntity!.Value).ParentUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cQuery.GetComponent(cGrid!.Value).ParentUid, Is.EqualTo(cMap));
|
||||
|
||||
// Remove the tile.
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
sys.SetTile(grid, grid, new Vector2i(0, 0), Tile.Empty);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Grid should no longer exist.
|
||||
Assert.That(!sEntMan.EntityExists(grid));
|
||||
Assert.That(!cEntMan.EntityExists(cGrid));
|
||||
|
||||
// Entity should now be parented to the map
|
||||
Assert.That(sQuery.GetComponent(sEntity).ParentUid, Is.EqualTo(sMap));
|
||||
Assert.That(cQuery.GetComponent(cEntity.Value).ParentUid, Is.EqualTo(cMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@ public sealed class Joints_Test
|
||||
public void JointsRelayTest()
|
||||
{
|
||||
var factory = RobustServerSimulation.NewSimulation();
|
||||
factory.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<CollideOnAnchorComponent>();
|
||||
});
|
||||
var sim = factory.InitializeInstance();
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
|
||||
Reference in New Issue
Block a user