Revert "OBT1.1.0 Merge branch 'dev_pte' into dev"
This reverts commit177cf89756, reversing changes made to42ba733cd4.
This commit is contained in:
@@ -5,7 +5,6 @@ using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if CLIENT
|
||||
@@ -25,8 +24,11 @@ namespace Barotrauma.Items.Components
|
||||
Right
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<DockingPort, byte> _dockingPortDict = new ConcurrentDictionary<DockingPort, byte>();
|
||||
public static IEnumerable<DockingPort> List => _dockingPortDict.Keys;
|
||||
private static readonly List<DockingPort> list = new List<DockingPort>();
|
||||
public static IEnumerable<DockingPort> List
|
||||
{
|
||||
get { return list; }
|
||||
}
|
||||
|
||||
private Sprite overlaySprite;
|
||||
private float dockingState;
|
||||
@@ -166,7 +168,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
IsActive = true;
|
||||
|
||||
_dockingPortDict.TryAdd(this, 0);
|
||||
list.Add(this);
|
||||
}
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
@@ -198,7 +200,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float closestDist = float.MaxValue;
|
||||
DockingPort closestPort = null;
|
||||
foreach (DockingPort port in List)
|
||||
foreach (DockingPort port in list)
|
||||
{
|
||||
if (port == this || port.item.Submarine == item.Submarine || port.IsHorizontal != IsHorizontal) { continue; }
|
||||
float xDist = Math.Abs(port.item.WorldPosition.X - item.WorldPosition.X);
|
||||
@@ -530,8 +532,8 @@ namespace Barotrauma.Items.Components
|
||||
wire.TryConnect(recipient, addNode: false);
|
||||
|
||||
//Flag connections to be updated
|
||||
Powered.MarkConnectionChanged(powerConnection);
|
||||
Powered.MarkConnectionChanged(recipient);
|
||||
Powered.ChangedConnections.Add(powerConnection);
|
||||
Powered.ChangedConnections.Add(recipient);
|
||||
}
|
||||
|
||||
private void CreateDoorBody()
|
||||
@@ -1005,7 +1007,7 @@ namespace Barotrauma.Items.Components
|
||||
Connection powerConnection = Item.Connections.Find(c => c.IsPower);
|
||||
if (powerConnection != null)
|
||||
{
|
||||
Powered.MarkConnectionChanged(powerConnection);
|
||||
Powered.ChangedConnections.Add(powerConnection);
|
||||
}
|
||||
|
||||
if (doorBody != null)
|
||||
@@ -1149,7 +1151,7 @@ namespace Barotrauma.Items.Components
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
_dockingPortDict.TryRemove(this, out _);
|
||||
list.Remove(this);
|
||||
hulls[0]?.Remove(); hulls[0] = null;
|
||||
hulls[1]?.Remove(); hulls[1] = null;
|
||||
gap?.Remove(); gap = null;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FarseerPhysics.Dynamics;
|
||||
@@ -15,9 +14,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Door : Pickable, IDrawableComponent, IServerSerializable
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Door, byte> _doorDict = new ConcurrentDictionary<Door, byte>();
|
||||
private static readonly HashSet<Door> doorList = new HashSet<Door>();
|
||||
|
||||
public static ICollection<Door> DoorList => _doorDict.Keys;
|
||||
public static IReadOnlyCollection<Door> DoorList { get { return doorList; } }
|
||||
|
||||
private Gap linkedGap;
|
||||
private bool isOpen;
|
||||
@@ -278,7 +277,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
IsActive = true;
|
||||
_doorDict.TryAdd(this, 0);
|
||||
doorList.Add(this);
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
@@ -314,21 +313,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
// Defer physics operation if in parallel context (Farseer is not thread-safe)
|
||||
if (Body != null)
|
||||
if (ignoreContacts)
|
||||
{
|
||||
var capturedBody = Body;
|
||||
var capturedNewPos = Body.SimPosition + ConvertUnits.ToSimUnits(amount);
|
||||
if (ignoreContacts)
|
||||
{
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() =>
|
||||
capturedBody.SetTransformIgnoreContacts(capturedNewPos, 0.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() =>
|
||||
capturedBody.SetTransform(capturedNewPos, 0.0f));
|
||||
}
|
||||
Body?.SetTransformIgnoreContacts(Body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Body?.SetTransform(Body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
}
|
||||
#if CLIENT
|
||||
UpdateConvexHulls();
|
||||
@@ -678,7 +669,7 @@ namespace Barotrauma.Items.Components
|
||||
convexHull2?.Remove();
|
||||
#endif
|
||||
|
||||
_doorDict.TryRemove(this, out _);
|
||||
doorList.Remove(this);
|
||||
}
|
||||
|
||||
private bool CheckSubmarinesInDoorWay()
|
||||
@@ -794,19 +785,13 @@ namespace Barotrauma.Items.Components
|
||||
//immediately teleport it to the correct side
|
||||
if (Math.Sign(diff) != dir)
|
||||
{
|
||||
// Defer physics operation if in parallel context (Farseer is not thread-safe)
|
||||
var capturedBody = body;
|
||||
if (IsHorizontal)
|
||||
{
|
||||
Vector2 newPos = new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * doorRectSimSize.Y * 2.0f);
|
||||
float rotation = body.Rotation;
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedBody.SetTransformIgnoreContacts(newPos, rotation));
|
||||
body.SetTransformIgnoreContacts(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * doorRectSimSize.Y * 2.0f), body.Rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 newPos = new Vector2(item.SimPosition.X + dir * doorRectSimSize.X * 1.2f, body.SimPosition.Y);
|
||||
float rotation = body.Rotation;
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedBody.SetTransformIgnoreContacts(newPos, rotation));
|
||||
body.SetTransformIgnoreContacts(new Vector2(item.SimPosition.X + dir * doorRectSimSize.X * 1.2f, body.SimPosition.Y), body.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -11,8 +10,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ElectricalDischarger : Powered, IServerSerializable
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ElectricalDischarger, byte> _dischargerDict = new ConcurrentDictionary<ElectricalDischarger, byte>();
|
||||
public static IEnumerable<ElectricalDischarger> List => _dischargerDict.Keys;
|
||||
private static readonly List<ElectricalDischarger> list = new List<ElectricalDischarger>();
|
||||
public static IEnumerable<ElectricalDischarger> List
|
||||
{
|
||||
get { return list; }
|
||||
}
|
||||
|
||||
const int MaxNodes = 100;
|
||||
const float MaxNodeDistance = 150.0f;
|
||||
@@ -113,7 +115,7 @@ namespace Barotrauma.Items.Components
|
||||
public ElectricalDischarger(Item item, ContentXElement element) :
|
||||
base(item, element)
|
||||
{
|
||||
_dischargerDict.TryAdd(this, 0);
|
||||
list.Add(this);
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
@@ -602,7 +604,7 @@ namespace Barotrauma.Items.Components
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
_dischargerDict.TryRemove(this, out _);
|
||||
list.Remove(this);
|
||||
}
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
|
||||
@@ -9,7 +9,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -486,10 +485,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate target position
|
||||
Vector2 targetPos;
|
||||
Submarine forceSubmarine = picker.Submarine;
|
||||
|
||||
item.body.ResetDynamics();
|
||||
Limb heldHand, arm;
|
||||
if (picker.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand))
|
||||
{
|
||||
@@ -507,42 +503,17 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 diff = new Vector2(
|
||||
(heldHand.SimPosition.X - arm.SimPosition.X) / 2f,
|
||||
(heldHand.SimPosition.Y - arm.SimPosition.Y) / 2.5f);
|
||||
targetPos = heldHand.SimPosition + diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetPos = picker.SimPosition;
|
||||
}
|
||||
|
||||
// Defer physics operations if in parallel context
|
||||
if (PhysicsBodyQueue.IsInParallelContext)
|
||||
{
|
||||
var capturedBody = item.body;
|
||||
var capturedItem = item;
|
||||
var capturedTargetPos = targetPos;
|
||||
var capturedForceSubmarine = forceSubmarine;
|
||||
|
||||
PhysicsBodyQueue.Enqueue(() =>
|
||||
{
|
||||
if (capturedBody.Removed || capturedItem.Removed) { return; }
|
||||
capturedBody.ResetDynamics();
|
||||
//we have forced the item to be in the same sub as the dropper above,
|
||||
//and are placing it to the position of the hands in "local" coordinates
|
||||
//which may be outside the sub if the character is e.g. standing half-way through the airlock
|
||||
// -> let's use the forceSubmarine argument ensure the item is still considered to be in the sub's coordinate space,
|
||||
// or it will end up in a weird state and seemingly disappear
|
||||
capturedItem.SetTransform(capturedTargetPos, 0.0f, forceSubmarine: capturedForceSubmarine);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
item.body.ResetDynamics();
|
||||
//we have forced the item to be in the same sub as the dropper above,
|
||||
//and are placing it to the position of the hands in "local" coordinates
|
||||
//which may be outside the sub if the character is e.g. standing half-way through the airlock
|
||||
// -> let's use the forceSubmarine argument ensure the item is still considered to be in the sub's coordinate space,
|
||||
// or it will end up in a weird state and seemingly disappear
|
||||
item.SetTransform(targetPos, 0.0f, forceSubmarine: forceSubmarine);
|
||||
item.SetTransform(heldHand.SimPosition + diff, 0.0f, forceSubmarine: picker.Submarine);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SetTransform(picker.SimPosition, 0.0f, forceSubmarine: picker.Submarine);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,13 +616,12 @@ namespace Barotrauma.Items.Components
|
||||
return CanBeAttached(user, out _);
|
||||
}
|
||||
|
||||
private static readonly ThreadLocal<List<Item>> tempOverlappingItems = new ThreadLocal<List<Item>>(() => new List<Item>());
|
||||
private static List<Item> tempOverlappingItems = new List<Item>();
|
||||
|
||||
private bool CanBeAttached(Character user, out IEnumerable<Item> overlappingItems)
|
||||
{
|
||||
var overlapping = tempOverlappingItems.Value;
|
||||
overlapping.Clear();
|
||||
overlappingItems = overlapping;
|
||||
tempOverlappingItems.Clear();
|
||||
overlappingItems = tempOverlappingItems;
|
||||
if (!attachable || !Reattachable) { return false; }
|
||||
|
||||
//can be attached anywhere in sub editor
|
||||
@@ -694,9 +664,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (attachPos.X + size.X < worldRect.X || attachPos.X - size.X > worldRect.Right) { continue; }
|
||||
if (attachPos.Y - size.Y > worldRect.Y || attachPos.Y + size.Y < worldRect.Y - worldRect.Height) { continue; }
|
||||
overlapping.Add(otherItem);
|
||||
tempOverlappingItems.Add(otherItem);
|
||||
}
|
||||
if (overlapping.Any()) { return false; }
|
||||
if (tempOverlappingItems.Any()) { return false; }
|
||||
}
|
||||
|
||||
//can be attached anywhere inside hulls
|
||||
|
||||
+5
-30
@@ -13,12 +13,6 @@ namespace Barotrauma.Items.Components
|
||||
private Holdable holdable;
|
||||
|
||||
private float deattachTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Flag to prevent multiple queued creation requests.
|
||||
/// Uses volatile to ensure visibility across threads.
|
||||
/// </summary>
|
||||
private volatile bool triggerBodyCreationQueued;
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "How long it takes to deattach the item from the level walls (in seconds).")]
|
||||
public float DeattachDuration
|
||||
@@ -92,16 +86,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (trigger != null && amount.LengthSquared() > 0.00001f)
|
||||
{
|
||||
// Defer physics operation if in parallel context (Farseer is not thread-safe)
|
||||
var capturedTrigger = trigger;
|
||||
var capturedPos = item.SimPosition;
|
||||
if (ignoreContacts)
|
||||
{
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedTrigger.SetTransformIgnoreContacts(capturedPos, 0.0f));
|
||||
trigger.SetTransformIgnoreContacts(item.SimPosition, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedTrigger.SetTransform(capturedPos, 0.0f));
|
||||
trigger.SetTransform(item.SimPosition, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,29 +109,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trigger == null && !triggerBodyCreationQueued)
|
||||
if (trigger == null)
|
||||
{
|
||||
// Queue the physics body creation to be processed on the main thread.
|
||||
// This is necessary because physics body creation is not thread-safe
|
||||
// and Update() may be called from a parallel loop.
|
||||
triggerBodyCreationQueued = true;
|
||||
PhysicsBodyQueue.EnqueueCreation(() =>
|
||||
{
|
||||
// Double-check that trigger hasn't been created yet
|
||||
// (in case this was called multiple times before queue processing)
|
||||
if (trigger == null && !item.Removed)
|
||||
{
|
||||
CreateTriggerBody();
|
||||
}
|
||||
triggerBodyCreationQueued = false;
|
||||
});
|
||||
CreateTriggerBody();
|
||||
}
|
||||
if (trigger != null && Vector2.DistanceSquared(item.SimPosition, trigger.SimPosition) > 0.01f)
|
||||
{
|
||||
// Defer physics operation if in parallel context (Farseer is not thread-safe)
|
||||
var capturedTrigger = trigger;
|
||||
var capturedPos = item.SimPosition;
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedTrigger.SetTransform(capturedPos, 0.0f));
|
||||
trigger.SetTransform(item.SimPosition, 0.0f);
|
||||
}
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
@@ -25,7 +24,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private readonly HashSet<Entity> hitTargets = new HashSet<Entity>();
|
||||
|
||||
private readonly ConcurrentQueue<Fixture> impactQueue = new ConcurrentQueue<Fixture>();
|
||||
private readonly Queue<Fixture> impactQueue = new Queue<Fixture>();
|
||||
|
||||
public Character User { get; private set; }
|
||||
|
||||
@@ -191,16 +190,17 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!item.body.Enabled)
|
||||
{
|
||||
while (impactQueue.TryDequeue(out _)) { } // Clear queue
|
||||
impactQueue.Clear();
|
||||
return;
|
||||
}
|
||||
if (picker == null || !picker.HeldItems.Contains(item))
|
||||
{
|
||||
while (impactQueue.TryDequeue(out _)) { } // Clear queue
|
||||
impactQueue.Clear();
|
||||
IsActive = false;
|
||||
}
|
||||
while (impactQueue.TryDequeue(out var impact))
|
||||
while (impactQueue.Count > 0)
|
||||
{
|
||||
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()
|
||||
{
|
||||
while (impactQueue.TryDequeue(out _)) { } // Clear queue
|
||||
impactQueue.Clear();
|
||||
item.body.FarseerBody.OnCollision -= OnCollision;
|
||||
item.body.CollisionCategories = Physics.CollisionItem;
|
||||
item.body.CollidesWith = Physics.DefaultItemCollidesWith;
|
||||
|
||||
@@ -4,7 +4,6 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
|
||||
@@ -316,7 +315,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UseProjSpecific(float deltaTime, Vector2 raystart);
|
||||
|
||||
private static readonly ThreadLocal<List<Body>> hitBodies = new ThreadLocal<List<Body>>(() => new List<Body>());
|
||||
private static readonly List<Body> hitBodies = new List<Body>();
|
||||
private readonly HashSet<Character> hitCharacters = new HashSet<Character>();
|
||||
private readonly List<FireSource> fireSourcesInRange = new List<FireSource>();
|
||||
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies)
|
||||
@@ -374,13 +373,13 @@ namespace Barotrauma.Items.Components
|
||||
},
|
||||
allowInsideFixture: true);
|
||||
|
||||
hitBodies.Value.Clear();
|
||||
hitBodies.Value.AddRange(bodies.Distinct());
|
||||
hitBodies.Clear();
|
||||
hitBodies.AddRange(bodies.Distinct());
|
||||
|
||||
lastPickedFraction = Submarine.LastPickedFraction;
|
||||
Type lastHitType = null;
|
||||
hitCharacters.Clear();
|
||||
foreach (Body body in hitBodies.Value)
|
||||
foreach (Body body in hitBodies)
|
||||
{
|
||||
Type bodyType = body.UserData?.GetType();
|
||||
if (!RepairThroughWalls && bodyType != null && bodyType != lastHitType)
|
||||
@@ -898,49 +897,48 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ThreadLocal<List<ISerializableEntity>> currentTargets = new ThreadLocal<List<ISerializableEntity>>(() => new List<ISerializableEntity>());
|
||||
private static List<ISerializableEntity> currentTargets = new List<ISerializableEntity>();
|
||||
private void ApplyStatusEffectsOnTarget(Character user, float deltaTime, ActionType actionType, Item targetItem = null, Character character = null, Limb limb = null, Structure structure = null)
|
||||
{
|
||||
if (statusEffectLists == null) { return; }
|
||||
if (!statusEffectLists.TryGetValue(actionType, out List<StatusEffect> statusEffects)) { return; }
|
||||
|
||||
var targets = currentTargets.Value;
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
targets.Clear();
|
||||
currentTargets.Clear();
|
||||
effect.SetUser(user);
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
||||
{
|
||||
if (targetItem != null)
|
||||
{
|
||||
targets.AddRange(targetItem.AllPropertyObjects);
|
||||
currentTargets.AddRange(targetItem.AllPropertyObjects);
|
||||
}
|
||||
if (structure != null)
|
||||
{
|
||||
targets.Add(structure);
|
||||
currentTargets.Add(structure);
|
||||
}
|
||||
if (character != null)
|
||||
{
|
||||
targets.Add(character);
|
||||
currentTargets.Add(character);
|
||||
}
|
||||
effect.Apply(actionType, deltaTime, item, targets);
|
||||
effect.Apply(actionType, deltaTime, item, currentTargets);
|
||||
}
|
||||
else if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
targets.Add(user);
|
||||
effect.Apply(actionType, deltaTime, item, targets);
|
||||
currentTargets.Add(user);
|
||||
effect.Apply(actionType, deltaTime, item, currentTargets);
|
||||
}
|
||||
else if (effect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||
{
|
||||
targets.Add(limb);
|
||||
effect.Apply(actionType, deltaTime, item, targets);
|
||||
currentTargets.Add(limb);
|
||||
effect.Apply(actionType, deltaTime, item, currentTargets);
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (user == null) { return; }
|
||||
// Hard-coded progress bars for welding doors stuck.
|
||||
// A general purpose system could be better, but it would most likely require changes in the way we define the status effects in xml.
|
||||
foreach (ISerializableEntity target in targets)
|
||||
foreach (ISerializableEntity target in currentTargets)
|
||||
{
|
||||
if (target is not Door door) { continue; }
|
||||
if (!door.CanBeWelded || !door.Item.IsInteractable(user)) { continue; }
|
||||
|
||||
@@ -949,8 +949,7 @@ namespace Barotrauma.Items.Components
|
||||
//if any of the effects reduce the item's condition, set the user for OnBroken effects as well
|
||||
if (reducesCondition && user != null && type != ActionType.OnBroken)
|
||||
{
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (ItemComponent ic in item.Components.ToArray())
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic.statusEffectLists == null || !ic.statusEffectLists.TryGetValue(ActionType.OnBroken, out List<StatusEffect> brokenEffects)) { continue; }
|
||||
foreach (var brokenEffect in brokenEffects)
|
||||
|
||||
@@ -884,8 +884,7 @@ namespace Barotrauma.Items.Components
|
||||
RelatedItem containableItem = FindContainableItem(containedItem);
|
||||
if (containableItem != null && containableItem.SetActive)
|
||||
{
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (var ic in containedItem.Components.ToArray())
|
||||
foreach (var ic in containedItem.Components)
|
||||
{
|
||||
ic.IsActive = active;
|
||||
}
|
||||
@@ -1011,11 +1010,7 @@ namespace Barotrauma.Items.Components
|
||||
if (flippedX ^ flippedY) { rotation = -rotation; }
|
||||
rotation += -item.RotationRad;
|
||||
}
|
||||
// Defer physics operation if in parallel context (Farseer is not thread-safe)
|
||||
var capturedBody = contained.Item.body.FarseerBody;
|
||||
var capturedSimPos = simPos;
|
||||
var capturedRotation = rotation;
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedBody.SetTransformIgnoreContacts(ref capturedSimPos, capturedRotation));
|
||||
contained.Item.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, rotation);
|
||||
contained.Item.body.UpdateDrawPosition(interpolate: false);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Ladder : ItemComponent
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Ladder, byte> _ladderDict = new ConcurrentDictionary<Ladder, byte>();
|
||||
public static IEnumerable<Ladder> List => _ladderDict.Keys;
|
||||
public static List<Ladder> List { get; } = new List<Ladder>();
|
||||
|
||||
public Ladder(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
InitProjSpecific(element);
|
||||
_ladderDict.TryAdd(this, 0);
|
||||
List.Add(this);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
@@ -30,7 +28,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
RemoveProjSpecific();
|
||||
_ladderDict.TryRemove(this, out _);
|
||||
List.Remove(this);
|
||||
}
|
||||
|
||||
partial void RemoveProjSpecific();
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -498,14 +497,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.SendSignal(new Signal(MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture), sender: user), positionOut);
|
||||
|
||||
// Use ToList() snapshot for thread-safe iteration
|
||||
var signalRecipients = item.LastSentSignalRecipients.ToList();
|
||||
for (int i = signalRecipients.Count - 1; i >= 0; i--)
|
||||
for (int i = item.LastSentSignalRecipients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (signalRecipients[i].Item.Condition <= 0.0f || signalRecipients[i].IsPower) { continue; }
|
||||
if (signalRecipients[i].Item.Prefab.FocusOnSelected)
|
||||
if (item.LastSentSignalRecipients[i].Item.Condition <= 0.0f || item.LastSentSignalRecipients[i].IsPower) { continue; }
|
||||
if (item.LastSentSignalRecipients[i].Item.Prefab.FocusOnSelected)
|
||||
{
|
||||
return signalRecipients[i].Item;
|
||||
return item.LastSentSignalRecipients[i].Item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Sonar : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Sonar, byte> _sonarDict = new ConcurrentDictionary<Sonar, byte>();
|
||||
public static IEnumerable<Sonar> SonarList => _sonarDict.Keys;
|
||||
public static List<Sonar> SonarList = new List<Sonar>();
|
||||
|
||||
public enum Mode
|
||||
{
|
||||
@@ -172,7 +169,7 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
InitProjSpecific(element);
|
||||
CurrentMode = Mode.Passive;
|
||||
_sonarDict.TryAdd(this, 0);
|
||||
SonarList.Add(this);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
@@ -294,15 +291,13 @@ namespace Barotrauma.Items.Components
|
||||
return currentPingIndex != -1 && (character == null || characterUsable);
|
||||
}
|
||||
|
||||
private static readonly ThreadLocal<Dictionary<string, List<Character>>> targetGroups =
|
||||
new ThreadLocal<Dictionary<string, List<Character>>>(() => new Dictionary<string, List<Character>>());
|
||||
private static readonly Dictionary<string, List<Character>> targetGroups = new Dictionary<string, List<Character>>();
|
||||
|
||||
public override bool CrewAIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (currentMode == Mode.Passive || !aiPingCheckPending) { return false; }
|
||||
|
||||
var groups = targetGroups.Value;
|
||||
foreach (List<Character> targetGroup in groups.Values)
|
||||
foreach (List<Character> targetGroup in targetGroups.Values)
|
||||
{
|
||||
targetGroup.Clear();
|
||||
}
|
||||
@@ -315,14 +310,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
#warning This is not the best key for a dictionary.
|
||||
string directionName = GetDirectionName(c.WorldPosition - item.WorldPosition).Value;
|
||||
if (!groups.ContainsKey(directionName))
|
||||
if (!targetGroups.ContainsKey(directionName))
|
||||
{
|
||||
groups.Add(directionName, new List<Character>());
|
||||
targetGroups.Add(directionName, new List<Character>());
|
||||
}
|
||||
groups[directionName].Add(c);
|
||||
targetGroups[directionName].Add(c);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, List<Character>> targetGroup in groups)
|
||||
foreach (KeyValuePair<string, List<Character>> targetGroup in targetGroups)
|
||||
{
|
||||
if (!targetGroup.Value.Any()) { continue; }
|
||||
string dialogTag = "DialogSonarTarget";
|
||||
@@ -406,7 +401,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
MineralClusters = null;
|
||||
#endif
|
||||
_sonarDict.TryRemove(this, out _);
|
||||
SonarList.Remove(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -637,7 +637,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (pathFinder == null)
|
||||
{
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList.ToList(), false)
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, false)
|
||||
{
|
||||
GetNodePenalty = GetNodePenalty
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -14,12 +13,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private readonly HashSet<Connection> signalConnections = new HashSet<Connection>();
|
||||
|
||||
private readonly ConcurrentDictionary<Connection, bool> connectionDirty = new ConcurrentDictionary<Connection, bool>();
|
||||
private readonly Dictionary<Connection, bool> connectionDirty = new Dictionary<Connection, bool>();
|
||||
|
||||
//a list of connections a given connection is connected to, either directly or via other power transfer components
|
||||
//Uses ConcurrentDictionary<Connection, byte> as a thread-safe HashSet replacement
|
||||
private readonly ConcurrentDictionary<Connection, ConcurrentDictionary<Connection, byte>> connectedRecipients =
|
||||
new ConcurrentDictionary<Connection, ConcurrentDictionary<Connection, byte>>();
|
||||
private readonly Dictionary<Connection, HashSet<Connection>> connectedRecipients = new Dictionary<Connection, HashSet<Connection>>();
|
||||
|
||||
private float overloadCooldownTimer;
|
||||
private const float OverloadCooldown = 5.0f;
|
||||
@@ -135,7 +132,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void InitProjectSpecific(XElement element);
|
||||
|
||||
private static readonly System.Collections.Concurrent.ConcurrentDictionary<PowerTransfer, byte> _recipientsToRefresh = new System.Collections.Concurrent.ConcurrentDictionary<PowerTransfer, byte>();
|
||||
private static readonly HashSet<PowerTransfer> recipientsToRefresh = new HashSet<PowerTransfer>();
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
@@ -147,21 +144,20 @@ namespace Barotrauma.Items.Components
|
||||
powerLoad = 0.0f;
|
||||
currPowerConsumption = 0.0f;
|
||||
SetAllConnectionsDirty();
|
||||
_recipientsToRefresh.Clear();
|
||||
// Take snapshot for thread-safe iteration (no locks needed with ConcurrentDictionary)
|
||||
foreach (var recipientDict in connectedRecipients.Values)
|
||||
recipientsToRefresh.Clear();
|
||||
foreach (HashSet<Connection> recipientList in connectedRecipients.Values)
|
||||
{
|
||||
foreach (Connection c in recipientDict.Keys)
|
||||
foreach (Connection c in recipientList)
|
||||
{
|
||||
if (c.Item == item) { continue; }
|
||||
var recipientPowerTransfer = c.Item.GetComponent<PowerTransfer>();
|
||||
if (recipientPowerTransfer != null)
|
||||
{
|
||||
_recipientsToRefresh.TryAdd(recipientPowerTransfer, 0);
|
||||
recipientsToRefresh.Add(recipientPowerTransfer);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (PowerTransfer recipientPowerTransfer in _recipientsToRefresh.Keys)
|
||||
foreach (PowerTransfer recipientPowerTransfer in recipientsToRefresh)
|
||||
{
|
||||
recipientPowerTransfer.SetAllConnectionsDirty();
|
||||
recipientPowerTransfer.RefreshConnections();
|
||||
@@ -308,56 +304,58 @@ namespace Barotrauma.Items.Components
|
||||
protected void RefreshConnections()
|
||||
{
|
||||
var connections = item.Connections;
|
||||
if (connections == null) { return; }
|
||||
|
||||
// Take a snapshot of connections for thread-safe iteration
|
||||
var connectionSnapshot = connections.ToList();
|
||||
foreach (Connection c in connectionSnapshot)
|
||||
foreach (Connection c in connections)
|
||||
{
|
||||
if (!connectionDirty.TryGetValue(c, out bool isDirty))
|
||||
if (!connectionDirty.ContainsKey(c))
|
||||
{
|
||||
connectionDirty[c] = true;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
if (!isDirty)
|
||||
else if (!connectionDirty[c])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//find all connections that are connected to this one (directly or via another PowerTransfer)
|
||||
var tempConnected = connectedRecipients.GetOrAdd(c, _ => new ConcurrentDictionary<Connection, byte>());
|
||||
|
||||
// Get previous recipients and clear
|
||||
var previousRecipients = tempConnected.Keys.ToList();
|
||||
tempConnected.Clear();
|
||||
|
||||
//mark all previous recipients as dirty
|
||||
foreach (Connection recipient in previousRecipients)
|
||||
HashSet<Connection> tempConnected;
|
||||
if (!connectedRecipients.ContainsKey(c))
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) { pt.connectionDirty[recipient] = true; }
|
||||
tempConnected = new HashSet<Connection>();
|
||||
connectedRecipients.Add(c, tempConnected);
|
||||
}
|
||||
else
|
||||
{
|
||||
tempConnected = connectedRecipients[c];
|
||||
tempConnected.Clear();
|
||||
//mark all previous recipients as dirty
|
||||
foreach (Connection recipient in tempConnected)
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) { pt.connectionDirty[recipient] = true; }
|
||||
}
|
||||
}
|
||||
|
||||
tempConnected.TryAdd(c, 0);
|
||||
tempConnected.Add(c);
|
||||
if (item.Condition > 0.0f)
|
||||
{
|
||||
GetConnected(c, tempConnected);
|
||||
//go through all the PowerTransfers that we're connected to and set their connections to match the ones we just calculated
|
||||
//(no need to go through the recursive GetConnected method again)
|
||||
// Take snapshot for thread-safe iteration (no locks needed)
|
||||
var tempConnectedSnapshot = tempConnected.Keys.ToList();
|
||||
foreach (Connection recipient in tempConnectedSnapshot)
|
||||
foreach (Connection recipient in tempConnected)
|
||||
{
|
||||
if (recipient == c) { continue; }
|
||||
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (recipientPowerTransfer == null) { continue; }
|
||||
|
||||
var recipientSet = recipientPowerTransfer.connectedRecipients.GetOrAdd(recipient, _ => new ConcurrentDictionary<Connection, byte>());
|
||||
recipientSet.Clear();
|
||||
foreach (var connection in tempConnectedSnapshot)
|
||||
if (!recipientPowerTransfer.connectedRecipients.ContainsKey(recipient))
|
||||
{
|
||||
recipientSet.TryAdd(connection, 0);
|
||||
recipientPowerTransfer.connectedRecipients.Add(recipient, new HashSet<Connection>());
|
||||
}
|
||||
else
|
||||
{
|
||||
recipientPowerTransfer.connectedRecipients[recipient].Clear();
|
||||
}
|
||||
foreach (var connection in tempConnected)
|
||||
{
|
||||
recipientPowerTransfer.connectedRecipients[recipient].Add(connection);
|
||||
}
|
||||
recipientPowerTransfer.connectionDirty[recipient] = false;
|
||||
}
|
||||
@@ -366,20 +364,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
//Finds all the connections that can receive a signal sent into the given connection and stores them in the concurrent dictionary.
|
||||
private void GetConnected(Connection c, ConcurrentDictionary<Connection, byte> connected)
|
||||
//Finds all the connections that can receive a signal sent into the given connection and stores them in the hashset.
|
||||
private void GetConnected(Connection c, HashSet<Connection> connected)
|
||||
{
|
||||
// Take snapshot for thread-safe iteration
|
||||
var recipients = c.Recipients.ToList();
|
||||
var recipients = c.Recipients;
|
||||
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (recipient == null || connected.ContainsKey(recipient)) { continue; }
|
||||
if (recipient == null || connected.Contains(recipient)) { continue; }
|
||||
|
||||
Item it = recipient.Item;
|
||||
if (it == null || it.Condition <= 0.0f) { continue; }
|
||||
|
||||
connected.TryAdd(recipient, 0);
|
||||
connected.Add(recipient);
|
||||
|
||||
var powerTransfer = it.GetComponent<PowerTransfer>();
|
||||
if (powerTransfer != null && powerTransfer.CanTransfer && powerTransfer.IsActive)
|
||||
@@ -397,14 +394,10 @@ namespace Barotrauma.Items.Components
|
||||
connectionDirty[c] = true;
|
||||
if (c.IsPower)
|
||||
{
|
||||
MarkConnectionChanged(c);
|
||||
ChangedConnections.Add(c);
|
||||
if (connectedRecipients.TryGetValue(c, out var recipients))
|
||||
{
|
||||
// No lock needed - ConcurrentDictionary.Keys is thread-safe
|
||||
foreach (var conn in recipients.Keys.Where(conn => conn.IsPower))
|
||||
{
|
||||
MarkConnectionChanged(conn);
|
||||
}
|
||||
recipients.Where(c => c.IsPower).ForEach(c => ChangedConnections.Add(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -417,14 +410,10 @@ namespace Barotrauma.Items.Components
|
||||
connectionDirty[connection] = true;
|
||||
if (connection.IsPower)
|
||||
{
|
||||
MarkConnectionChanged(connection);
|
||||
ChangedConnections.Add(connection);
|
||||
if (connectedRecipients.TryGetValue(connection, out var recipients))
|
||||
{
|
||||
// No lock needed - ConcurrentDictionary.Keys is thread-safe
|
||||
foreach (var conn in recipients.Keys.Where(conn => conn.IsPower))
|
||||
{
|
||||
MarkConnectionChanged(conn);
|
||||
}
|
||||
recipients.Where(c => c.IsPower).ForEach(c => ChangedConnections.Add(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,19 +452,16 @@ namespace Barotrauma.Items.Components
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
if (item.Condition <= 0.0f || connection.IsPower) { return; }
|
||||
if (!connectedRecipients.TryGetValue(connection, out var recipients)) { return; }
|
||||
if (!connectedRecipients.ContainsKey(connection)) { return; }
|
||||
if (!signalConnections.Contains(connection)) { return; }
|
||||
|
||||
// No lock needed - ConcurrentDictionary.Keys is thread-safe
|
||||
// Use ToList() snapshot for thread-safe iteration
|
||||
foreach (Connection recipient in recipients.Keys.ToList())
|
||||
foreach (Connection recipient in connectedRecipients[connection])
|
||||
{
|
||||
if (recipient.Item == item || recipient.Item == signal.source) { continue; }
|
||||
|
||||
signal.source?.LastSentSignalRecipients.Add(recipient);
|
||||
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (ItemComponent ic in recipient.Item.Components.ToArray())
|
||||
foreach (ItemComponent ic in recipient.Item.Components)
|
||||
{
|
||||
//other junction boxes don't need to receive the signal in the pass-through signal connections
|
||||
//because we relay it straight to the connected items without going through the whole chain of junction boxes
|
||||
@@ -485,8 +471,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (recipient.Effects != null && signal.value != "0" && !string.IsNullOrEmpty(signal.value))
|
||||
{
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (StatusEffect effect in recipient.Effects.ToArray())
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
|
||||
}
|
||||
@@ -499,7 +484,7 @@ namespace Barotrauma.Items.Components
|
||||
base.RemoveComponentSpecific();
|
||||
connectedRecipients?.Clear();
|
||||
connectionDirty?.Clear();
|
||||
_recipientsToRefresh.Clear();
|
||||
recipientsToRefresh.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -64,77 +62,17 @@ namespace Barotrauma.Items.Components
|
||||
protected const float UpdateInterval = (float)Timing.Step;
|
||||
|
||||
/// <summary>
|
||||
/// List of all powered ItemComponents (thread-safe)
|
||||
/// List of all powered ItemComponents
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Powered, byte> _poweredDict = new ConcurrentDictionary<Powered, byte>();
|
||||
|
||||
/// <summary>
|
||||
/// Cached list for iteration - updated when collection changes
|
||||
/// </summary>
|
||||
private static volatile List<Powered> _cachedPoweredList;
|
||||
private static int _poweredListVersion;
|
||||
|
||||
private static readonly List<Powered> poweredList = new List<Powered>();
|
||||
public static IEnumerable<Powered> PoweredList
|
||||
{
|
||||
get
|
||||
{
|
||||
var cached = _cachedPoweredList;
|
||||
if (cached != null) return cached;
|
||||
return GetCachedPoweredList();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Powered> GetCachedPoweredList()
|
||||
{
|
||||
var newList = _poweredDict.Keys.ToList();
|
||||
_cachedPoweredList = newList;
|
||||
return newList;
|
||||
}
|
||||
|
||||
private static void InvalidatePoweredListCache()
|
||||
{
|
||||
_cachedPoweredList = null;
|
||||
Interlocked.Increment(ref _poweredListVersion);
|
||||
get { return poweredList; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thread-safe set of changed connections
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Connection, byte> _changedConnections = new ConcurrentDictionary<Connection, byte>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all changed connections (snapshot)
|
||||
/// </summary>
|
||||
public static ICollection<Connection> ChangedConnections => _changedConnections.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// Add a connection to the changed set
|
||||
/// </summary>
|
||||
public static void MarkConnectionChanged(Connection c)
|
||||
{
|
||||
_changedConnections.TryAdd(c, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all changed connections
|
||||
/// </summary>
|
||||
public static void ClearChangedConnections()
|
||||
{
|
||||
_changedConnections.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a connection from the changed set
|
||||
/// </summary>
|
||||
public static void UnmarkConnectionChanged(Connection c)
|
||||
{
|
||||
_changedConnections.TryRemove(c, out _);
|
||||
}
|
||||
public static readonly HashSet<Connection> ChangedConnections = new HashSet<Connection>();
|
||||
|
||||
/// <summary>
|
||||
/// Thread-safe grid dictionary
|
||||
/// </summary>
|
||||
public readonly static ConcurrentDictionary<int, GridInfo> Grids = new ConcurrentDictionary<int, GridInfo>();
|
||||
public readonly static Dictionary<int, GridInfo> Grids = new Dictionary<int, GridInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// The amount of power currently consumed by the item. Negative values mean that the item is providing power to connected items
|
||||
@@ -271,8 +209,7 @@ namespace Barotrauma.Items.Components
|
||||
public Powered(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
_poweredDict.TryAdd(this, 0);
|
||||
InvalidatePoweredListCache();
|
||||
poweredList.Add(this);
|
||||
InitProjectSpecific(element);
|
||||
}
|
||||
|
||||
@@ -385,20 +322,17 @@ namespace Barotrauma.Items.Components
|
||||
//don't use cache if there are no existing grids
|
||||
if (Grids.Count > 0 && useCache)
|
||||
{
|
||||
// Take a snapshot of changed connections for iteration
|
||||
var changedSnapshot = ChangedConnections.ToList();
|
||||
|
||||
//delete all grids that were affected
|
||||
foreach (Connection c in changedSnapshot)
|
||||
foreach (Connection c in ChangedConnections)
|
||||
{
|
||||
if (c.Grid != null)
|
||||
{
|
||||
Grids.TryRemove(c.Grid.ID, out _);
|
||||
Grids.Remove(c.Grid.ID);
|
||||
c.Grid = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Connection c in changedSnapshot)
|
||||
foreach (Connection c in ChangedConnections)
|
||||
{
|
||||
//Make sure the connection grid hasn't been resolved by another connection update
|
||||
//Ensure the connection has other connections
|
||||
@@ -412,7 +346,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
//Clear all grid IDs from connections
|
||||
foreach (Powered powered in PoweredList)
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
//Only check devices with connectors
|
||||
if (powered.powerIn != null)
|
||||
@@ -427,7 +361,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Grids.Clear();
|
||||
|
||||
foreach (Powered powered in PoweredList)
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered.Item.Condition <= 0f) { continue; }
|
||||
|
||||
@@ -458,7 +392,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//Clear changed connections after each update
|
||||
ClearChangedConnections();
|
||||
ChangedConnections.Clear();
|
||||
}
|
||||
|
||||
private static GridInfo PropagateGrid(Connection conn)
|
||||
@@ -488,8 +422,8 @@ namespace Barotrauma.Items.Components
|
||||
c.Grid = grid;
|
||||
grid.AddConnection(c);
|
||||
|
||||
//Add on recipients - use ToList() snapshot for thread-safe iteration
|
||||
foreach (Connection otherC in c.Recipients.ToList())
|
||||
//Add on recipients
|
||||
foreach (Connection otherC in c.Recipients)
|
||||
{
|
||||
//Only add valid connections
|
||||
if (otherC.Grid != grid && (otherC.Grid == null || !Grids.ContainsKey(otherC.Grid.ID)) && ValidPowerConnection(c, otherC))
|
||||
@@ -560,7 +494,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//Determine if devices are adding a load or providing power, also resolve solo nodes
|
||||
foreach (Powered powered in PoweredList)
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
//Make voltage decay to ensure the device powers down.
|
||||
//This only effects devices with no power input (whose voltage is set by other means, e.g. status effects from a contained battery)
|
||||
@@ -796,8 +730,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (item.Connections != null && powerIn != null)
|
||||
{
|
||||
// Use ToList() snapshot for thread-safe iteration
|
||||
foreach (Connection recipient in powerIn.Recipients.ToList())
|
||||
foreach (Connection recipient in powerIn.Recipients)
|
||||
{
|
||||
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
|
||||
if (recipient.Item?.GetComponent<PowerContainer>() is PowerContainer battery)
|
||||
@@ -817,14 +750,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (c.IsPower && c.Grid != null)
|
||||
{
|
||||
MarkConnectionChanged(c);
|
||||
ChangedConnections.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.RemoveComponentSpecific();
|
||||
_poweredDict.TryRemove(this, out _);
|
||||
InvalidatePoweredListCache();
|
||||
poweredList.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,9 +780,9 @@ namespace Barotrauma.Items.Components
|
||||
Connections.Remove(c);
|
||||
|
||||
//Remove the grid if it has no devices
|
||||
if (Connections.Count == 0)
|
||||
if (Connections.Count == 0 && Powered.Grids.ContainsKey(ID))
|
||||
{
|
||||
Powered.Grids.TryRemove(ID, out _);
|
||||
Powered.Grids.Remove(ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ 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;
|
||||
@@ -73,7 +72,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public const float WaterDragCoefficient = 0.1f;
|
||||
|
||||
private readonly ConcurrentQueue<Impact> impactQueue = new ConcurrentQueue<Impact>();
|
||||
private readonly Queue<Impact> impactQueue = new Queue<Impact>();
|
||||
|
||||
private bool removePending;
|
||||
|
||||
@@ -841,8 +840,9 @@ namespace Barotrauma.Items.Components
|
||||
DisableProjectileCollisions();
|
||||
}
|
||||
}
|
||||
while (impactQueue.TryDequeue(out var impact))
|
||||
while (impactQueue.Count > 0)
|
||||
{
|
||||
var impact = impactQueue.Dequeue();
|
||||
HandleProjectileCollision(impact.Fixture, impact.Normal, impact.LinearVelocity);
|
||||
}
|
||||
|
||||
|
||||
@@ -456,8 +456,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.SendSignal(conditionSignal, "condition_out");
|
||||
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (var component in item.Components.ToArray())
|
||||
foreach (var component in item.Components)
|
||||
{
|
||||
if (component is IDeteriorateUnderStress deteriorateUnderStress)
|
||||
{
|
||||
@@ -714,8 +713,7 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
|
||||
if (LastActiveTime > Timing.TotalTime) { return true; }
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (ItemComponent ic in item.Components.ToArray())
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic is Fabricator || ic is Deconstructor)
|
||||
{
|
||||
@@ -763,8 +761,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float GetDeteriorationDelayMultiplier()
|
||||
{
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (ItemComponent ic in item.Components.ToArray())
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic is Engine engine)
|
||||
{
|
||||
|
||||
@@ -222,14 +222,13 @@ namespace Barotrauma.Items.Components
|
||||
public void SetRecipientsDirty()
|
||||
{
|
||||
recipientsDirty = true;
|
||||
if (IsPower) { Powered.MarkConnectionChanged(this); }
|
||||
if (IsPower) { Powered.ChangedConnections.Add(this); }
|
||||
}
|
||||
|
||||
private void RefreshRecipients()
|
||||
{
|
||||
recipients.Clear();
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (var wire in wires.ToArray())
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
Connection recipient = wire.OtherConnection(this);
|
||||
if (recipient != null) { recipients.Add(recipient); }
|
||||
@@ -268,8 +267,8 @@ namespace Barotrauma.Items.Components
|
||||
//Check if both connections belong to a larger grid
|
||||
if (prevOtherConnection.recipients.Count > 1 && recipients.Count > 1)
|
||||
{
|
||||
Powered.MarkConnectionChanged(prevOtherConnection);
|
||||
Powered.MarkConnectionChanged(this);
|
||||
Powered.ChangedConnections.Add(prevOtherConnection);
|
||||
Powered.ChangedConnections.Add(this);
|
||||
}
|
||||
else if (recipients.Count > 1)
|
||||
{
|
||||
@@ -285,7 +284,7 @@ namespace Barotrauma.Items.Components
|
||||
else if (Grid.Connections.Count == 2)
|
||||
{
|
||||
//Delete the grid as these were the only 2 devices
|
||||
Powered.Grids.TryRemove(Grid.ID, out _);
|
||||
Powered.Grids.Remove(Grid.ID);
|
||||
Grid = null;
|
||||
prevOtherConnection.Grid = null;
|
||||
}
|
||||
@@ -326,8 +325,8 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
//Flag change so that proper grids can be formed
|
||||
Powered.MarkConnectionChanged(this);
|
||||
Powered.MarkConnectionChanged(otherConnection);
|
||||
Powered.ChangedConnections.Add(this);
|
||||
Powered.ChangedConnections.Add(otherConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,8 +339,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
LastSentSignal = signal;
|
||||
enumeratingWires = true;
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (var wire in wires.ToArray())
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
Connection recipient = wire.OtherConnection(this);
|
||||
if (recipient == null) { continue; }
|
||||
@@ -356,14 +354,14 @@ namespace Barotrauma.Items.Components
|
||||
GameMain.LuaCs.Hook.Call("signalReceived." + recipient.item.Prefab.Identifier, signal, recipient);
|
||||
}
|
||||
|
||||
foreach (CircuitBoxConnection connection in CircuitBoxConnections.ToArray())
|
||||
foreach (CircuitBoxConnection connection in CircuitBoxConnections)
|
||||
{
|
||||
connection.ReceiveSignal(signal);
|
||||
GameMain.LuaCs.Hook.Call("signalReceived", signal, connection.Connection);
|
||||
GameMain.LuaCs.Hook.Call("signalReceived." + connection.Connection.Item.Prefab.Identifier, signal, connection);
|
||||
}
|
||||
enumeratingWires = false;
|
||||
foreach (var removedWire in removedWires.ToArray())
|
||||
foreach (var removedWire in removedWires)
|
||||
{
|
||||
wires.Remove(removedWire);
|
||||
}
|
||||
@@ -374,16 +372,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
conn.LastReceivedSignal = signal;
|
||||
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (ItemComponent ic in conn.item.Components.ToArray())
|
||||
foreach (ItemComponent ic in conn.item.Components)
|
||||
{
|
||||
ic.ReceiveSignal(signal, conn);
|
||||
}
|
||||
|
||||
if (conn.Effects == null || signal.value == "0") { return; }
|
||||
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (StatusEffect effect in conn.Effects.ToArray())
|
||||
foreach (StatusEffect effect in conn.Effects)
|
||||
{
|
||||
conn.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
|
||||
}
|
||||
@@ -393,15 +389,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (IsPower && Grid != null)
|
||||
{
|
||||
Powered.MarkConnectionChanged(this);
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (Connection c in recipients.ToArray())
|
||||
Powered.ChangedConnections.Add(this);
|
||||
foreach (Connection c in recipients)
|
||||
{
|
||||
Powered.MarkConnectionChanged(c);
|
||||
Powered.ChangedConnections.Add(c);
|
||||
}
|
||||
}
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (var wire in wires.ToArray())
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
wire.RemoveConnection(this);
|
||||
recipientsDirty = true;
|
||||
@@ -409,7 +403,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (enumeratingWires)
|
||||
{
|
||||
foreach (var wire in wires.ToArray())
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
removedWires.Add(wire);
|
||||
}
|
||||
@@ -452,8 +446,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
XElement newElement = new XElement(IsOutput ? "output" : "input", new XAttribute("name", Name));
|
||||
|
||||
// Use ToArray() snapshot before OrderBy for thread-safe iteration
|
||||
foreach (var wire in wires.ToArray().OrderBy(w => w.Item.ID))
|
||||
foreach (var wire in wires.OrderBy(w => w.Item.ID))
|
||||
{
|
||||
newElement.Add(new XElement("link",
|
||||
new XAttribute("w", wire.Item.ID.ToString()),
|
||||
|
||||
+7
-13
@@ -148,16 +148,14 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 wireNodeOffset = item.Submarine == null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (Wire wire in c.Wires.ToArray())
|
||||
foreach (Wire wire in c.Wires)
|
||||
{
|
||||
if (wire == null) { continue; }
|
||||
TryMoveWire(wire);
|
||||
}
|
||||
}
|
||||
|
||||
// Use ToList() snapshot for thread-safe iteration
|
||||
foreach (var wire in DisconnectedWires.ToList())
|
||||
foreach (var wire in DisconnectedWires)
|
||||
{
|
||||
TryMoveWire(wire);
|
||||
}
|
||||
@@ -389,7 +387,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
foreach (var connection in Connections)
|
||||
{
|
||||
Powered.UnmarkConnectionChanged(connection);
|
||||
Powered.ChangedConnections.Remove(connection);
|
||||
connection.Recipients.Clear();
|
||||
}
|
||||
Connections.Clear();
|
||||
@@ -414,19 +412,15 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteByte((byte)Connections.Count);
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
var wiresSnapshot = connection.Wires.ToArray();
|
||||
msg.WriteVariableUInt32((uint)wiresSnapshot.Length);
|
||||
foreach (Wire wire in wiresSnapshot)
|
||||
msg.WriteVariableUInt32((uint)connection.Wires.Count);
|
||||
foreach (Wire wire in connection.Wires)
|
||||
{
|
||||
msg.WriteUInt16(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
// Use ToList() snapshot for thread-safe iteration
|
||||
var disconnectedSnapshot = DisconnectedWires.ToList();
|
||||
msg.WriteUInt16((ushort)disconnectedSnapshot.Count);
|
||||
foreach (Wire disconnectedWire in disconnectedSnapshot)
|
||||
msg.WriteUInt16((ushort)DisconnectedWires.Count);
|
||||
foreach (Wire disconnectedWire in DisconnectedWires)
|
||||
{
|
||||
msg.WriteUInt16(disconnectedWire.Item.ID);
|
||||
}
|
||||
|
||||
+11
-19
@@ -1,8 +1,6 @@
|
||||
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
|
||||
@@ -27,8 +25,7 @@ namespace Barotrauma.Items.Components
|
||||
private int signalQueueSize;
|
||||
private int delayTicks;
|
||||
|
||||
// Thread-safe queue for concurrent access
|
||||
private readonly ConcurrentQueue<DelayedSignal> signalQueue = new ConcurrentQueue<DelayedSignal>();
|
||||
private readonly Queue<DelayedSignal> signalQueue = new Queue<DelayedSignal>();
|
||||
|
||||
private DelayedSignal prevQueuedSignal;
|
||||
|
||||
@@ -43,8 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
delay = value;
|
||||
delayTicks = (int)(delay / Timing.Step);
|
||||
signalQueueSize = Math.Max(delayTicks, 1) * 2;
|
||||
// ConcurrentQueue doesn't have Clear(), drain it instead
|
||||
while (signalQueue.TryDequeue(out _)) { }
|
||||
signalQueue.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,19 +66,19 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (signalQueue.IsEmpty)
|
||||
if (signalQueue.Count == 0)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (var val in signalQueue.ToArray())
|
||||
foreach (var val in signalQueue)
|
||||
{
|
||||
val.SendTimer -= 1;
|
||||
}
|
||||
while (signalQueue.TryPeek(out var signalOut) && signalOut.SendTimer <= 0)
|
||||
while (signalQueue.Count > 0 && signalQueue.Peek().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)
|
||||
@@ -104,15 +100,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
case "signal_in":
|
||||
if (signalQueue.Count >= signalQueueSize) { return; }
|
||||
if (ResetWhenSignalReceived)
|
||||
{
|
||||
prevQueuedSignal = null;
|
||||
while (signalQueue.TryDequeue(out _)) { }
|
||||
}
|
||||
if (ResetWhenDifferentSignalReceived && signalQueue.TryPeek(out var peekSignal) && peekSignal.Signal.value != signal.value)
|
||||
if (ResetWhenSignalReceived) { prevQueuedSignal = null; signalQueue.Clear(); }
|
||||
if (ResetWhenDifferentSignalReceived && signalQueue.Count > 0 && signalQueue.Peek().Signal.value != signal.value)
|
||||
{
|
||||
prevQueuedSignal = null;
|
||||
while (signalQueue.TryDequeue(out _)) { }
|
||||
signalQueue.Clear();
|
||||
}
|
||||
|
||||
if (prevQueuedSignal != null &&
|
||||
@@ -135,10 +127,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.IsEmpty && newDelay != Delay)
|
||||
if (signalQueue.Count > 0 && newDelay != Delay)
|
||||
{
|
||||
prevQueuedSignal = null;
|
||||
while (signalQueue.TryDequeue(out _)) { }
|
||||
signalQueue.Clear();
|
||||
}
|
||||
Delay = newDelay;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -32,8 +31,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float thirdInverseMax = 0, loadEqnConstant = 0;
|
||||
|
||||
// Thread-safe immutable dictionary for connection pairs (read-only after initialization)
|
||||
private static readonly ImmutableDictionary<string, string> connectionPairs = new Dictionary<string, string>
|
||||
private static readonly Dictionary<string, string> connectionPairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "power_in", "power_out"},
|
||||
{ "signal_in", "signal_out" },
|
||||
@@ -42,7 +40,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; } }
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -11,8 +10,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class WifiComponent : ItemComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private static readonly ConcurrentDictionary<WifiComponent, byte> _wifiDict = new ConcurrentDictionary<WifiComponent, byte>();
|
||||
private static IEnumerable<WifiComponent> AllWifiComponents => _wifiDict.Keys;
|
||||
private static readonly List<WifiComponent> list = new List<WifiComponent>();
|
||||
|
||||
const int ChannelMemorySize = 10;
|
||||
|
||||
@@ -113,7 +111,7 @@ namespace Barotrauma.Items.Components
|
||||
public WifiComponent(Item item, ContentXElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
_wifiDict.TryAdd(this, 0);
|
||||
list.Add(this);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -158,7 +156,7 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
public IEnumerable<WifiComponent> GetReceiversInRange()
|
||||
{
|
||||
return AllWifiComponents.Where(w => w != this && w.CanReceive(this));
|
||||
return list.Where(w => w != this && w.CanReceive(this));
|
||||
}
|
||||
|
||||
public bool CanReceive(WifiComponent sender)
|
||||
@@ -187,7 +185,7 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
public IEnumerable<WifiComponent> GetTransmittersInRange()
|
||||
{
|
||||
return AllWifiComponents.Where(w => w != this && w.CanTransmit(this));
|
||||
return list.Where(w => w != this && w.CanTransmit(this));
|
||||
}
|
||||
|
||||
public bool CanTransmit(WifiComponent sender)
|
||||
@@ -277,8 +275,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (signal.source != null)
|
||||
{
|
||||
// Use ToList() snapshot for thread-safe iteration
|
||||
foreach (Connection receiver in wifiComp.item.LastSentSignalRecipients.ToList())
|
||||
foreach (Connection receiver in wifiComp.item.LastSentSignalRecipients)
|
||||
{
|
||||
if (!signal.source.LastSentSignalRecipients.Contains(receiver))
|
||||
{
|
||||
@@ -369,7 +366,7 @@ namespace Barotrauma.Items.Components
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
_wifiDict.TryRemove(this, out _);
|
||||
list.Remove(this);
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
|
||||
@@ -250,8 +250,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (connections[0] != null && connections[1] != null)
|
||||
{
|
||||
// Use ToArray() snapshot for thread-safe iteration
|
||||
foreach (ItemComponent ic in item.Components.ToArray())
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic == this) { continue; }
|
||||
|
||||
@@ -724,11 +723,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item0 == null && item1 != null)
|
||||
{
|
||||
item0 = Item.ItemList.FirstOrDefault(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
|
||||
item0 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
|
||||
}
|
||||
else if (item0 != null && item1 == null)
|
||||
{
|
||||
item1 = Item.ItemList.FirstOrDefault(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
|
||||
item1 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
|
||||
}
|
||||
|
||||
if (item0 == null || item1 == null || nodes.Count == 0) { return; }
|
||||
|
||||
@@ -73,11 +73,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public PhysicsBody PhysicsBody { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag to prevent multiple queued refresh requests.
|
||||
/// </summary>
|
||||
private volatile bool physicsBodyRefreshQueued;
|
||||
|
||||
private float radius;
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
public float Radius
|
||||
@@ -88,7 +83,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (radius == value) { return; }
|
||||
radius = value;
|
||||
if (PhysicsBody != null) { QueuePhysicsBodyRefresh(); }
|
||||
if (PhysicsBody != null) { RefreshPhysicsBodySize(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +97,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (width == value) { return; }
|
||||
width = value;
|
||||
if (PhysicsBody != null) { QueuePhysicsBodyRefresh(); }
|
||||
if (PhysicsBody != null) { RefreshPhysicsBodySize(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,28 +111,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (height == value) { return; }
|
||||
height = value;
|
||||
if (PhysicsBody != null) { QueuePhysicsBodyRefresh(); }
|
||||
if (PhysicsBody != null) { RefreshPhysicsBodySize(); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue the physics body refresh to be executed on the main thread.
|
||||
/// This is necessary because physics body operations are not thread-safe.
|
||||
/// </summary>
|
||||
private void QueuePhysicsBodyRefresh()
|
||||
{
|
||||
if (physicsBodyRefreshQueued) { return; }
|
||||
physicsBodyRefreshQueued = true;
|
||||
PhysicsBodyQueue.EnqueueCreation(() =>
|
||||
{
|
||||
if (!item.Removed)
|
||||
{
|
||||
RefreshPhysicsBodySize();
|
||||
}
|
||||
physicsBodyRefreshQueued = false;
|
||||
});
|
||||
}
|
||||
|
||||
private float currentRadius, currentWidth, currentHeight;
|
||||
|
||||
private Vector2 bodyOffset;
|
||||
@@ -312,18 +289,13 @@ namespace Barotrauma.Items.Components
|
||||
Matrix transform = Matrix.CreateRotationZ(-item.RotationRad);
|
||||
offset = Vector2.Transform(offset, transform);
|
||||
}
|
||||
|
||||
// Defer physics operations if in parallel context (Farseer is not thread-safe)
|
||||
var capturedBody = PhysicsBody;
|
||||
var capturedPos = item.SimPosition + offset;
|
||||
var capturedRot = -item.RotationRad;
|
||||
if (ignoreContacts)
|
||||
{
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedBody.SetTransformIgnoreContacts(capturedPos, capturedRot));
|
||||
PhysicsBody.SetTransformIgnoreContacts(item.SimPosition + offset, -item.RotationRad);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsBodyQueue.ExecuteOrDefer(() => capturedBody.SetTransform(capturedPos, capturedRot));
|
||||
PhysicsBody.SetTransform(item.SimPosition + offset, -item.RotationRad);
|
||||
}
|
||||
PhysicsBody.UpdateDrawPosition();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user