Unstable 0.2 Defer physics operations during parallel updates

Introduces a thread-safe queue for deferring physics operations (such as body creation and transforms) to the main thread, ensuring Farseer Physics is not accessed from parallel contexts. Updates Holdable, Item, MapEntity, and GameScreen to use the new PhysicsBodyQueue for safe physics operations during parallel updates, and refactors PhysicsBodyQueue to support general deferred physics actions.
This commit is contained in:
Eero
2025-12-28 15:10:06 +08:00
parent 49355fe32b
commit f485583621
5 changed files with 261 additions and 46 deletions
@@ -3669,20 +3669,45 @@ namespace Barotrauma
if (body != null)
{
IsActive = true;
body.Enabled = true;
body.PhysEnabled = true;
body.ResetDynamics();
if (dropper != null)
// Physics body operations must be deferred if we're in a parallel update context,
// because Farseer Physics is not thread-safe.
if (PhysicsBodyQueue.IsInParallelContext)
{
if (body.Removed)
// Capture the values we need for the deferred operation
var capturedBody = body;
var capturedDropperSimPos = dropper?.SimPosition ?? Microsoft.Xna.Framework.Vector2.Zero;
var capturedSetTransform = setTransform && dropper != null;
PhysicsBodyQueue.Enqueue(() =>
{
DebugConsole.ThrowError(
"Failed to drop the item \"" + Name + "\" (body has been removed"
+ (Removed ? ", item has been removed)" : ")"));
}
else if (setTransform)
if (capturedBody.Removed || Removed) { return; }
capturedBody.Enabled = true;
capturedBody.PhysEnabled = true;
capturedBody.ResetDynamics();
if (capturedSetTransform)
{
capturedBody.SetTransformIgnoreContacts(capturedDropperSimPos, 0.0f);
}
});
}
else
{
body.Enabled = true;
body.PhysEnabled = true;
body.ResetDynamics();
if (dropper != null)
{
body.SetTransformIgnoreContacts(dropper.SimPosition, 0.0f);
if (body.Removed)
{
DebugConsole.ThrowError(
"Failed to drop the item \"" + Name + "\" (body has been removed"
+ (Removed ? ", item has been removed)" : ")"));
}
else if (setTransform)
{
body.SetTransformIgnoreContacts(dropper.SimPosition, 0.0f);
}
}
}
}
@@ -3693,7 +3718,22 @@ namespace Barotrauma
{
if (setTransform)
{
SetTransform(Container.SimPosition, 0.0f);
// Defer SetTransform if in parallel context
if (PhysicsBodyQueue.IsInParallelContext)
{
var capturedContainerSimPos = Container.SimPosition;
PhysicsBodyQueue.Enqueue(() =>
{
if (!Removed)
{
SetTransform(capturedContainerSimPos, 0.0f);
}
});
}
else
{
SetTransform(Container.SimPosition, 0.0f);
}
}
Container.RemoveContained(this);
Container = null;