From 135a65f030fd8f998c11ffe0cd3f20eac1180365 Mon Sep 17 00:00:00 2001 From: py01 <60152240+collinlunn@users.noreply.github.com> Date: Fri, 1 Jan 2021 11:21:18 -0600 Subject: [PATCH] ApcNet code improvements (#2876) * Removes per-frame setting of PowerReceiverComponent.Powered * PowerReceiver.SetLoad Co-authored-by: py01 --- .../NodeGroups/ApcNetNodeGroup.cs | 120 ++++++++++++------ .../PowerProviderComponent.cs | 44 ++++++- .../PowerReceiverComponent.cs | 34 ++--- 3 files changed, 135 insertions(+), 63 deletions(-) diff --git a/Content.Server/GameObjects/Components/NodeContainer/NodeGroups/ApcNetNodeGroup.cs b/Content.Server/GameObjects/Components/NodeContainer/NodeGroups/ApcNetNodeGroup.cs index 93da4c630c..d0441b55f4 100644 --- a/Content.Server/GameObjects/Components/NodeContainer/NodeGroups/ApcNetNodeGroup.cs +++ b/Content.Server/GameObjects/Components/NodeContainer/NodeGroups/ApcNetNodeGroup.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Content.Server.GameObjects.Components.Power; @@ -9,6 +10,8 @@ namespace Content.Server.GameObjects.Components.NodeContainer.NodeGroups { public interface IApcNet { + bool Powered { get; } + void AddApc(ApcComponent apc); void RemoveApc(ApcComponent apc); @@ -17,7 +20,7 @@ namespace Content.Server.GameObjects.Components.NodeContainer.NodeGroups void RemovePowerProvider(PowerProviderComponent provider); - void UpdatePowerProviderReceivers(PowerProviderComponent provider); + void UpdatePowerProviderReceivers(PowerProviderComponent provider, int oldLoad, int newLoad); void Update(float frameTime); } @@ -29,11 +32,19 @@ namespace Content.Server.GameObjects.Components.NodeContainer.NodeGroups private readonly Dictionary _apcBatteries = new(); [ViewVariables] - private readonly Dictionary> _providerReceivers = new(); + private readonly List _providers = new(); + + [ViewVariables] + public bool Powered { get => _powered; private set => SetPowered(value); } + private bool _powered = false; //Debug property [ViewVariables] - private int TotalReceivers => _providerReceivers.SelectMany(kvp => kvp.Value).Count(); + private int TotalReceivers => _providers.SelectMany(provider => provider.LinkedReceivers).Count(); + + [ViewVariables] + private int TotalPowerReceiverLoad { get => _totalPowerReceiverLoad; set => SetTotalPowerReceiverLoad(value); } + private int _totalPowerReceiverLoad = 0; public static readonly IApcNet NullNet = new NullApcNet(); @@ -57,80 +68,107 @@ namespace Content.Server.GameObjects.Components.NodeContainer.NodeGroups public void RemoveApc(ApcComponent apc) { _apcBatteries.Remove(apc); - if (!_apcBatteries.Any()) - { - foreach (var receiver in _providerReceivers.SelectMany(kvp => kvp.Value)) - { - receiver.HasApcPower = false; - } - } } public void AddPowerProvider(PowerProviderComponent provider) { - _providerReceivers.Add(provider, provider.LinkedReceivers.ToList()); + _providers.Add(provider); + + foreach (var receiver in provider.LinkedReceivers) + { + TotalPowerReceiverLoad += receiver.Load; + } } public void RemovePowerProvider(PowerProviderComponent provider) { - _providerReceivers.Remove(provider); + _providers.Remove(provider); + + foreach (var receiver in provider.LinkedReceivers) + { + TotalPowerReceiverLoad -= receiver.Load; + } } - public void UpdatePowerProviderReceivers(PowerProviderComponent provider) + public void UpdatePowerProviderReceivers(PowerProviderComponent provider, int oldLoad, int newLoad) { - Debug.Assert(_providerReceivers.ContainsKey(provider)); - _providerReceivers[provider] = provider.LinkedReceivers.ToList(); - } + Debug.Assert(_providers.Contains(provider)); + TotalPowerReceiverLoad -= oldLoad; + TotalPowerReceiverLoad += newLoad; + } public void Update(float frameTime) { - var totalCharge = 0.0; - var totalMaxCharge = 0; - foreach (var (apc, battery) in _apcBatteries) + var remainingPowerNeeded = TotalPowerReceiverLoad * frameTime; + + foreach (var apcBatteryPair in _apcBatteries) { + var apc = apcBatteryPair.Key; + if (!apc.MainBreakerEnabled) continue; - totalCharge += battery.CurrentCharge; - totalMaxCharge += battery.MaxCharge; + var battery = apcBatteryPair.Value; + + if (battery.CurrentCharge < remainingPowerNeeded) + { + remainingPowerNeeded -= battery.CurrentCharge; + battery.CurrentCharge = 0; + } + else + { + battery.UseCharge(remainingPowerNeeded); + remainingPowerNeeded = 0; + } + + if (remainingPowerNeeded == 0) + break; } - foreach (var (_, receivers) in _providerReceivers) - { - foreach (var receiver in receivers) - { - if (!receiver.NeedsPower || receiver.PowerDisabled) - continue; + Powered = remainingPowerNeeded == 0; + } - receiver.HasApcPower = TryUsePower(receiver.Load * frameTime); + private void SetPowered(bool powered) + { + if (powered != Powered) + { + _powered = powered; + PoweredChanged(); + } + } + + private void PoweredChanged() + { + foreach (var provider in _providers) + { + foreach (var receiver in provider.LinkedReceivers) + { + receiver.ApcPowerChanged(); } } } - private bool TryUsePower(float neededCharge) + private void SetTotalPowerReceiverLoad(int totalPowerReceiverLoad) { - foreach (var (apc, battery) in _apcBatteries) - { - if (!apc.MainBreakerEnabled) - continue; + Debug.Assert(totalPowerReceiverLoad >= 0); + _totalPowerReceiverLoad = totalPowerReceiverLoad; - if (battery.TryUseCharge(neededCharge)) //simplification - all power needed must come from one battery - { - return true; - } - } - return false; } #endregion private class NullApcNet : IApcNet { + /// + /// It is important that this returns false, so s with a have no power. + /// + public bool Powered => false; + public void AddApc(ApcComponent apc) { } public void AddPowerProvider(PowerProviderComponent provider) { } public void RemoveApc(ApcComponent apc) { } public void RemovePowerProvider(PowerProviderComponent provider) { } - public void UpdatePowerProviderReceivers(PowerProviderComponent provider) { } + public void UpdatePowerProviderReceivers(PowerProviderComponent provider, int oldLoad, int newLoad) { } public void Update(float frameTime) { } } } diff --git a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerProviderComponent.cs b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerProviderComponent.cs index 3e3549fd10..570357c3bc 100644 --- a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerProviderComponent.cs +++ b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerProviderComponent.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.NodeContainer.NodeGroups; @@ -18,7 +18,11 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents void RemoveReceiver(PowerReceiverComponent receiver); + void UpdateReceiverLoad(int oldLoad, int newLoad); + public IEntity ProviderOwner { get; } + + public bool HasApcPower { get; } } [RegisterComponent] @@ -28,6 +32,9 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents public IEntity ProviderOwner => Owner; + [ViewVariables] + public bool HasApcPower => Net.Powered; + /// /// The max distance this can transmit power to s from. /// @@ -49,14 +56,23 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents public void AddReceiver(PowerReceiverComponent receiver) { + var oldLoad = GetTotalLoad(); _linkedReceivers.Add(receiver); - Net.UpdatePowerProviderReceivers(this); + var newLoad = oldLoad + receiver.Load; + Net.UpdatePowerProviderReceivers(this, oldLoad, newLoad); } public void RemoveReceiver(PowerReceiverComponent receiver) { + var oldLoad = GetTotalLoad(); _linkedReceivers.Remove(receiver); - Net.UpdatePowerProviderReceivers(this); + var newLoad = oldLoad - receiver.Load; + Net.UpdatePowerProviderReceivers(this, oldLoad, newLoad); + } + + public void UpdateReceiverLoad(int oldLoad, int newLoad) + { + Net.UpdatePowerProviderReceivers(this, oldLoad, newLoad); } public override void ExposeData(ObjectSerializer serializer) @@ -82,8 +98,6 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents { receiver.ClearProvider(); } - _linkedReceivers = new List(); - Net.UpdatePowerProviderReceivers(this); foreach (var receiver in receivers) { receiver.TryFindAndSetProvider(); @@ -115,19 +129,37 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents private void SetPowerTransferRange(int newPowerTransferRange) { + var oldLoad = GetTotalLoad(); foreach (var receiver in _linkedReceivers.ToArray()) { receiver.ClearProvider(); } _powerTransferRange = newPowerTransferRange; _linkedReceivers = FindAvailableReceivers(); - Net.UpdatePowerProviderReceivers(this); + var newLoad = GetTotalLoad(); + Net.UpdatePowerProviderReceivers(this, oldLoad, newLoad); + } + + private int GetTotalLoad() + { + var load = 0; + foreach (var receiver in _linkedReceivers) + { + load += receiver.Load; + } + return load; } private class NullPowerProvider : IPowerProvider { + /// + /// It is important that this returns false, so s with a have no power. + /// + public bool HasApcPower => false; + public void AddReceiver(PowerReceiverComponent receiver) { } public void RemoveReceiver(PowerReceiverComponent receiver) { } + public void UpdateReceiverLoad(int oldLoad, int newLoad) { } public IEntity ProviderOwner => default; } } diff --git a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverComponent.cs b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverComponent.cs index 520de52709..b05f03ef96 100644 --- a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverComponent.cs +++ b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverComponent.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System; using Content.Server.GameObjects.Components.NodeContainer; using Content.Server.GameObjects.Components.NodeContainer.NodeGroups; @@ -34,9 +34,11 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents [ViewVariables] public bool Powered => (HasApcPower || !NeedsPower) && !PowerDisabled; + /// + /// If this is being powered by an Apc. + /// [ViewVariables] - public bool HasApcPower { get => _hasApcPower; set => SetHasApcPower(value); } - private bool _hasApcPower; + public bool HasApcPower { get; private set; } /// /// The max distance from a that this can receive power from. @@ -103,7 +105,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents } } - public override void OnRemove() + public override void OnRemove() { if (_physicsComponent != null) { @@ -121,6 +123,14 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents } } + public void ApcPowerChanged() + { + var oldPowered = Powered; + HasApcPower = Provider.HasApcPower; + if (Powered != oldPowered) + OnNewPowerState(); + } + private bool TryFindAvailableProvider(out IPowerProvider foundProvider) { var nearbyEntities = _serverEntityManager @@ -152,7 +162,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents _provider.RemoveReceiver(this); _provider = PowerProviderComponent.NullProvider; NeedsProvider = true; - HasApcPower = false; + ApcPowerChanged(); } private void SetProvider(IPowerProvider newProvider) @@ -161,16 +171,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents _provider = newProvider; newProvider.AddReceiver(this); NeedsProvider = false; - } - - private void SetHasApcPower(bool newHasApcPower) - { - var oldPowered = Powered; - _hasApcPower = newHasApcPower; - if (oldPowered != Powered) - { - OnNewPowerState(); - } + ApcPowerChanged(); } private void SetPowerReceptionRange(int newPowerReceptionRange) @@ -182,6 +183,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents private void SetLoad(int newLoad) { + Provider.UpdateReceiverLoad(Load, newLoad); _load = newLoad; } @@ -228,10 +230,10 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents ClearProvider(); } } + /// ///Adds some markup to the examine text of whatever object is using this component to tell you if it's powered or not, even if it doesn't have an icon state to do this for you. /// - public void Examine(FormattedMessage message, bool inDetailsRange) { message.AddMarkup(Loc.GetString("It appears to be {0}.", Powered ? "[color=darkgreen]powered[/color]" : "[color=darkred]un-powered[/color]"));