Reapply "OBT1.1.0 Merge branch 'dev_pte' into dev"

This reverts commit 046483b9da.
This commit is contained in:
NotAlwaysTrue
2026-04-30 21:59:54 +08:00
parent 02689d0d86
commit 25683dcf39
85 changed files with 2413 additions and 779 deletions
@@ -227,13 +227,14 @@ namespace Barotrauma.Items.Components
public void SetRecipientsDirty()
{
recipientsDirty = true;
if (IsPower) { Powered.ChangedConnections.Add(this); }
if (IsPower) { Powered.MarkConnectionChanged(this); }
}
private void RefreshRecipients()
{
recipients.Clear();
foreach (var wire in wires)
// Use ToArray() snapshot for thread-safe iteration
foreach (var wire in wires.ToArray())
{
Connection recipient = wire.OtherConnection(this);
if (recipient != null) { recipients.Add(recipient); }
@@ -272,8 +273,8 @@ namespace Barotrauma.Items.Components
//Check if both connections belong to a larger grid
if (prevOtherConnection.recipients.Count > 1 && recipients.Count > 1)
{
Powered.ChangedConnections.Add(prevOtherConnection);
Powered.ChangedConnections.Add(this);
Powered.MarkConnectionChanged(prevOtherConnection);
Powered.MarkConnectionChanged(this);
}
else if (recipients.Count > 1)
{
@@ -289,7 +290,7 @@ namespace Barotrauma.Items.Components
else if (Grid.Connections.Count == 2)
{
//Delete the grid as these were the only 2 devices
Powered.Grids.Remove(Grid.ID);
Powered.Grids.TryRemove(Grid.ID, out _);
Grid = null;
prevOtherConnection.Grid = null;
}
@@ -330,8 +331,8 @@ namespace Barotrauma.Items.Components
else
{
//Flag change so that proper grids can be formed
Powered.ChangedConnections.Add(this);
Powered.ChangedConnections.Add(otherConnection);
Powered.MarkConnectionChanged(this);
Powered.MarkConnectionChanged(otherConnection);
}
}
@@ -344,7 +345,8 @@ namespace Barotrauma.Items.Components
{
LastSentSignal = signal;
enumeratingWires = true;
foreach (var wire in wires)
// Use ToArray() snapshot for thread-safe iteration
foreach (var wire in wires.ToArray())
{
Connection recipient = wire.OtherConnection(this);
if (recipient == null) { continue; }
@@ -357,12 +359,12 @@ namespace Barotrauma.Items.Components
SendSignalIntoConnection(signal, recipient);
}
foreach (CircuitBoxConnection connection in CircuitBoxConnections)
foreach (CircuitBoxConnection connection in CircuitBoxConnections.ToArray())
{
connection.ReceiveSignal(signal);
}
enumeratingWires = false;
foreach (var removedWire in removedWires)
foreach (var removedWire in removedWires.ToArray())
{
wires.Remove(removedWire);
}
@@ -373,14 +375,16 @@ namespace Barotrauma.Items.Components
{
conn.LastReceivedSignal = signal;
foreach (ItemComponent ic in conn.item.Components)
// Use ToArray() snapshot for thread-safe iteration
foreach (ItemComponent ic in conn.item.Components.ToArray())
{
ic.ReceiveSignal(signal, conn);
}
if (conn.Effects == null || signal.value == "0") { return; }
foreach (StatusEffect effect in conn.Effects)
// Use ToArray() snapshot for thread-safe iteration
foreach (StatusEffect effect in conn.Effects.ToArray())
{
conn.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
}
@@ -390,13 +394,15 @@ namespace Barotrauma.Items.Components
{
if (IsPower && Grid != null)
{
Powered.ChangedConnections.Add(this);
foreach (Connection c in recipients)
Powered.MarkConnectionChanged(this);
// Use ToArray() snapshot for thread-safe iteration
foreach (Connection c in recipients.ToArray())
{
Powered.ChangedConnections.Add(c);
Powered.MarkConnectionChanged(c);
}
}
foreach (var wire in wires)
// Use ToArray() snapshot for thread-safe iteration
foreach (var wire in wires.ToArray())
{
wire.RemoveConnection(this);
recipientsDirty = true;
@@ -404,7 +410,7 @@ namespace Barotrauma.Items.Components
if (enumeratingWires)
{
foreach (var wire in wires)
foreach (var wire in wires.ToArray())
{
removedWires.Add(wire);
}
@@ -447,7 +453,8 @@ namespace Barotrauma.Items.Components
{
XElement newElement = new XElement(IsOutput ? "output" : "input", new XAttribute("name", Name));
foreach (var wire in wires.OrderBy(w => w.Item.ID))
// Use ToArray() snapshot before OrderBy for thread-safe iteration
foreach (var wire in wires.ToArray().OrderBy(w => w.Item.ID))
{
newElement.Add(new XElement("link",
new XAttribute("w", wire.Item.ID.ToString()),
@@ -148,14 +148,16 @@ namespace Barotrauma.Items.Components
Vector2 wireNodeOffset = item.Submarine == null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
foreach (Connection c in Connections)
{
foreach (Wire wire in c.Wires)
// Use ToArray() snapshot for thread-safe iteration
foreach (Wire wire in c.Wires.ToArray())
{
if (wire == null) { continue; }
TryMoveWire(wire);
}
}
foreach (var wire in DisconnectedWires)
// Use ToList() snapshot for thread-safe iteration
foreach (var wire in DisconnectedWires.ToList())
{
TryMoveWire(wire);
}
@@ -387,7 +389,7 @@ namespace Barotrauma.Items.Components
}
foreach (var connection in Connections)
{
Powered.ChangedConnections.Remove(connection);
Powered.UnmarkConnectionChanged(connection);
connection.Recipients.Clear();
}
Connections.Clear();
@@ -412,15 +414,19 @@ namespace Barotrauma.Items.Components
msg.WriteByte((byte)Connections.Count);
foreach (Connection connection in Connections)
{
msg.WriteVariableUInt32((uint)connection.Wires.Count);
foreach (Wire wire in connection.Wires)
// Use ToArray() snapshot for thread-safe iteration
var wiresSnapshot = connection.Wires.ToArray();
msg.WriteVariableUInt32((uint)wiresSnapshot.Length);
foreach (Wire wire in wiresSnapshot)
{
msg.WriteUInt16(wire?.Item == null ? (ushort)0 : wire.Item.ID);
}
}
msg.WriteUInt16((ushort)DisconnectedWires.Count);
foreach (Wire disconnectedWire in DisconnectedWires)
// Use ToList() snapshot for thread-safe iteration
var disconnectedSnapshot = DisconnectedWires.ToList();
msg.WriteUInt16((ushort)disconnectedSnapshot.Count);
foreach (Wire disconnectedWire in disconnectedSnapshot)
{
msg.WriteUInt16(disconnectedWire.Item.ID);
}
@@ -1,6 +1,8 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma.Items.Components
@@ -25,7 +27,8 @@ namespace Barotrauma.Items.Components
private int signalQueueSize;
private int delayTicks;
private readonly Queue<DelayedSignal> signalQueue = new Queue<DelayedSignal>();
// Thread-safe queue for concurrent access
private readonly ConcurrentQueue<DelayedSignal> signalQueue = new ConcurrentQueue<DelayedSignal>();
private DelayedSignal prevQueuedSignal;
@@ -40,7 +43,8 @@ namespace Barotrauma.Items.Components
delay = value;
delayTicks = (int)(delay / Timing.Step);
signalQueueSize = Math.Max(delayTicks, 1) * 2;
signalQueue.Clear();
// ConcurrentQueue doesn't have Clear(), drain it instead
while (signalQueue.TryDequeue(out _)) { }
}
}
@@ -66,19 +70,19 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (signalQueue.Count == 0)
if (signalQueue.IsEmpty)
{
IsActive = false;
return;
}
foreach (var val in signalQueue)
// Use ToArray() snapshot for thread-safe iteration
foreach (var val in signalQueue.ToArray())
{
val.SendTimer -= 1;
}
while (signalQueue.Count > 0 && signalQueue.Peek().SendTimer <= 0)
while (signalQueue.TryPeek(out var signalOut) && signalOut.SendTimer <= 0)
{
var signalOut = signalQueue.Peek();
signalOut.SendDuration -= 1;
item.SendSignal(new Signal(signalOut.Signal.value, sender: signalOut.Signal.sender, strength: signalOut.Signal.strength), "signal_out");
if (signalOut.SendDuration <= 0)
@@ -100,11 +104,15 @@ namespace Barotrauma.Items.Components
{
case "signal_in":
if (signalQueue.Count >= signalQueueSize) { return; }
if (ResetWhenSignalReceived) { prevQueuedSignal = null; signalQueue.Clear(); }
if (ResetWhenDifferentSignalReceived && signalQueue.Count > 0 && signalQueue.Peek().Signal.value != signal.value)
if (ResetWhenSignalReceived)
{
prevQueuedSignal = null;
while (signalQueue.TryDequeue(out _)) { }
}
if (ResetWhenDifferentSignalReceived && signalQueue.TryPeek(out var peekSignal) && peekSignal.Signal.value != signal.value)
{
prevQueuedSignal = null;
signalQueue.Clear();
while (signalQueue.TryDequeue(out _)) { }
}
if (prevQueuedSignal != null &&
@@ -127,10 +135,10 @@ namespace Barotrauma.Items.Components
if (float.TryParse(signal.value, NumberStyles.Any, CultureInfo.InvariantCulture, out float newDelay))
{
newDelay = MathHelper.Clamp(newDelay, 0, 60);
if (signalQueue.Count > 0 && newDelay != Delay)
if (!signalQueue.IsEmpty && newDelay != Delay)
{
prevQueuedSignal = null;
signalQueue.Clear();
while (signalQueue.TryDequeue(out _)) { }
}
Delay = newDelay;
}
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
@@ -31,7 +32,8 @@ namespace Barotrauma.Items.Components
private float thirdInverseMax = 0, loadEqnConstant = 0;
private static readonly Dictionary<string, string> connectionPairs = new Dictionary<string, string>
// Thread-safe immutable dictionary for connection pairs (read-only after initialization)
private static readonly ImmutableDictionary<string, string> connectionPairs = new Dictionary<string, string>
{
{ "power_in", "power_out"},
{ "signal_in", "signal_out" },
@@ -40,7 +42,7 @@ namespace Barotrauma.Items.Components
{ "signal_in3", "signal_out3" },
{ "signal_in4", "signal_out4" },
{ "signal_in5", "signal_out5" }
};
}.ToImmutableDictionary();
protected override PowerPriority Priority { get { return PowerPriority.Relay; } }
@@ -2,6 +2,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -13,7 +14,8 @@ namespace Barotrauma.Items.Components
{
partial class WifiComponent : ItemComponent, IServerSerializable, IClientSerializable
{
private static readonly List<WifiComponent> list = new List<WifiComponent>();
private static readonly ConcurrentDictionary<WifiComponent, byte> _wifiDict = new ConcurrentDictionary<WifiComponent, byte>();
private static IEnumerable<WifiComponent> AllWifiComponents => _wifiDict.Keys;
const int ChannelMemorySize = 10;
@@ -114,7 +116,7 @@ namespace Barotrauma.Items.Components
public WifiComponent(Item item, ContentXElement element)
: base (item, element)
{
list.Add(this);
_wifiDict.TryAdd(this, 0);
IsActive = true;
}
@@ -159,7 +161,7 @@ namespace Barotrauma.Items.Components
/// </summary>
public IEnumerable<WifiComponent> GetReceiversInRange()
{
return list.Where(w => w != this && w.CanReceive(this));
return AllWifiComponents.Where(w => w != this && w.CanReceive(this));
}
public bool CanReceive(WifiComponent sender)
@@ -188,7 +190,7 @@ namespace Barotrauma.Items.Components
/// </summary>
public IEnumerable<WifiComponent> GetTransmittersInRange()
{
return list.Where(w => w != this && w.CanTransmit(this));
return AllWifiComponents.Where(w => w != this && w.CanTransmit(this));
}
public bool CanTransmit(WifiComponent sender)
@@ -278,7 +280,8 @@ namespace Barotrauma.Items.Components
if (signal.source != null)
{
foreach (Connection receiver in wifiComp.item.LastSentSignalRecipients)
// Use ToList() snapshot for thread-safe iteration
foreach (Connection receiver in wifiComp.item.LastSentSignalRecipients.ToList())
{
if (!signal.source.LastSentSignalRecipients.Contains(receiver))
{
@@ -369,7 +372,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
list.Remove(this);
_wifiDict.TryRemove(this, out _);
}
public override XElement Save(XElement parentElement)
@@ -250,7 +250,8 @@ namespace Barotrauma.Items.Components
{
if (connections[0] != null && connections[1] != null)
{
foreach (ItemComponent ic in item.Components)
// Use ToArray() snapshot for thread-safe iteration
foreach (ItemComponent ic in item.Components.ToArray())
{
if (ic == this) { continue; }
@@ -723,11 +724,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; }