Fixed multiple LINQ using shared resources and cause crashes

Added an null check in AIObjectiveManager.cs to avoid accessing removed resources
Use shuffledGaps instead of gapList to ensure update order requirement(already in master)
Updated parallelism count
This commit is contained in:
NotAlwaysTrue
2026-01-09 18:09:49 +08:00
parent caec44c57d
commit e7e444e9b2
5 changed files with 24 additions and 36 deletions

View File

@@ -382,8 +382,13 @@ namespace Barotrauma
}
steeringBuffer = Math.Clamp(steeringBuffer, minSteeringBuffer, maxSteeringBuffer);
AnimController.Crouching = shouldCrouch;
CheckCrouching(deltaTime);
// in case of somehow AnimController was a null, eg. something removed AnimController in the middle of an update
if (AnimController != null)
{
AnimController.Crouching = shouldCrouch;
CheckCrouching(deltaTime);
}
Character.ClearInputs();
if (SortTimer > 0.0f)
@@ -1638,7 +1643,8 @@ namespace Barotrauma
if (mode == AIObjectiveCombat.CombatMode.None) { return; }
if (Character.IsDead || Character.IsIncapacitated || Character.Removed) { return; }
if (!Character.IsBot) { return; }
if (ObjectiveManager.Objectives.FirstOrDefault(o => o is AIObjectiveCombat) is AIObjectiveCombat combatObjective)
List<AIObjective> ObjectivesLocal = ObjectiveManager.Objectives;
if (ObjectivesLocal.FirstOrDefault(o => o is AIObjectiveCombat) is AIObjectiveCombat combatObjective)
{
// Don't replace offensive mode with something else
if (combatObjective.Mode == AIObjectiveCombat.CombatMode.Offensive && mode != AIObjectiveCombat.CombatMode.Offensive) { return; }

View File

@@ -126,7 +126,7 @@ namespace Barotrauma
}
else
{
Objectives.RemoveAll(o => o.GetType() == type);
Objectives.RemoveAll(o => o?.GetType() == type);
}
Objectives.Add(objective);
}

View File

@@ -345,7 +345,8 @@ namespace Barotrauma.Items.Components
{
Connection recipient = wire.OtherConnection(this);
if (recipient == null) { continue; }
if (recipient.item == this.item || signal.source?.LastSentSignalRecipients.LastOrDefault() == recipient) { continue; }
List<Connection> LastSentSignalRecipientsCopy = signal.source?.LastSentSignalRecipients.ToList();
if (recipient.item == this.item || LastSentSignalRecipientsCopy.LastOrDefault() == recipient) { continue; }
signal.source?.LastSentSignalRecipients.Add(recipient);
#if CLIENT

View File

@@ -816,7 +816,7 @@ namespace Barotrauma
// Gap update (has order dependencies, keep random order but execute sequentially)
var shuffledGaps = gapList.OrderBy(g => Rand.Int(int.MaxValue)).ToList();
Parallel.ForEach(gapList, parallelOptions, gap =>
Parallel.ForEach(shuffledGaps, parallelOptions, gap =>
{
PhysicsBodyQueue.IsInParallelContext = true;
try

View File

@@ -25,7 +25,7 @@ namespace Barotrauma
private static readonly ParallelOptions parallelOptions = new ParallelOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount * 2,
MaxDegreeOfParallelism = Math.Max(4,Environment.ProcessorCount - 1),
};
#if CLIENT
@@ -259,33 +259,15 @@ namespace Barotrauma
Character.Controlled?.UpdateLocalCursor(cam);
#elif SERVER
Parallel.Invoke(parallelOptions,
() =>
{
PhysicsBodyQueue.IsInParallelContext = true;
try
{
if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime, Camera.Instance);
}
finally
{
PhysicsBodyQueue.IsInParallelContext = false;
}
},
() =>
{
PhysicsBodyQueue.IsInParallelContext = true;
try
{
Character.UpdateAll((float)deltaTime, Camera.Instance);
}
finally
{
PhysicsBodyQueue.IsInParallelContext = false;
}
}
);
// DO NOT PARALLELIZE THESE TWO OR IT MAY STUCK HERE
// SO FOLLOW THE ORIGINAL SINGLE-THREAD LOGIC STRICTLY
if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime, Camera.Instance);
Character.UpdateAll((float)deltaTime, Camera.Instance);
StatusEffect.UpdateAll((float)deltaTime);
PhysicsBodyQueue.ProcessPendingOperations();
#endif
@@ -310,8 +292,6 @@ namespace Barotrauma
MapEntity.UpdateAll((float)deltaTime, Camera.Instance, parallelOptions);
StatusEffect.UpdateAll((float)deltaTime);
#endif
#if CLIENT
@@ -321,6 +301,7 @@ namespace Barotrauma
#endif
//Character.UpdateAnimAll is not thread-safe and must be executed on the main thread
Character.UpdateAnimAll((float)deltaTime);
PhysicsBodyQueue.ProcessPendingOperations();
#if CLIENT
Ragdoll.UpdateAll((float)deltaTime, cam);