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:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -60,3 +60,5 @@ Deploy/DeployAll/PrivateKey.*
|
|||||||
#Rider
|
#Rider
|
||||||
*.DotSettings.user
|
*.DotSettings.user
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/tasks.json
|
||||||
|
|||||||
@@ -867,7 +867,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (var stackedItem in item.GetStackedItems())
|
foreach (var stackedItem in item.GetStackedItems())
|
||||||
{
|
{
|
||||||
Item.DeconstructItems.Add(stackedItem);
|
Item.MarkForDeconstruction(stackedItem);
|
||||||
}
|
}
|
||||||
HintManager.OnItemMarkedForDeconstruction(order.OrderGiver);
|
HintManager.OnItemMarkedForDeconstruction(order.OrderGiver);
|
||||||
}
|
}
|
||||||
@@ -875,7 +875,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (var stackedItem in item.GetStackedItems())
|
foreach (var stackedItem in item.GetStackedItems())
|
||||||
{
|
{
|
||||||
Item.DeconstructItems.Remove(stackedItem);
|
Item.UnmarkForDeconstruction(stackedItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1891,7 +1891,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Item.DeconstructItems.Contains(item) &&
|
else if (Item.IsMarkedForDeconstruction(item) &&
|
||||||
OrderPrefab.Prefabs.TryGet(Tags.DeconstructThis, out OrderPrefab deconstructOrder))
|
OrderPrefab.Prefabs.TryGet(Tags.DeconstructThis, out OrderPrefab deconstructOrder))
|
||||||
{
|
{
|
||||||
DrawSideIcon(deconstructOrder.SymbolSprite, Direction.Right, TextManager.Get("tooltip.markedfordeconstruction"), GUIStyle.Red, out bool mouseOn);
|
DrawSideIcon(deconstructOrder.SymbolSprite, Direction.Right, TextManager.Get("tooltip.markedfordeconstruction"), GUIStyle.Red, out bool mouseOn);
|
||||||
|
|||||||
@@ -471,11 +471,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (item0 == null && item1 != null)
|
if (item0 == null && item1 != null)
|
||||||
{
|
{
|
||||||
item0 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
|
item0 = Item.ItemList.FirstOrDefault(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
|
||||||
}
|
}
|
||||||
else if (item0 != null && item1 == null)
|
else if (item0 != null && item1 == null)
|
||||||
{
|
{
|
||||||
item1 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
|
item1 = Item.ItemList.FirstOrDefault(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
|
||||||
}
|
}
|
||||||
if (item0 != null && item1 != null && SelectedList.Contains(item0) && SelectedList.Contains(item1))
|
if (item0 != null && item1 != null && SelectedList.Contains(item0) && SelectedList.Contains(item1))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ namespace Barotrauma.Networking
|
|||||||
var shuttleGaps = Gap.GapList.FindAll(g => RespawnShuttles.Contains(g.Submarine) && g.ConnectedWall != null);
|
var shuttleGaps = Gap.GapList.FindAll(g => RespawnShuttles.Contains(g.Submarine) && g.ConnectedWall != null);
|
||||||
shuttleGaps.ForEach(g => Spawner.AddEntityToRemoveQueue(g));
|
shuttleGaps.ForEach(g => Spawner.AddEntityToRemoveQueue(g));
|
||||||
|
|
||||||
var dockingPorts = Item.ItemList.FindAll(i => RespawnShuttles.Contains(i.Submarine) && i.GetComponent<DockingPort>() != null);
|
var dockingPorts = Item.ItemList.Where(i => RespawnShuttles.Contains(i.Submarine) && i.GetComponent<DockingPort>() != null).ToList();
|
||||||
dockingPorts.ForEach(d => d.GetComponent<DockingPort>().Undock());
|
dockingPorts.ForEach(d => d.GetComponent<DockingPort>().Undock());
|
||||||
|
|
||||||
if (!IsShuttleInsideLevel || DateTime.Now > teamSpecificState.DespawnTime)
|
if (!IsShuttleInsideLevel || DateTime.Now > teamSpecificState.DespawnTime)
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
protected override bool CheckObjectiveState()
|
protected override bool CheckObjectiveState()
|
||||||
{
|
{
|
||||||
if (item.IgnoreByAI(character) || Item.DeconstructItems.Contains(item))
|
if (item.IgnoreByAI(character) || Item.IsMarkedForDeconstruction(item))
|
||||||
{
|
{
|
||||||
Abandon = true;
|
Abandon = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ namespace Barotrauma
|
|||||||
if (!allowUnloading) { return false; }
|
if (!allowUnloading) { return false; }
|
||||||
if (requireValidContainer && !IsValidContainer(item.Container, character)) { return false; }
|
if (requireValidContainer && !IsValidContainer(item.Container, character)) { return false; }
|
||||||
}
|
}
|
||||||
if (ignoreItemsMarkedForDeconstruction && Item.DeconstructItems.Contains(item)) { return false; }
|
if (ignoreItemsMarkedForDeconstruction && Item.IsMarkedForDeconstruction(item)) { return false; }
|
||||||
if (!item.HasAccess(character)) { return false; }
|
if (!item.HasAccess(character)) { return false; }
|
||||||
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
|
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
|
||||||
if (item.HasBallastFloraInHull) { return false; }
|
if (item.HasBallastFloraInHull) { return false; }
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (Identifier == Tags.DeconstructThis && item.AllowDeconstruct)
|
if (Identifier == Tags.DeconstructThis && item.AllowDeconstruct)
|
||||||
{
|
{
|
||||||
if (item.AllowDeconstruct && !Item.DeconstructItems.Contains(item) &&
|
if (item.AllowDeconstruct && !Item.IsMarkedForDeconstruction(item) &&
|
||||||
//only allow deconstructing if there are no deconstruction recipes (= deconstructing yields nothing), or deconstruction recipes that
|
//only allow deconstructing if there are no deconstruction recipes (= deconstructing yields nothing), or deconstruction recipes that
|
||||||
(item.Prefab.DeconstructItems.None() ||
|
(item.Prefab.DeconstructItems.None() ||
|
||||||
item.Prefab.DeconstructItems.Any(deconstructItem =>
|
item.Prefab.DeconstructItems.Any(deconstructItem =>
|
||||||
@@ -454,7 +454,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else if (Identifier == Tags.DontDeconstructThis)
|
else if (Identifier == Tags.DontDeconstructThis)
|
||||||
{
|
{
|
||||||
if (Item.DeconstructItems.Contains(item)) { return true; }
|
if (Item.IsMarkedForDeconstruction(item)) { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableArray<Identifier> targetItems = GetTargetItems(option);
|
ImmutableArray<Identifier> targetItems = GetTargetItems(option);
|
||||||
|
|||||||
@@ -2761,10 +2761,11 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
int itemsPerFrame = IsOnPlayerTeam ? 100 : 10;
|
int itemsPerFrame = IsOnPlayerTeam ? 100 : 10;
|
||||||
int checkedItemCount = 0;
|
int checkedItemCount = 0;
|
||||||
for (int i = 0; i < itemsPerFrame && itemIndex < Item.ItemList.Count; i++, itemIndex++)
|
var cachedItems = Item.GetCachedItemList();
|
||||||
|
for (int i = 0; i < itemsPerFrame && itemIndex < cachedItems.Count; i++, itemIndex++)
|
||||||
{
|
{
|
||||||
checkedItemCount++;
|
checkedItemCount++;
|
||||||
var item = Item.ItemList[itemIndex];
|
var item = cachedItems[itemIndex];
|
||||||
if (!item.IsInteractable(this)) { continue; }
|
if (!item.IsInteractable(this)) { continue; }
|
||||||
if (ignoredItems != null && ignoredItems.Contains(item)) { continue; }
|
if (ignoredItems != null && ignoredItems.Contains(item)) { continue; }
|
||||||
if (item.Submarine == null) { continue; }
|
if (item.Submarine == null) { continue; }
|
||||||
@@ -2800,10 +2801,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetItem = _foundItem;
|
targetItem = _foundItem;
|
||||||
bool completed = itemIndex >= Item.ItemList.Count - 1;
|
bool completed = itemIndex >= cachedItems.Count - 1;
|
||||||
if (HumanAIController.DebugAI && checkedItemCount > 0 && targetItem != null && StopWatch.ElapsedMilliseconds > 1)
|
if (HumanAIController.DebugAI && checkedItemCount > 0 && targetItem != null && StopWatch.ElapsedMilliseconds > 1)
|
||||||
{
|
{
|
||||||
var msg = $"Went through {checkedItemCount} of total {Item.ItemList.Count} items. Found item {targetItem.Name} in {StopWatch.ElapsedMilliseconds} ms. Completed: {completed}";
|
var msg = $"Went through {checkedItemCount} of total {cachedItems.Count} items. Found item {targetItem.Name} in {StopWatch.ElapsedMilliseconds} ms. Completed: {completed}";
|
||||||
if (StopWatch.ElapsedMilliseconds > 5)
|
if (StopWatch.ElapsedMilliseconds > 5)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError(msg);
|
DebugConsole.ThrowError(msg);
|
||||||
|
|||||||
@@ -1478,7 +1478,7 @@ namespace Barotrauma
|
|||||||
newItemName = args[2];
|
newItemName = args[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldItem = Item.ItemList.FindAll(it => it.Name == args[0]).ElementAtOrDefault(itemIndex);
|
var oldItem = Item.ItemList.Where(it => it.Name == args[0]).ElementAtOrDefault(itemIndex);
|
||||||
if (oldItem == null)
|
if (oldItem == null)
|
||||||
{
|
{
|
||||||
ThrowError($"Could not find an item with the name {args[0]} (index {itemIndex}).");
|
ThrowError($"Could not find an item with the name {args[0]} (index {itemIndex}).");
|
||||||
@@ -1852,7 +1852,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
commands.Add(new Command("power", "power: Immediately powers up the submarine's nuclear reactor.", (string[] args) =>
|
commands.Add(new Command("power", "power: Immediately powers up the submarine's nuclear reactor.", (string[] args) =>
|
||||||
{
|
{
|
||||||
Item reactorItem = Item.ItemList.Find(i => i.GetComponent<Reactor>() != null);
|
Item reactorItem = Item.ItemList.FirstOrDefault(i => i.GetComponent<Reactor>() != null);
|
||||||
if (reactorItem == null) { return; }
|
if (reactorItem == null) { return; }
|
||||||
|
|
||||||
var reactor = reactorItem.GetComponent<Reactor>();
|
var reactor = reactorItem.GetComponent<Reactor>();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
protected override void InitEventSpecific(EventSet parentSet)
|
protected override void InitEventSpecific(EventSet parentSet)
|
||||||
{
|
{
|
||||||
var matchingItems = Item.ItemList.FindAll(i => i.Condition > 0.0f && targetItemIdentifiers.Contains(i.Prefab.Identifier));
|
var matchingItems = Item.ItemList.Where(i => i.Condition > 0.0f && targetItemIdentifiers.Contains(i.Prefab.Identifier)).ToList();
|
||||||
int itemAmount = Rand.Range(minItemAmount, maxItemAmount, Rand.RandSync.ServerAndClient);
|
int itemAmount = Rand.Range(minItemAmount, maxItemAmount, Rand.RandSync.ServerAndClient);
|
||||||
for (int i = 0; i < itemAmount; i++)
|
for (int i = 0; i < itemAmount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (!itemTag.IsEmpty)
|
if (!itemTag.IsEmpty)
|
||||||
{
|
{
|
||||||
var itemsToDestroy = Item.ItemList.FindAll(it => it.Submarine?.Info.Type != SubmarineType.Player && it.HasTag(itemTag));
|
var itemsToDestroy = Item.ItemList.Where(it => it.Submarine?.Info.Type != SubmarineType.Player && it.HasTag(itemTag)).ToList();
|
||||||
if (!itemsToDestroy.Any())
|
if (!itemsToDestroy.Any())
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError($"Error in mission \"{Prefab.Identifier}\". Could not find an item with the tag \"{itemTag}\".",
|
DebugConsole.ThrowError($"Error in mission \"{Prefab.Identifier}\". Could not find an item with the tag \"{itemTag}\".",
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ namespace Barotrauma
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
destructibleItems.Clear();
|
destructibleItems.Clear();
|
||||||
destructibleItems.AddRange(Item.ItemList.FindAll(it => it.HasTag(destructibleItemTag)));
|
destructibleItems.AddRange(Item.ItemList.Where(it => it.HasTag(destructibleItemTag)));
|
||||||
if (destructibleItems.None())
|
if (destructibleItems.None())
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError($"Error in end mission \"{Prefab.Identifier}\". Could not find any destructible items with the tag \"{spawnPointTag}\".",
|
DebugConsole.ThrowError($"Error in end mission \"{Prefab.Identifier}\". Could not find any destructible items with the tag \"{spawnPointTag}\".",
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (var stackedItem in item.GetStackedItems())
|
foreach (var stackedItem in item.GetStackedItems())
|
||||||
{
|
{
|
||||||
Item.DeconstructItems.Add(stackedItem);
|
Item.MarkForDeconstruction(stackedItem);
|
||||||
}
|
}
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
HintManager.OnItemMarkedForDeconstruction(order.OrderGiver);
|
HintManager.OnItemMarkedForDeconstruction(order.OrderGiver);
|
||||||
@@ -138,7 +138,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (var stackedItem in item.GetStackedItems())
|
foreach (var stackedItem in item.GetStackedItems())
|
||||||
{
|
{
|
||||||
Item.DeconstructItems.Remove(stackedItem);
|
Item.UnmarkForDeconstruction(stackedItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using FarseerPhysics.Dynamics;
|
|||||||
using FarseerPhysics.Dynamics.Contacts;
|
using FarseerPhysics.Dynamics.Contacts;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -24,7 +25,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
private readonly HashSet<Entity> hitTargets = new HashSet<Entity>();
|
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; }
|
public Character User { get; private set; }
|
||||||
|
|
||||||
@@ -190,17 +191,16 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
if (!item.body.Enabled)
|
if (!item.body.Enabled)
|
||||||
{
|
{
|
||||||
impactQueue.Clear();
|
while (impactQueue.TryDequeue(out _)) { } // Clear queue
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (picker == null || !picker.HeldItems.Contains(item))
|
if (picker == null || !picker.HeldItems.Contains(item))
|
||||||
{
|
{
|
||||||
impactQueue.Clear();
|
while (impactQueue.TryDequeue(out _)) { } // Clear queue
|
||||||
IsActive = false;
|
IsActive = false;
|
||||||
}
|
}
|
||||||
while (impactQueue.Count > 0)
|
while (impactQueue.TryDequeue(out var impact))
|
||||||
{
|
{
|
||||||
var impact = impactQueue.Dequeue();
|
|
||||||
HandleImpact(impact);
|
HandleImpact(impact);
|
||||||
}
|
}
|
||||||
//in case handling the impact does something to the picker
|
//in case handling the impact does something to the picker
|
||||||
@@ -300,7 +300,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
private void RestoreCollision()
|
private void RestoreCollision()
|
||||||
{
|
{
|
||||||
impactQueue.Clear();
|
while (impactQueue.TryDequeue(out _)) { } // Clear queue
|
||||||
item.body.FarseerBody.OnCollision -= OnCollision;
|
item.body.FarseerBody.OnCollision -= OnCollision;
|
||||||
item.body.CollisionCategories = Physics.CollisionItem;
|
item.body.CollisionCategories = Physics.CollisionItem;
|
||||||
item.body.CollidesWith = Physics.DefaultItemCollidesWith;
|
item.body.CollidesWith = Physics.DefaultItemCollidesWith;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using FarseerPhysics.Dynamics.Contacts;
|
|||||||
using FarseerPhysics.Dynamics.Joints;
|
using FarseerPhysics.Dynamics.Joints;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -72,7 +73,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public const float WaterDragCoefficient = 0.1f;
|
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;
|
private bool removePending;
|
||||||
|
|
||||||
@@ -840,9 +841,8 @@ namespace Barotrauma.Items.Components
|
|||||||
DisableProjectileCollisions();
|
DisableProjectileCollisions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (impactQueue.Count > 0)
|
while (impactQueue.TryDequeue(out var impact))
|
||||||
{
|
{
|
||||||
var impact = impactQueue.Dequeue();
|
|
||||||
HandleProjectileCollision(impact.Fixture, impact.Normal, impact.LinearVelocity);
|
HandleProjectileCollision(impact.Fixture, impact.Normal, impact.LinearVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -723,11 +723,11 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
if (item0 == null && item1 != null)
|
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)
|
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; }
|
if (item0 == null || item1 == null || nodes.Count == 0) { return; }
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ using Barotrauma.Extensions;
|
|||||||
using Barotrauma.MapCreatures.Behavior;
|
using Barotrauma.MapCreatures.Behavior;
|
||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using System.Threading;
|
||||||
using Barotrauma.Abilities;
|
using Barotrauma.Abilities;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
@@ -27,57 +29,172 @@ namespace Barotrauma
|
|||||||
#region Lists
|
#region Lists
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of every item that exists somewhere in the world. Note that there can be a huge number of items in the list,
|
/// Thread-safe dictionary of all items by ID.
|
||||||
/// and you probably shouldn't be enumerating it to find some that match some specific criteria (unless that's done very, very sparsely or during initialization).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly List<Item> ItemList = new List<Item>();
|
private static readonly ConcurrentDictionary<ushort, Item> _itemDictionary = new ConcurrentDictionary<ushort, Item>();
|
||||||
|
|
||||||
private static readonly HashSet<Item> _dangerousItems = new HashSet<Item>();
|
/// <summary>
|
||||||
|
/// Provides thread-safe enumeration over all items.
|
||||||
|
/// </summary>
|
||||||
|
public static ICollection<Item> ItemList => _itemDictionary.Values;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Thread-safe item lookup by ID.
|
||||||
|
/// </summary>
|
||||||
|
public static Item GetItemById(ushort id)
|
||||||
|
{
|
||||||
|
_itemDictionary.TryGetValue(id, out var item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread-safe optimized item collections using Immutable + atomic swap pattern
|
||||||
|
private static volatile ImmutableHashSet<Item> _dangerousItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
private static volatile ImmutableHashSet<Item> _repairableItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
private static volatile ImmutableHashSet<Item> _cleanableItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
private static volatile ImmutableHashSet<Item> _sonarVisibleItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
private static volatile ImmutableHashSet<Item> _turretTargetItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
private static volatile ImmutableHashSet<Item> _chairItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
|
||||||
|
// DeconstructItems uses ConcurrentDictionary to simulate a thread-safe HashSet
|
||||||
|
private static readonly ConcurrentDictionary<Item, byte> _deconstructItems = new ConcurrentDictionary<Item, byte>();
|
||||||
|
|
||||||
public static IReadOnlyCollection<Item> DangerousItems => _dangerousItems;
|
public static IReadOnlyCollection<Item> DangerousItems => _dangerousItems;
|
||||||
|
|
||||||
private static readonly List<Item> _repairableItems = new List<Item>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Items that have one more more Repairable component
|
/// Items that have one more more Repairable component
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IReadOnlyCollection<Item> RepairableItems => _repairableItems;
|
public static IReadOnlyCollection<Item> RepairableItems => _repairableItems;
|
||||||
|
|
||||||
private static readonly List<Item> _cleanableItems = new List<Item>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Items that may potentially need to be cleaned up (pickable, not attached to a wall, and not inside a valid container)
|
/// Items that may potentially need to be cleaned up (pickable, not attached to a wall, and not inside a valid container)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IReadOnlyCollection<Item> CleanableItems => _cleanableItems;
|
public static IReadOnlyCollection<Item> CleanableItems => _cleanableItems;
|
||||||
|
|
||||||
private static readonly HashSet<Item> _deconstructItems = new HashSet<Item>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Items that have been marked for deconstruction
|
/// Items that have been marked for deconstruction. Thread-safe collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static HashSet<Item> DeconstructItems => _deconstructItems;
|
public static ICollection<Item> DeconstructItems => _deconstructItems.Keys;
|
||||||
|
|
||||||
private static readonly List<Item> _sonarVisibleItems = new List<Item>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Items whose <see cref="ItemPrefab.SonarSize"/> is larger than 0
|
/// Items whose <see cref="ItemPrefab.SonarSize"/> is larger than 0
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IReadOnlyCollection<Item> SonarVisibleItems => _sonarVisibleItems;
|
public static IReadOnlyCollection<Item> SonarVisibleItems => _sonarVisibleItems;
|
||||||
|
|
||||||
private static readonly List<Item> _turretTargetItems = new List<Item>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Items whose <see cref="ItemPrefab.IsAITurretTarget"/> is true.
|
/// Items whose <see cref="ItemPrefab.IsAITurretTarget"/> is true.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IReadOnlyCollection<Item> TurretTargetItems => _turretTargetItems;
|
public static IReadOnlyCollection<Item> TurretTargetItems => _turretTargetItems;
|
||||||
|
|
||||||
private static readonly List<Item> _chairItems = new List<Item>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Items that have the tag <see cref="Tags.ChairItem"/>. Which is an oddly specific thing, but useful as an optimization for NPC AI.
|
/// Items that have the tag <see cref="Tags.ChairItem"/>. Which is an oddly specific thing, but useful as an optimization for NPC AI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IReadOnlyCollection<Item> ChairItems => _chairItems;
|
public static IReadOnlyCollection<Item> ChairItems => _chairItems;
|
||||||
|
|
||||||
|
#region Thread-safe collection helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Atomically adds an item to an immutable set using compare-and-swap.
|
||||||
|
/// </summary>
|
||||||
|
private static void AddToImmutableSet(ref ImmutableHashSet<Item> location, Item item)
|
||||||
|
{
|
||||||
|
ImmutableHashSet<Item> original, updated;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
original = location;
|
||||||
|
updated = original.Add(item);
|
||||||
|
if (ReferenceEquals(original, updated)) return; // Already exists
|
||||||
|
}
|
||||||
|
while (Interlocked.CompareExchange(ref location, updated, original) != original);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Atomically removes an item from an immutable set using compare-and-swap.
|
||||||
|
/// </summary>
|
||||||
|
private static void RemoveFromImmutableSet(ref ImmutableHashSet<Item> location, Item item)
|
||||||
|
{
|
||||||
|
ImmutableHashSet<Item> original, updated;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
original = location;
|
||||||
|
updated = original.Remove(item);
|
||||||
|
if (ReferenceEquals(original, updated)) return; // Doesn't exist
|
||||||
|
}
|
||||||
|
while (Interlocked.CompareExchange(ref location, updated, original) != original);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks an item for deconstruction (thread-safe).
|
||||||
|
/// </summary>
|
||||||
|
public static void MarkForDeconstruction(Item item)
|
||||||
|
{
|
||||||
|
_deconstructItems.TryAdd(item, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unmarks an item for deconstruction (thread-safe).
|
||||||
|
/// </summary>
|
||||||
|
public static void UnmarkForDeconstruction(Item item)
|
||||||
|
{
|
||||||
|
_deconstructItems.TryRemove(item, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if an item is marked for deconstruction (thread-safe).
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsMarkedForDeconstruction(Item item)
|
||||||
|
{
|
||||||
|
return _deconstructItems.ContainsKey(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all item collections (thread-safe). Used during unloading.
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearAllItemCollections()
|
||||||
|
{
|
||||||
|
_itemDictionary.Clear();
|
||||||
|
_dangerousItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
_repairableItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
_cleanableItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
_sonarVisibleItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
_turretTargetItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
_chairItems = ImmutableHashSet<Item>.Empty;
|
||||||
|
_deconstructItems.Clear();
|
||||||
|
while (_pendingConditionUpdates.TryDequeue(out _)) { }
|
||||||
|
_cachedItemList = null;
|
||||||
|
_cachedItemListVersion = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cached item list for indexed access (used by AI systems)
|
||||||
|
private static volatile List<Item> _cachedItemList;
|
||||||
|
private static volatile int _cachedItemListVersion = -1;
|
||||||
|
private static volatile int _itemListVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a cached list snapshot of all items for indexed access.
|
||||||
|
/// The list is refreshed when items are added or removed.
|
||||||
|
/// Thread-safe but may return slightly stale data.
|
||||||
|
/// </summary>
|
||||||
|
public static List<Item> GetCachedItemList()
|
||||||
|
{
|
||||||
|
int currentVersion = _itemListVersion;
|
||||||
|
if (_cachedItemList == null || _cachedItemListVersion != currentVersion)
|
||||||
|
{
|
||||||
|
_cachedItemList = _itemDictionary.Values.ToList();
|
||||||
|
_cachedItemListVersion = currentVersion;
|
||||||
|
}
|
||||||
|
return _cachedItemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when items are added or removed to invalidate the cached list.
|
||||||
|
/// </summary>
|
||||||
|
private static void InvalidateCachedItemList()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _itemListVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public new ItemPrefab Prefab => base.Prefab as ItemPrefab;
|
public new ItemPrefab Prefab => base.Prefab as ItemPrefab;
|
||||||
@@ -179,7 +296,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool transformDirty = true;
|
private bool transformDirty = true;
|
||||||
|
|
||||||
private static readonly List<Item> itemsWithPendingConditionUpdates = new List<Item>();
|
private static readonly ConcurrentQueue<Item> _pendingConditionUpdates = new ConcurrentQueue<Item>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flag to avoid duplicate enqueue for pending condition updates.
|
||||||
|
/// </summary>
|
||||||
|
private volatile bool _hasPendingConditionUpdate;
|
||||||
|
|
||||||
private float lastSentCondition;
|
private float lastSentCondition;
|
||||||
private float sendConditionUpdateTimer;
|
private float sendConditionUpdateTimer;
|
||||||
@@ -845,11 +967,11 @@ namespace Barotrauma
|
|||||||
isDangerous = value;
|
isDangerous = value;
|
||||||
if (!value)
|
if (!value)
|
||||||
{
|
{
|
||||||
_dangerousItems.Remove(this);
|
RemoveFromImmutableSet(ref _dangerousItems, this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_dangerousItems.Add(this);
|
AddToImmutableSet(ref _dangerousItems, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1398,12 +1520,13 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
InsertToList();
|
InsertToList();
|
||||||
ItemList.Add(this);
|
_itemDictionary.TryAdd(ID, this);
|
||||||
if (Prefab.IsDangerous) { _dangerousItems.Add(this); }
|
InvalidateCachedItemList();
|
||||||
if (Repairables.Any()) { _repairableItems.Add(this); }
|
if (Prefab.IsDangerous) { AddToImmutableSet(ref _dangerousItems, this); }
|
||||||
if (Prefab.SonarSize > 0.0f) { _sonarVisibleItems.Add(this); }
|
if (Repairables.Any()) { AddToImmutableSet(ref _repairableItems, this); }
|
||||||
if (Prefab.IsAITurretTarget) { _turretTargetItems.Add(this); }
|
if (Prefab.SonarSize > 0.0f) { AddToImmutableSet(ref _sonarVisibleItems, this); }
|
||||||
if (Prefab.Tags.Contains(Barotrauma.Tags.ChairItem)) { _chairItems.Add(this); }
|
if (Prefab.IsAITurretTarget) { AddToImmutableSet(ref _turretTargetItems, this); }
|
||||||
|
if (Prefab.Tags.Contains(Barotrauma.Tags.ChairItem)) { AddToImmutableSet(ref _chairItems, this); }
|
||||||
CheckCleanable();
|
CheckCleanable();
|
||||||
|
|
||||||
DebugConsole.Log("Created " + Name + " (" + ID + ")");
|
DebugConsole.Log("Created " + Name + " (" + ID + ")");
|
||||||
@@ -1756,14 +1879,11 @@ namespace Barotrauma
|
|||||||
Prefab.PreferredContainers.Any() &&
|
Prefab.PreferredContainers.Any() &&
|
||||||
(container == null || container.HasTag(Barotrauma.Tags.AllowCleanup)))
|
(container == null || container.HasTag(Barotrauma.Tags.AllowCleanup)))
|
||||||
{
|
{
|
||||||
if (!_cleanableItems.Contains(this))
|
AddToImmutableSet(ref _cleanableItems, this);
|
||||||
{
|
|
||||||
_cleanableItems.Add(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_cleanableItems.Remove(this);
|
RemoveFromImmutableSet(ref _cleanableItems, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2294,9 +2414,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
needsConditionUpdate = true;
|
needsConditionUpdate = true;
|
||||||
}
|
}
|
||||||
if (needsConditionUpdate && !itemsWithPendingConditionUpdates.Contains(this))
|
if (needsConditionUpdate && !_hasPendingConditionUpdate)
|
||||||
{
|
{
|
||||||
itemsWithPendingConditionUpdates.Add(this);
|
_hasPendingConditionUpdate = true;
|
||||||
|
_pendingConditionUpdates.Enqueue(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2352,9 +2473,9 @@ namespace Barotrauma
|
|||||||
public void SendPendingNetworkUpdates()
|
public void SendPendingNetworkUpdates()
|
||||||
{
|
{
|
||||||
if (!(GameMain.NetworkMember is { IsServer: true })) { return; }
|
if (!(GameMain.NetworkMember is { IsServer: true })) { return; }
|
||||||
if (!itemsWithPendingConditionUpdates.Contains(this)) { return; }
|
if (!_hasPendingConditionUpdate) { return; }
|
||||||
SendPendingNetworkUpdatesInternal();
|
SendPendingNetworkUpdatesInternal();
|
||||||
itemsWithPendingConditionUpdates.Remove(this);
|
_hasPendingConditionUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendPendingNetworkUpdatesInternal()
|
private void SendPendingNetworkUpdatesInternal()
|
||||||
@@ -2383,21 +2504,35 @@ namespace Barotrauma
|
|||||||
public static void UpdatePendingConditionUpdates(float deltaTime)
|
public static void UpdatePendingConditionUpdates(float deltaTime)
|
||||||
{
|
{
|
||||||
if (GameMain.NetworkMember is not { IsServer: true }) { return; }
|
if (GameMain.NetworkMember is not { IsServer: true }) { return; }
|
||||||
for (int i = 0; i < itemsWithPendingConditionUpdates.Count; i++)
|
|
||||||
|
int count = _pendingConditionUpdates.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var item = itemsWithPendingConditionUpdates[i];
|
if (!_pendingConditionUpdates.TryDequeue(out var item)) { break; }
|
||||||
|
|
||||||
if (item == null || item.Removed)
|
if (item == null || item.Removed)
|
||||||
{
|
{
|
||||||
itemsWithPendingConditionUpdates.RemoveAt(i--);
|
item._hasPendingConditionUpdate = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Submarine is { Loading: true })
|
||||||
|
{
|
||||||
|
// Re-enqueue, still loading
|
||||||
|
_pendingConditionUpdates.Enqueue(item);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (item.Submarine is { Loading: true }) { continue; }
|
|
||||||
|
|
||||||
item.sendConditionUpdateTimer -= deltaTime;
|
item.sendConditionUpdateTimer -= deltaTime;
|
||||||
if (item.sendConditionUpdateTimer <= 0.0f)
|
if (item.sendConditionUpdateTimer <= 0.0f)
|
||||||
{
|
{
|
||||||
item.SendPendingNetworkUpdatesInternal();
|
item.SendPendingNetworkUpdatesInternal();
|
||||||
itemsWithPendingConditionUpdates.RemoveAt(i--);
|
item._hasPendingConditionUpdate = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not ready yet, re-enqueue
|
||||||
|
_pendingConditionUpdates.Enqueue(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4217,7 +4352,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.GetAttributeBool("markedfordeconstruction", false)) { _deconstructItems.Add(item); }
|
if (element.GetAttributeBool("markedfordeconstruction", false)) { _deconstructItems.TryAdd(item, 0); }
|
||||||
|
|
||||||
float prevRotation = item.Rotation;
|
float prevRotation = item.Rotation;
|
||||||
if (element.GetAttributeBool("flippedx", false)) { item.FlipX(relativeToSub: false, force: true); }
|
if (element.GetAttributeBool("flippedx", false)) { item.FlipX(relativeToSub: false, force: true); }
|
||||||
@@ -4510,7 +4645,7 @@ namespace Barotrauma
|
|||||||
new XAttribute("name", Prefab.OriginalName),
|
new XAttribute("name", Prefab.OriginalName),
|
||||||
new XAttribute("identifier", Prefab.Identifier),
|
new XAttribute("identifier", Prefab.Identifier),
|
||||||
new XAttribute("ID", ID),
|
new XAttribute("ID", ID),
|
||||||
new XAttribute("markedfordeconstruction", _deconstructItems.Contains(this)));
|
new XAttribute("markedfordeconstruction", _deconstructItems.ContainsKey(this)));
|
||||||
|
|
||||||
if (PendingItemSwap != null)
|
if (PendingItemSwap != null)
|
||||||
{
|
{
|
||||||
@@ -4713,14 +4848,16 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void RemoveFromLists()
|
private void RemoveFromLists()
|
||||||
{
|
{
|
||||||
ItemList.Remove(this);
|
_itemDictionary.TryRemove(ID, out _);
|
||||||
_dangerousItems.Remove(this);
|
InvalidateCachedItemList();
|
||||||
_repairableItems.Remove(this);
|
RemoveFromImmutableSet(ref _dangerousItems, this);
|
||||||
_sonarVisibleItems.Remove(this);
|
RemoveFromImmutableSet(ref _repairableItems, this);
|
||||||
_cleanableItems.Remove(this);
|
RemoveFromImmutableSet(ref _sonarVisibleItems, this);
|
||||||
_deconstructItems.Remove(this);
|
RemoveFromImmutableSet(ref _cleanableItems, this);
|
||||||
_turretTargetItems.Remove(this);
|
_deconstructItems.TryRemove(this, out _);
|
||||||
_chairItems.Remove(this);
|
RemoveFromImmutableSet(ref _turretTargetItems, this);
|
||||||
|
RemoveFromImmutableSet(ref _chairItems, this);
|
||||||
|
_hasPendingConditionUpdate = false;
|
||||||
RemoveFromDroppedStack(allowClientExecute: true);
|
RemoveFromDroppedStack(allowClientExecute: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ namespace Barotrauma
|
|||||||
DebugConsole.ThrowError($"Error while removing item \"{item}\"", exception);
|
DebugConsole.ThrowError($"Error while removing item \"{item}\"", exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item.ItemList.Clear();
|
Item.ClearAllItemCollections();
|
||||||
}
|
}
|
||||||
if (Character.CharacterList.Count > 0)
|
if (Character.CharacterList.Count > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4775,7 +4775,7 @@ namespace Barotrauma
|
|||||||
// BeaconStation.FlipX();
|
// BeaconStation.FlipX();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Item sonarItem = Item.ItemList.Find(it => it.Submarine == BeaconStation && it.GetComponent<Sonar>() != null);
|
Item sonarItem = Item.ItemList.FirstOrDefault(it => it.Submarine == BeaconStation && it.GetComponent<Sonar>() != null);
|
||||||
if (sonarItem == null)
|
if (sonarItem == null)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError($"No sonar found in the beacon station \"{beaconStationName}\"!");
|
DebugConsole.ThrowError($"No sonar found in the beacon station \"{beaconStationName}\"!");
|
||||||
@@ -4794,7 +4794,7 @@ namespace Barotrauma
|
|||||||
throw new InvalidOperationException("Failed to prepare beacon station (no beacon station in the level).");
|
throw new InvalidOperationException("Failed to prepare beacon station (no beacon station in the level).");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Item> beaconItems = Item.ItemList.FindAll(it => it.Submarine == BeaconStation);
|
List<Item> beaconItems = Item.ItemList.Where(it => it.Submarine == BeaconStation).ToList();
|
||||||
|
|
||||||
Item reactorItem = beaconItems.Find(it => it.GetComponent<Reactor>() != null);
|
Item reactorItem = beaconItems.Find(it => it.GetComponent<Reactor>() != null);
|
||||||
Reactor reactorComponent = null;
|
Reactor reactorComponent = null;
|
||||||
@@ -4840,7 +4840,7 @@ namespace Barotrauma
|
|||||||
if (BeaconStation?.Info?.BeaconStationInfo is { AllowDisconnectedWires: false }) { return; }
|
if (BeaconStation?.Info?.BeaconStationInfo is { AllowDisconnectedWires: false }) { return; }
|
||||||
|
|
||||||
if (disconnectWireProbability <= 0.0f) { return; }
|
if (disconnectWireProbability <= 0.0f) { return; }
|
||||||
List<Item> beaconItems = Item.ItemList.FindAll(it => it.Submarine == BeaconStation);
|
List<Item> beaconItems = Item.ItemList.Where(it => it.Submarine == BeaconStation).ToList();
|
||||||
foreach (Item item in beaconItems.Where(it => it.GetComponent<Wire>() != null).ToList())
|
foreach (Item item in beaconItems.Where(it => it.GetComponent<Wire>() != null).ToList())
|
||||||
{
|
{
|
||||||
if (item.NonInteractable || item.InvulnerableToDamage) { continue; }
|
if (item.NonInteractable || item.InvulnerableToDamage) { continue; }
|
||||||
@@ -4878,7 +4878,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (breakDeviceProbability <= 0.0f) { return; }
|
if (breakDeviceProbability <= 0.0f) { return; }
|
||||||
//break powered items
|
//break powered items
|
||||||
List<Item> beaconItems = Item.ItemList.FindAll(it => it.Submarine == BeaconStation);
|
List<Item> beaconItems = Item.ItemList.Where(it => it.Submarine == BeaconStation).ToList();
|
||||||
foreach (Item item in beaconItems.Where(it => it.Components.Any(c => c is Powered) && it.Components.Any(c => c is Repairable)))
|
foreach (Item item in beaconItems.Where(it => it.Components.Any(c => c is Powered) && it.Components.Any(c => c is Repairable)))
|
||||||
{
|
{
|
||||||
if (item.NonInteractable || item.InvulnerableToDamage) { continue; }
|
if (item.NonInteractable || item.InvulnerableToDamage) { continue; }
|
||||||
|
|||||||
@@ -1371,7 +1371,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (TakenItem takenItem in takenItems)
|
foreach (TakenItem takenItem in takenItems)
|
||||||
{
|
{
|
||||||
Item item = Item.ItemList.Find(it => takenItem.Matches(it));
|
Item item = Item.ItemList.FirstOrDefault(it => takenItem.Matches(it));
|
||||||
item?.Remove();
|
item?.Remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1077,7 +1077,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
Item.UpdateHulls();
|
Item.UpdateHulls();
|
||||||
|
|
||||||
List<Item> bodyItems = Item.ItemList.FindAll(it => it.Submarine == this && it.body != null);
|
List<Item> bodyItems = Item.ItemList.Where(it => it.Submarine == this && it.body != null).ToList();
|
||||||
List<MapEntity> subEntities = MapEntity.MapEntityList.FindAll(me => me.Submarine == this);
|
List<MapEntity> subEntities = MapEntity.MapEntityList.FindAll(me => me.Submarine == this);
|
||||||
|
|
||||||
foreach (MapEntity e in subEntities)
|
foreach (MapEntity e in subEntities)
|
||||||
@@ -1507,7 +1507,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public List<Hull> GetHulls(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Hull.HullList);
|
public List<Hull> GetHulls(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Hull.HullList);
|
||||||
public List<Gap> GetGaps(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Gap.GapList);
|
public List<Gap> GetGaps(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Gap.GapList);
|
||||||
public List<Item> GetItems(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Item.ItemList);
|
public List<Item> GetItems(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Item.ItemList).ToList();
|
||||||
public List<WayPoint> GetWaypoints(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, WayPoint.WayPointList);
|
public List<WayPoint> GetWaypoints(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, WayPoint.WayPointList);
|
||||||
public List<Structure> GetWalls(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Structure.WallList);
|
public List<Structure> GetWalls(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Structure.WallList);
|
||||||
|
|
||||||
@@ -2148,7 +2148,7 @@ namespace Barotrauma
|
|||||||
DebugConsole.ThrowError("Error while removing \"" + item.Name + "\"!", e);
|
DebugConsole.ThrowError("Error while removing \"" + item.Name + "\"!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item.ItemList.Clear();
|
Item.ClearAllItemCollections();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ragdoll.RemoveAll();
|
Ragdoll.RemoveAll();
|
||||||
|
|||||||
Reference in New Issue
Block a user