Improved PowerTransfer logic. The PowerTransfer components cache all the connections that are connected to the item (directly or via another item) and only recalculate them when changes are done to the wiring. Signals received by the PowerTransfer component are relayed directly to the recipients instead of stepping through all the wires and junction boxes in between.
Fixes stack overflow exceptions caused by signals looping between junction boxes and improves performance because the layout of the power grid doesn't have to be calculated every frame and the wire connections don't have to be rechecked when sending a signal. Closes #222
This commit is contained in:
@@ -182,8 +182,8 @@ namespace Barotrauma.Items.Components
|
||||
GameServer.Log(Character.Controlled.LogName + " connected a wire from " +
|
||||
Item.Name + " (" + Name + ") to " + otherConnection.item.Name + " (" + otherConnection.Name + ")", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
|
||||
Wires[index] = draggingConnected;
|
||||
|
||||
AddLink(index, draggingConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
static float fullPower;
|
||||
static float fullLoad;
|
||||
|
||||
//private bool updated;
|
||||
|
||||
|
||||
private int updateTimer;
|
||||
|
||||
const float FireProbability = 0.15f;
|
||||
@@ -19,35 +17,82 @@ namespace Barotrauma.Items.Components
|
||||
//affects how fast changes in power/load are carried over the grid
|
||||
static float inertia = 5.0f;
|
||||
|
||||
static HashSet<Powered> connectedList = new HashSet<Powered>();
|
||||
|
||||
private HashSet<PowerTransfer> connectedPoweredList = new HashSet<PowerTransfer>();
|
||||
private List<Connection> powerConnections;
|
||||
|
||||
private 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
|
||||
private Dictionary<Connection, HashSet<Connection>> connectedRecipients = new Dictionary<Connection, HashSet<Connection>>();
|
||||
|
||||
private float powerLoad;
|
||||
|
||||
private bool isBroken;
|
||||
|
||||
public float PowerLoad
|
||||
{
|
||||
get { return powerLoad; }
|
||||
}
|
||||
|
||||
//can the component transfer power
|
||||
public virtual bool CanTransfer
|
||||
private bool canTransfer;
|
||||
public bool CanTransfer
|
||||
{
|
||||
get { return IsActive; }
|
||||
get { return canTransfer; }
|
||||
set
|
||||
{
|
||||
if (canTransfer == value) return;
|
||||
canTransfer = value;
|
||||
SetAllConnectionsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.IsActive;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (base.IsActive != value) SetAllConnectionsDirty();
|
||||
base.IsActive = value;
|
||||
}
|
||||
}
|
||||
|
||||
public PowerTransfer(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
canTransfer = true;
|
||||
|
||||
powerConnections = new List<Connection>();
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
|
||||
if (!isBroken)
|
||||
{
|
||||
SetAllConnectionsDirty();
|
||||
isBroken = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!CanTransfer) return;
|
||||
|
||||
if (isBroken)
|
||||
{
|
||||
SetAllConnectionsDirty();
|
||||
isBroken = false;
|
||||
}
|
||||
|
||||
RefreshConnections();
|
||||
|
||||
if (updateTimer > 0)
|
||||
{
|
||||
//this junction box has already been updated this frame
|
||||
@@ -60,16 +105,13 @@ namespace Barotrauma.Items.Components
|
||||
fullPower = 0.0f;
|
||||
fullLoad = 0.0f;
|
||||
|
||||
connectedList.Clear();
|
||||
connectedPoweredList.Clear();
|
||||
|
||||
CheckJunctions(deltaTime);
|
||||
CheckPower(deltaTime);
|
||||
updateTimer = 0;
|
||||
|
||||
foreach (Powered p in connectedList)
|
||||
{
|
||||
PowerTransfer pt = p as PowerTransfer;
|
||||
if (pt == null) continue;
|
||||
|
||||
foreach (PowerTransfer pt in connectedPoweredList)
|
||||
{
|
||||
pt.powerLoad += (fullLoad - pt.powerLoad) / inertia;
|
||||
pt.currPowerConsumption += (-fullPower - pt.currPowerConsumption) / inertia;
|
||||
pt.Item.SendSignal(0, "", "power", null, fullPower / Math.Max(fullLoad, 1.0f));
|
||||
@@ -112,39 +154,99 @@ namespace Barotrauma.Items.Components
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
private void RefreshConnections()
|
||||
{
|
||||
var connections = item.Connections;
|
||||
foreach (Connection c in connections)
|
||||
{
|
||||
if (!connectionDirty[c]) continue;
|
||||
|
||||
HashSet<Connection> connected = new HashSet<Connection>();
|
||||
if (!connectedRecipients.ContainsKey(c))
|
||||
{
|
||||
connectedRecipients.Add(c, connected);
|
||||
}
|
||||
else
|
||||
{
|
||||
//mark all previous recipients as dirty
|
||||
foreach (Connection recipient in connectedRecipients[c])
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) pt.connectionDirty[recipient] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//find all connections that are connected to this one (directly or via another PowerTransfer)
|
||||
connected.Add(c);
|
||||
GetConnected(c, connected);
|
||||
connectedRecipients[c] = connected;
|
||||
|
||||
//go through all the PowerTransfers and 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)
|
||||
foreach (Connection recipient in connected)
|
||||
{
|
||||
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (recipientPowerTransfer == null) continue;
|
||||
|
||||
if (!connectedRecipients.ContainsKey(recipient))
|
||||
{
|
||||
connectedRecipients.Add(recipient, connected);
|
||||
}
|
||||
|
||||
recipientPowerTransfer.connectedRecipients[recipient] = connected;
|
||||
recipientPowerTransfer.connectionDirty[recipient] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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)
|
||||
{
|
||||
var recipients = c.Recipients;
|
||||
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (recipient == null || connected.Contains(recipient)) continue;
|
||||
|
||||
Item it = recipient.Item;
|
||||
if (it == null || it.Condition <= 0.0f) continue;
|
||||
|
||||
connected.Add(recipient);
|
||||
|
||||
var powerTransfer = it.GetComponent<PowerTransfer>();
|
||||
if (powerTransfer != null && powerTransfer.CanTransfer && powerTransfer.IsActive)
|
||||
{
|
||||
GetConnected(recipient, connected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//a recursive function that goes through all the junctions and adds up
|
||||
//all the generated/consumed power of the constructions connected to the grid
|
||||
private void CheckJunctions(float deltaTime)
|
||||
private void CheckPower(float deltaTime)
|
||||
{
|
||||
updateTimer = 1;
|
||||
connectedList.Add(this);
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
connectedPoweredList.Clear();
|
||||
|
||||
foreach (Connection c in powerConnections)
|
||||
{
|
||||
var recipients = c.Recipients;
|
||||
|
||||
HashSet<Connection> recipients = connectedRecipients[c];
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (recipient == null) continue;
|
||||
|
||||
Item it = recipient.Item;
|
||||
if (it == null) continue;
|
||||
|
||||
if (it.Condition <= 0.0f) continue;
|
||||
if (it == null || it.Condition <= 0.0f) continue;
|
||||
|
||||
foreach (Powered powered in it.GetComponents<Powered>())
|
||||
{
|
||||
if (powered == null || !powered.IsActive) continue;
|
||||
|
||||
if (connectedList.Contains(powered)) continue;
|
||||
|
||||
PowerTransfer powerTransfer = powered as PowerTransfer;
|
||||
if (powerTransfer != null)
|
||||
{
|
||||
if (!powerTransfer.CanTransfer) continue;
|
||||
powerTransfer.CheckJunctions(deltaTime);
|
||||
connectedPoweredList.Add(powerTransfer);
|
||||
powerTransfer.updateTimer = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -162,25 +264,38 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
connectedList.Add(powered);
|
||||
//positive power consumption = the construction requires power -> increase load
|
||||
if (powered.CurrPowerConsumption > 0.0f)
|
||||
{
|
||||
fullLoad += powered.CurrPowerConsumption;
|
||||
}
|
||||
else if (powered.CurrPowerConsumption < 0.0f)
|
||||
//negative power consumption = the construction is a
|
||||
//generator/battery or another junction box
|
||||
//negative power consumption = the construction is a /generator/battery
|
||||
{
|
||||
fullPower -= powered.CurrPowerConsumption;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAllConnectionsDirty()
|
||||
{
|
||||
if (item.Connections == null) return;
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
connectionDirty[c] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetConnectionDirty(Connection connection)
|
||||
{
|
||||
var connections = item.Connections;
|
||||
if (connections == null || !connections.Contains(connection)) return;
|
||||
connectionDirty[connection] = true;
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
var connections = item.Connections;
|
||||
@@ -189,18 +304,38 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
powerConnections = connections.FindAll(c => c.IsPower);
|
||||
if (powerConnections.Count == 0) IsActive = false;
|
||||
|
||||
SetAllConnectionsDirty();
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power)
|
||||
{
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power);
|
||||
|
||||
if (!connectedRecipients.ContainsKey(connection)) return;
|
||||
|
||||
if (connection.Name.Length > 5 && connection.Name.Substring(0, 6).ToLowerInvariant() == "signal")
|
||||
{
|
||||
connection.SendSignal(stepsTaken, signal, source, sender, 0.0f);
|
||||
foreach (Connection recipient in connectedRecipients[connection])
|
||||
{
|
||||
if (recipient.Item == item || recipient.Item == source) continue;
|
||||
|
||||
foreach (ItemComponent ic in recipient.Item.components)
|
||||
{
|
||||
//powertransfer components don't need to receive the signal because we relay it straight
|
||||
//to the connected items without going through the whole chain of junction boxes
|
||||
if (ic is PowerTransfer) continue;
|
||||
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, 0.0f);
|
||||
}
|
||||
|
||||
foreach (StatusEffect effect in recipient.effects)
|
||||
{
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private static Wire draggingConnected;
|
||||
|
||||
private List<StatusEffect> effects;
|
||||
public readonly List<StatusEffect> effects;
|
||||
|
||||
public readonly ushort[] wireId;
|
||||
|
||||
@@ -167,8 +167,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (StatusEffect effect in recipient.effects)
|
||||
{
|
||||
|
||||
//effect.Apply(ActionType.OnUse, 1.0f, recipient.item, recipient.item);
|
||||
recipient.item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,21 +31,14 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
isOn = value;
|
||||
CanTransfer = value;
|
||||
if (!isOn)
|
||||
{
|
||||
currPowerConsumption = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanTransfer
|
||||
{
|
||||
get
|
||||
{
|
||||
return isOn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public RelayComponent(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
|
||||
@@ -86,14 +86,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RemoveConnection(Item item)
|
||||
{
|
||||
for (int i = 0; i<2; i++)
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i]==null || connections[i].Item!=item) continue;
|
||||
|
||||
for (int n = 0; n< connections[i].Wires.Length; n++)
|
||||
if (connections[i] == null || connections[i].Item != item) continue;
|
||||
|
||||
for (int n = 0; n < connections[i].Wires.Length; n++)
|
||||
{
|
||||
if (connections[i].Wires[n] != this) continue;
|
||||
|
||||
SetConnectedDirty();
|
||||
connections[i].Wires[n] = null;
|
||||
}
|
||||
connections[i] = null;
|
||||
@@ -104,6 +105,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (connection == connections[0]) connections[0] = null;
|
||||
if (connection == connections[1]) connections[1] = null;
|
||||
|
||||
SetConnectedDirty();
|
||||
}
|
||||
|
||||
public bool Connect(Connection newConnection, bool addNode = true, bool sendNetworkEvent = false)
|
||||
@@ -137,8 +140,7 @@ namespace Barotrauma.Items.Components
|
||||
if (newConnection.Item.Submarine == null) continue;
|
||||
|
||||
if (nodes.Count > 0 && nodes[0] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break;
|
||||
if (nodes.Count > 1 && nodes[nodes.Count-1] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break;
|
||||
|
||||
if (nodes.Count > 1 && nodes[nodes.Count - 1] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
@@ -148,11 +150,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
nodes.Add(newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
SetConnectedDirty();
|
||||
|
||||
if (connections[0] != null && connections[1] != null)
|
||||
{
|
||||
foreach (ItemComponent ic in item.components)
|
||||
@@ -323,6 +326,8 @@ namespace Barotrauma.Items.Components
|
||||
connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
}
|
||||
|
||||
SetConnectedDirty();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@@ -363,6 +368,18 @@ namespace Barotrauma.Items.Components
|
||||
return position;
|
||||
}
|
||||
|
||||
public void SetConnectedDirty()
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i]?.Item != null)
|
||||
{
|
||||
var pt = connections[i].Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) pt.SetConnectionDirty(connections[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanNodes()
|
||||
{
|
||||
for (int i = nodes.Count - 2; i > 0; i--)
|
||||
|
||||
Reference in New Issue
Block a user