Remove unnecessary thread-safety code from entity spawning

Eliminated redundant locks and related comments in EntitySpawner and Entity classes, simplifying the spawn and remove queue handling. Also removed outdated comments in GameScreen regarding thread safety. These changes assume entity spawning and removal are no longer performed from multiple threads, improving code clarity and maintainability.
This commit is contained in:
Eero
2025-12-28 17:45:51 +08:00
parent 1db14631df
commit bd1e624eb1
3 changed files with 26 additions and 72 deletions
@@ -147,7 +147,6 @@ namespace Barotrauma
CreationStackTrace += $"{fileName}@{fileLineNumber}; "; CreationStackTrace += $"{fileName}@{fileLineNumber}; ";
} }
#endif #endif
#warning TODO: consider removing this mutex, entity creation probably shouldn't be multithreaded
lock (creationCounterMutex) lock (creationCounterMutex)
{ {
CreationIndex = creationCounter; CreationIndex = creationCounter;
@@ -5,7 +5,6 @@ using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
namespace Barotrauma namespace Barotrauma
{ {
@@ -206,7 +205,6 @@ namespace Barotrauma
} }
private readonly Queue<Either<IEntitySpawnInfo, Entity>> spawnOrRemoveQueue; private readonly Queue<Either<IEntitySpawnInfo, Entity>> spawnOrRemoveQueue;
private readonly object spawnOrRemoveQueueLock = new object();
public abstract class SpawnOrRemove : NetEntityEvent.IData public abstract class SpawnOrRemove : NetEntityEvent.IData
{ {
@@ -284,11 +282,8 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, onSpawned, condition, quality)); spawnOrRemoveQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, onSpawned, condition, quality));
} }
}
public void AddItemToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, int? quality = null, Action<Item> onSpawned = null) public void AddItemToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, int? quality = null, Action<Item> onSpawned = null)
{ {
@@ -300,11 +295,8 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, onSpawned, condition, quality)); spawnOrRemoveQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, onSpawned, condition, quality));
} }
}
public void AddItemToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, int? quality = null, Action<Item> onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None) public void AddItemToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, int? quality = null, Action<Item> onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None)
{ {
@@ -316,8 +308,6 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(new ItemSpawnInfo(itemPrefab, inventory, onSpawned, condition, quality) spawnOrRemoveQueue.Enqueue(new ItemSpawnInfo(itemPrefab, inventory, onSpawned, condition, quality)
{ {
SpawnIfInventoryFull = spawnIfInventoryFull, SpawnIfInventoryFull = spawnIfInventoryFull,
@@ -325,7 +315,6 @@ namespace Barotrauma
Slot = slot Slot = slot
}); });
} }
}
public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 worldPosition, Action<Character> onSpawn = null) public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 worldPosition, Action<Character> onSpawn = null)
{ {
@@ -337,11 +326,8 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue4:SpeciesNameNullOrEmpty", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue4:SpeciesNameNullOrEmpty", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(new CharacterSpawnInfo(speciesName, worldPosition, onSpawn)); spawnOrRemoveQueue.Enqueue(new CharacterSpawnInfo(speciesName, worldPosition, onSpawn));
} }
}
public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 position, Submarine sub, Action<Character> onSpawn = null) public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 position, Submarine sub, Action<Character> onSpawn = null)
{ {
@@ -353,11 +339,8 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue5:SpeciesNameNullOrEmpty", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue5:SpeciesNameNullOrEmpty", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(new CharacterSpawnInfo(speciesName, position, sub, onSpawn)); spawnOrRemoveQueue.Enqueue(new CharacterSpawnInfo(speciesName, position, sub, onSpawn));
} }
}
public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 worldPosition, CharacterInfo characterInfo, Action<Character> onSpawn = null) public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 worldPosition, CharacterInfo characterInfo, Action<Character> onSpawn = null)
{ {
@@ -369,11 +352,8 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue4:SpeciesNameNullOrEmpty", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue4:SpeciesNameNullOrEmpty", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(new CharacterSpawnInfo(speciesName, worldPosition, characterInfo, onSpawn)); spawnOrRemoveQueue.Enqueue(new CharacterSpawnInfo(speciesName, worldPosition, characterInfo, onSpawn));
} }
}
public void AddEntityToRemoveQueue(Entity entity) public void AddEntityToRemoveQueue(Entity entity)
{ {
@@ -395,21 +375,15 @@ namespace Barotrauma
#endif #endif
} }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(entity); spawnOrRemoveQueue.Enqueue(entity);
} }
}
public void AddItemToRemoveQueue(Item item) public void AddItemToRemoveQueue(Item item)
{ {
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (IsInRemoveQueue(item) || item.Removed) { return; } if (IsInRemoveQueue(item) || item.Removed) { return; }
lock (spawnOrRemoveQueueLock)
{
spawnOrRemoveQueue.Enqueue(item); spawnOrRemoveQueue.Enqueue(item);
}
item.IsInRemoveQueue = true; item.IsInRemoveQueue = true;
foreach (var containedItem in item.ContainedItems) foreach (var containedItem in item.ContainedItems)
@@ -425,8 +399,6 @@ namespace Barotrauma
/// Are there any entities in the spawn queue that match the given predicate /// Are there any entities in the spawn queue that match the given predicate
/// </summary> /// </summary>
public bool IsInSpawnQueue(Predicate<IEntitySpawnInfo> predicate) public bool IsInSpawnQueue(Predicate<IEntitySpawnInfo> predicate)
{
lock (spawnOrRemoveQueueLock)
{ {
foreach (var spawnOrRemove in spawnOrRemoveQueue) foreach (var spawnOrRemove in spawnOrRemoveQueue)
{ {
@@ -434,14 +406,11 @@ namespace Barotrauma
} }
return false; return false;
} }
}
/// <summary> /// <summary>
/// How many entities in the spawn queue match the given predicate /// How many entities in the spawn queue match the given predicate
/// </summary> /// </summary>
public int CountSpawnQueue(Predicate<IEntitySpawnInfo> predicate) public int CountSpawnQueue(Predicate<IEntitySpawnInfo> predicate)
{
lock (spawnOrRemoveQueueLock)
{ {
int count = 0; int count = 0;
foreach (var spawnOrRemove in spawnOrRemoveQueue) foreach (var spawnOrRemove in spawnOrRemoveQueue)
@@ -450,11 +419,8 @@ namespace Barotrauma
} }
return count; return count;
} }
}
public bool IsInRemoveQueue(Entity entity) public bool IsInRemoveQueue(Entity entity)
{
lock (spawnOrRemoveQueueLock)
{ {
foreach (var spawnOrRemove in spawnOrRemoveQueue) foreach (var spawnOrRemove in spawnOrRemoveQueue)
{ {
@@ -462,19 +428,13 @@ namespace Barotrauma
} }
return false; return false;
} }
}
public void Update(bool createNetworkEvents = true) public void Update(bool createNetworkEvents = true)
{ {
if (GameMain.NetworkMember is { IsClient: true }) { return; } if (GameMain.NetworkMember is { IsClient: true }) { return; }
while (true) while (spawnOrRemoveQueue.Count > 0)
{ {
Either<IEntitySpawnInfo, Entity> spawnOrRemove; if (!spawnOrRemoveQueue.TryDequeue(out var spawnOrRemove)) { break; }
lock (spawnOrRemoveQueueLock)
{
if (spawnOrRemoveQueue.Count == 0) { break; }
spawnOrRemove = spawnOrRemoveQueue.Dequeue();
}
if (spawnOrRemove.TryGet(out Entity entityToRemove)) if (spawnOrRemove.TryGet(out Entity entityToRemove))
{ {
if (entityToRemove is Item item) if (entityToRemove is Item item)
@@ -504,11 +464,8 @@ namespace Barotrauma
partial void CreateNetworkEventProjSpecific(SpawnOrRemove spawnOrRemove); partial void CreateNetworkEventProjSpecific(SpawnOrRemove spawnOrRemove);
public void Reset() public void Reset()
{
lock (spawnOrRemoveQueueLock)
{ {
spawnOrRemoveQueue.Clear(); spawnOrRemoveQueue.Clear();
}
#if CLIENT #if CLIENT
receivedEvents.Clear(); receivedEvents.Clear();
#endif #endif
@@ -286,7 +286,6 @@ namespace Barotrauma
} }
); );
// Process any physics operations queued during parallel updates
PhysicsBodyQueue.ProcessPendingOperations(); PhysicsBodyQueue.ProcessPendingOperations();
#endif #endif
@@ -311,7 +310,6 @@ namespace Barotrauma
MapEntity.UpdateAll((float)deltaTime, Camera.Instance, parallelOptions); MapEntity.UpdateAll((float)deltaTime, Camera.Instance, parallelOptions);
//StatusEffect.UpdateAll is not thread-safe and must be executed on the main thread
StatusEffect.UpdateAll((float)deltaTime); StatusEffect.UpdateAll((float)deltaTime);
#endif #endif