diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs index 4479bdb81..e1eb0c40e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs @@ -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 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; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs index 83e475c37..ead5b6a87 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs @@ -126,7 +126,7 @@ namespace Barotrauma } else { - Objectives.RemoveAll(o => o.GetType() == type); + Objectives.RemoveAll(o => o?.GetType() == type); } Objectives.Add(objective); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs index d55150237..37cb33d9c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs @@ -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 LastSentSignalRecipientsCopy = signal.source?.LastSentSignalRecipients.ToList(); + if (recipient.item == this.item || LastSentSignalRecipientsCopy.LastOrDefault() == recipient) { continue; } signal.source?.LastSentSignalRecipients.Add(recipient); #if CLIENT diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs index b61d2e68e..fb3723934 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs @@ -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 diff --git a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs index e2fddef7c..be3e4bb12 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs @@ -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);