diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index bd589ba209..e7cb195519 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -43,7 +43,7 @@ END TEMPLATE-->
### Bugfixes
-*None yet*
+* Fix QueueDel during EndCollideEvents from throwing while removing contacts.
### Other
diff --git a/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs b/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs
index 2e6279c133..52ef03d0ef 100644
--- a/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs
+++ b/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs
@@ -376,6 +376,9 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
///
Grid = 1 << 3,
+ ///
+ /// Set right before the contact is deleted
+ ///
Deleting = 1 << 4,
}
}
diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs
index 487c06b231..6b15cecf17 100644
--- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs
+++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs
@@ -186,17 +186,25 @@ public partial class SharedPhysicsSystem
{
if (body.Contacts.Count == 0) return;
+ // This variable is only used in edge-case scenario when contact flagged Deleting raises
+ // EndCollideEvent which will QueueDelete contact's entity
+ ushort contactsFlaggedDeleting = 0;
var node = body.Contacts.First;
while (node != null)
{
var contact = node.Value;
node = node.Next;
+
// Destroy last so the linked-list doesn't get touched.
- DestroyContact(contact);
+ if (!DestroyContact(contact))
+ {
+ contactsFlaggedDeleting++;
+ }
}
- DebugTools.Assert(body.Contacts.Count == 0);
+ // This contact will be deleted before SimulateWorld runs since it is already set to be Deleted
+ DebugTools.Assert(body.Contacts.Count == contactsFlaggedDeleting);
}
///
diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs
index fbd944ec75..678b594bd2 100644
--- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs
+++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs
@@ -310,11 +310,10 @@ public abstract partial class SharedPhysicsSystem
(fixtureB.CollisionMask & fixtureA.CollisionLayer) == 0x0);
}
- public void DestroyContact(Contact contact)
+ public bool DestroyContact(Contact contact)
{
- // Don't recursive update or we're in for a bad time.
if ((contact.Flags & ContactFlags.Deleting) != 0x0)
- return;
+ return false;
Fixture fixtureA = contact.FixtureA!;
Fixture fixtureB = contact.FixtureB!;
@@ -326,7 +325,7 @@ public abstract partial class SharedPhysicsSystem
if (contact.IsTouching)
{
- var ev1 = new EndCollideEvent(aUid, bUid, contact.FixtureAId, contact.FixtureBId ,fixtureA, fixtureB, bodyA, bodyB);
+ var ev1 = new EndCollideEvent(aUid, bUid, contact.FixtureAId, contact.FixtureBId, fixtureA, fixtureB, bodyA, bodyB);
var ev2 = new EndCollideEvent(bUid, aUid, contact.FixtureBId, contact.FixtureAId, fixtureB, fixtureA, bodyB, bodyA);
RaiseLocalEvent(aUid, ref ev1);
RaiseLocalEvent(bUid, ref ev2);
@@ -335,10 +334,10 @@ public abstract partial class SharedPhysicsSystem
if (contact.Manifold.PointCount > 0 && contact.FixtureA?.Hard == true && contact.FixtureB?.Hard == true)
{
if (bodyA.CanCollide)
- SetAwake(aUid, bodyA, true);
+ SetAwake((aUid, bodyA), true);
if (bodyB.CanCollide)
- SetAwake(bUid, bodyB, true);
+ SetAwake((bUid, bodyB), true);
}
// Remove from the world
@@ -347,16 +346,19 @@ public abstract partial class SharedPhysicsSystem
// Remove from body 1
DebugTools.Assert(fixtureA.Contacts.ContainsKey(fixtureB));
fixtureA.Contacts.Remove(fixtureB);
- DebugTools.Assert(bodyA.Contacts.Contains(contact.BodyANode!.Value));
+ DebugTools.Assert(bodyA.Contacts.Contains(contact.BodyANode.Value));
bodyA.Contacts.Remove(contact.BodyANode);
// Remove from body 2
DebugTools.Assert(fixtureB.Contacts.ContainsKey(fixtureA));
fixtureB.Contacts.Remove(fixtureA);
+ DebugTools.Assert(bodyB.Contacts.Contains(contact.BodyBNode.Value));
bodyB.Contacts.Remove(contact.BodyBNode);
// Insert into the pool.
_contactPool.Return(contact);
+
+ return true;
}
internal void CollideContacts()