Refactor Item collections for thread safety and performance

Replaces static Item.ItemList and related collections with thread-safe data structures using ConcurrentDictionary and ImmutableHashSet. Adds thread-safe helpers for marking items for deconstruction and managing item lists. Updates all usages of Item.ItemList and DeconstructItems to use new APIs, improving performance and safety in multi-threaded contexts. Also refactors MeleeWeapon and Projectile impact queues to use ConcurrentQueue, and updates related logic throughout the codebase.
This commit is contained in:
Eero
2025-12-28 03:57:04 +08:00
parent edd50ef181
commit 90962b2328
22 changed files with 232 additions and 92 deletions
@@ -3,6 +3,7 @@ using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@@ -24,7 +25,7 @@ namespace Barotrauma.Items.Components
private readonly HashSet<Entity> hitTargets = new HashSet<Entity>();
private readonly Queue<Fixture> impactQueue = new Queue<Fixture>();
private readonly ConcurrentQueue<Fixture> impactQueue = new ConcurrentQueue<Fixture>();
public Character User { get; private set; }
@@ -190,17 +191,16 @@ namespace Barotrauma.Items.Components
{
if (!item.body.Enabled)
{
impactQueue.Clear();
while (impactQueue.TryDequeue(out _)) { } // Clear queue
return;
}
if (picker == null || !picker.HeldItems.Contains(item))
{
impactQueue.Clear();
while (impactQueue.TryDequeue(out _)) { } // Clear queue
IsActive = false;
}
while (impactQueue.Count > 0)
while (impactQueue.TryDequeue(out var impact))
{
var impact = impactQueue.Dequeue();
HandleImpact(impact);
}
//in case handling the impact does something to the picker
@@ -300,7 +300,7 @@ namespace Barotrauma.Items.Components
private void RestoreCollision()
{
impactQueue.Clear();
while (impactQueue.TryDequeue(out _)) { } // Clear queue
item.body.FarseerBody.OnCollision -= OnCollision;
item.body.CollisionCategories = Physics.CollisionItem;
item.body.CollidesWith = Physics.DefaultItemCollidesWith;
@@ -5,6 +5,7 @@ using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@@ -72,7 +73,7 @@ namespace Barotrauma.Items.Components
public const float WaterDragCoefficient = 0.1f;
private readonly Queue<Impact> impactQueue = new Queue<Impact>();
private readonly ConcurrentQueue<Impact> impactQueue = new ConcurrentQueue<Impact>();
private bool removePending;
@@ -840,9 +841,8 @@ namespace Barotrauma.Items.Components
DisableProjectileCollisions();
}
}
while (impactQueue.Count > 0)
while (impactQueue.TryDequeue(out var impact))
{
var impact = impactQueue.Dequeue();
HandleProjectileCollision(impact.Fixture, impact.Normal, impact.LinearVelocity);
}
@@ -723,11 +723,11 @@ namespace Barotrauma.Items.Components
if (item0 == null && item1 != null)
{
item0 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
item0 = Item.ItemList.FirstOrDefault(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
}
else if (item0 != null && item1 == null)
{
item1 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
item1 = Item.ItemList.FirstOrDefault(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
}
if (item0 == null || item1 == null || nodes.Count == 0) { return; }