From 1617cd8f7a0340b9dfd5f1939090f800eaf6c648 Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 8 Nov 2016 21:14:29 +0200 Subject: [PATCH] Item/statuseffect optimization: - PowerTransfer components cache power connections instead of rechecking them every frame - items store connections in a dictionary with the name of the connection as a key (-> finding the correct connection when sending a signal is more efficient) - storing item tags & StatusEffect targetNames in HashSets --- Subsurface/Source/Characters/StatusEffect.cs | 20 ++-- .../Source/Items/Components/ItemComponent.cs | 2 +- .../Items/Components/Power/PowerTransfer.cs | 57 +++++---- Subsurface/Source/Items/Item.cs | 110 ++++++++++++------ 4 files changed, 120 insertions(+), 69 deletions(-) diff --git a/Subsurface/Source/Characters/StatusEffect.cs b/Subsurface/Source/Characters/StatusEffect.cs index 7518fa4e7..1ff504b28 100644 --- a/Subsurface/Source/Characters/StatusEffect.cs +++ b/Subsurface/Source/Characters/StatusEffect.cs @@ -16,7 +16,7 @@ namespace Barotrauma } private TargetType targetTypes; - private string[] targetNames; + private HashSet targetNames; private List requiredItems; @@ -26,8 +26,8 @@ namespace Barotrauma private bool setValue; private bool disableDeltaTime; - - private string[] onContainingNames; + + private HashSet onContainingNames; private readonly float duration; @@ -48,12 +48,12 @@ namespace Barotrauma get { return targetTypes; } } - public string[] TargetNames + public HashSet TargetNames { get { return targetNames; } } - public string[] OnContainingNames + public HashSet OnContainingNames { get { return onContainingNames; } } @@ -92,10 +92,10 @@ namespace Barotrauma type = (ActionType)Enum.Parse(typeof(ActionType), split[0], true); string[] containingNames = split[1].Split(','); - onContainingNames = new string[containingNames.Length]; - for (int i =0; i < containingNames.Length; i++) + onContainingNames = new HashSet(); + for (int i = 0; i < containingNames.Length; i++) { - onContainingNames[i] = containingNames[i].Trim(); + onContainingNames.Add(containingNames[i].Trim()); } } @@ -116,10 +116,10 @@ namespace Barotrauma break; case "targetnames": string[] names = attribute.Value.Split(','); - targetNames = new string[names.Length]; + targetNames = new HashSet(); for (int i=0; i < names.Length; i++ ) { - targetNames[i] = names[i].Trim(); + targetNames.Add(names[i].Trim()); } break; case "sound": diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index a10d499dc..f8250618a 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -374,7 +374,7 @@ namespace Barotrauma.Items.Components return; } - List matchingSounds = null; + List matchingSounds; if (!sounds.TryGetValue(type, out matchingSounds)) return; ItemSound itemSound = null; diff --git a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs index 4fe57217a..d7257c601 100644 --- a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs @@ -22,7 +22,9 @@ namespace Barotrauma.Items.Components //affects how fast changes in power/load are carried over the grid static float inertia = 5.0f; - static List connectedList = new List(); + static HashSet connectedList = new HashSet(); + + private List powerConnections; private float powerLoad; @@ -35,24 +37,27 @@ namespace Barotrauma.Items.Components : base(item, element) { IsActive = true; + + powerConnections = new List(); } public override void Update(float deltaTime, Camera cam) { - //reset and recalculate the power generated/consumed - //by the constructions connected to the grid - fullPower = 0.0f; - fullLoad = 0.0f; - connectedList.Clear(); - if (updateTimer > 0) { + //this junction box has already been updated this frame updateTimer--; return; } - + + //reset and recalculate the power generated/consumed + //by the constructions connected to the grid + fullPower = 0.0f; + fullLoad = 0.0f; + updateTimer = 0; + connectedList.Clear(); + CheckJunctions(deltaTime); - updateTimer = 0; foreach (Powered p in connectedList) { @@ -68,8 +73,7 @@ namespace Barotrauma.Items.Components //(except if running as a client) if (GameMain.Client != null) continue; if (-pt.currPowerConsumption < Math.Max(pt.powerLoad * Rand.Range(1.9f,2.1f), 200.0f)) continue; - - + float prevCondition = pt.item.Condition; pt.item.Condition -= deltaTime * 10.0f; @@ -109,37 +113,31 @@ namespace Barotrauma.Items.Components ApplyStatusEffects(ActionType.OnActive, deltaTime, null); - List connections = item.Connections; - if (connections == null) return; - - foreach (Connection c in connections) + foreach (Connection c in powerConnections) { - if (!c.IsPower) continue; - var recipients = c.Recipients; foreach (Connection recipient in recipients) { - if (recipient == null || !c.IsPower) continue; + if (recipient == null) continue; Item it = recipient.Item; if (it == null) continue; - //if (it.Updated) continue; - Powered powered = it.GetComponent(); if (powered == null || !powered.IsActive) continue; if (connectedList.Contains(powered)) continue; PowerTransfer powerTransfer = powered as PowerTransfer; - PowerContainer powerContainer = powered as PowerContainer; if (powerTransfer != null) { - //if (powerTransfer.updateTimer>0) continue; powerTransfer.CheckJunctions(deltaTime); + continue; } - else if (powerContainer != null) + + PowerContainer powerContainer = powered as PowerContainer; + if (powerContainer != null) { if (recipient.Name == "power_in") { @@ -187,6 +185,19 @@ namespace Barotrauma.Items.Components GuiFrame.Update(1.0f / 60.0f); } + public override void OnMapLoaded() + { + var connections = item.Connections; + if (connections == null) + { + IsActive = false; + return; + } + + powerConnections = connections.FindAll(c => c.IsPower); + if (powerConnections.Count == 0) IsActive = false; + } + public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item sender, float power) { base.ReceiveSignal(stepsTaken, signal, connection, sender, power); diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 2179be778..75a051450 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -36,7 +36,7 @@ namespace Barotrauma public static bool ShowLinks = true; - private List tags; + private HashSet tags; public Hull CurrentHull; @@ -55,6 +55,9 @@ namespace Barotrauma private bool inWater; private Inventory parentInventory; + private Inventory ownInventory; + + private Dictionary connections; //a dictionary containing lists of the status effects in all the components of the item private Dictionary> statusEffectLists; @@ -275,8 +278,7 @@ namespace Barotrauma { get { - ItemContainer c = GetComponent(); - return (c == null) ? null : Array.FindAll(c.Inventory.Items, i=>i!=null); + return (ownInventory == null) ? null : Array.FindAll(ownInventory.Items, i => i != null); } } @@ -326,7 +328,7 @@ namespace Barotrauma components = new List(); drawableComponents = new List(); FixRequirements = new List(); - tags = new List(); + tags = new HashSet(); rect = newRect; @@ -401,6 +403,12 @@ namespace Barotrauma if (body != null) body.FarseerBody.OnCollision += OnCollision; } + var itemContainer = GetComponent(); + if (itemContainer!=null) + { + ownInventory = itemContainer.Inventory; + } + InsertToList(); ItemList.Add(this); } @@ -428,15 +436,16 @@ namespace Barotrauma public void RemoveContained(Item contained) { - ItemContainer c = GetComponent(); - if (c == null) return; - - c.RemoveContained(contained); + if (ownInventory != null) + { + ownInventory.RemoveItem(contained); + } + contained.Container = null; } - public void SetTransform(Vector2 simPosition, float rotation) + public void SetTransform(Vector2 simPosition, float rotation, bool findNewHull = true) { if (body != null) { @@ -457,7 +466,7 @@ namespace Barotrauma rect.X = (int)(displayPos.X - rect.Width / 2.0f); rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); - FindHull(); + if (findNewHull) FindHull(); } public override void Move(Vector2 amount) @@ -544,7 +553,34 @@ namespace Barotrauma return rootContainer; } - + + public void SetContainedItemPositions() + { + if (ownInventory == null) return; + + Vector2 simPos = SimPosition; + Vector2 displayPos = Position; + + foreach (Item contained in ownInventory.Items) + { + if (contained == null) continue; + + if (contained.body != null) + { + contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, 0.0f); + } + + contained.Rect = + new Rectangle( + (int)(displayPos.X - contained.Rect.Width / 2.0f), + (int)(displayPos.Y + contained.Rect.Height / 2.0f), + contained.Rect.Width, contained.Rect.Height); + + contained.Submarine = Submarine; + contained.CurrentHull = CurrentHull; + } + } + public void AddTag(string tag) { if (tags.Contains(tag)) return; @@ -580,16 +616,15 @@ namespace Barotrauma bool hasTargets = (effect.TargetNames == null); Item[] containedItems = ContainedItems; - if (effect.OnContainingNames!=null) + if (effect.OnContainingNames != null) { foreach (string s in effect.OnContainingNames) { - if (containedItems.FirstOrDefault(x => x!=null && x.Name==s && x.Condition>0.0f) == null) return; + if (!containedItems.Any(x => x!=null && x.Name==s && x.Condition > 0.0f)) return; } } List targets = new List(); - if (containedItems != null) { if (effect.Targets.HasFlag(StatusEffect.TargetType.Contained)) @@ -691,8 +726,7 @@ namespace Barotrauma { ic.Update(deltaTime, cam); - if (ic.IsActive) ic.PlaySound(ActionType.OnActive, WorldPosition); - //ic.ApplyStatusEffects(ActionType.OnActive, deltaTime, null); + if (ic.IsActive) ic.PlaySound(ActionType.OnActive, WorldPosition); } else { @@ -1154,25 +1188,23 @@ namespace Barotrauma public void SendSignal(int stepsTaken, string signal, string connectionName, float power = 0.0f) { + if (connections == null) return; + stepsTaken++; - ConnectionPanel panel = GetComponent(); - if (panel == null) return; - foreach (Connection c in panel.Connections) + Connection c = null; + if (!connections.TryGetValue(connectionName, out c)) return; + + if (stepsTaken > 10) { - if (c.Name != connectionName) continue; - - if (stepsTaken > 10) - { - //use a coroutine to prevent infinite loops by creating a one - //frame delay if the "signal chain" gets too long - CoroutineManager.StartCoroutine(SendSignal(signal, c, power)); - } - else - { - c.SendSignal(stepsTaken, signal, this, power); - } + //use a coroutine to prevent infinite loops by creating a one + //frame delay if the "signal chain" gets too long + CoroutineManager.StartCoroutine(SendSignal(signal, c, power)); } + else + { + c.SendSignal(stepsTaken, signal, this, power); + } } private IEnumerable SendSignal(string signal, Connection connection, float power = 0.0f) @@ -1180,9 +1212,6 @@ namespace Barotrauma //wait one frame yield return CoroutineStatus.Running; - ConnectionPanel panel = GetComponent(); - if (panel == null) yield return CoroutineStatus.Success; - connection.SendSignal(0, signal, this, power); yield return CoroutineStatus.Success; @@ -1654,7 +1683,18 @@ namespace Barotrauma foreach (ItemComponent ic in components) { ic.OnMapLoaded(); - } + } + + //cache connections into a dictionary for faster lookups + var connectionPanel = GetComponent(); + connections = new Dictionary(); + + if (connectionPanel == null) return; + foreach (Connection c in connectionPanel.Connections) + { + if (!connections.ContainsKey(c.Name)) + connections.Add(c.Name, c); + } }