diff --git a/Content.Server/GameObjects/Components/Power/PowerDevice.cs b/Content.Server/GameObjects/Components/Power/PowerDevice.cs index 7df5f041d6..10dcd4812e 100644 --- a/Content.Server/GameObjects/Components/Power/PowerDevice.cs +++ b/Content.Server/GameObjects/Components/Power/PowerDevice.cs @@ -67,7 +67,8 @@ namespace Content.Server.GameObjects.Components.Power private float _load = 100; //arbitrary magic number to start /// - /// Power load from this entity + /// Power load from this entity. + /// In Watts. /// public float Load { diff --git a/Content.Server/GameObjects/Components/Power/PowerProviderComponent.cs b/Content.Server/GameObjects/Components/Power/PowerProviderComponent.cs index af5c810612..6c0d6a5f5d 100644 --- a/Content.Server/GameObjects/Components/Power/PowerProviderComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerProviderComponent.cs @@ -72,18 +72,20 @@ namespace Content.Server.GameObjects.Components.Power return; } - if (storage.CanDeductCharge(Load)) + + if (storage.CanDeductCharge(Load * frametime)) { PowerAllDevices(); - storage.DeductCharge(Load); + storage.DeductCharge(Load * frametime); return; } - var remainingLoad = storage.AvailableCharge(); - var usedLoad = 0f; + var remainingEnergy = storage.AvailableCharge(frametime); + var usedEnergy = 0f; foreach (var device in DeviceLoadList) { - if (device.Load > remainingLoad) + var deviceLoad = device.Load * frametime; + if (deviceLoad > remainingEnergy) { device.ExternalPowered = false; DepoweredDevices.Add(device); @@ -92,15 +94,15 @@ namespace Content.Server.GameObjects.Components.Power { if (!device.ExternalPowered) { - device.ExternalPowered = true; DepoweredDevices.Remove(device); + device.ExternalPowered = true; } - usedLoad += device.Load; - remainingLoad -= device.Load; + usedEnergy += deviceLoad; + remainingEnergy -= deviceLoad; } } - storage.DeductCharge(usedLoad); + storage.DeductCharge(usedEnergy); } private void PowerAllDevices() { diff --git a/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs b/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs index 2236351173..37aa5264d1 100644 --- a/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs @@ -15,22 +15,26 @@ namespace Content.Server.GameObjects.Components.Power public override string Name => "PowerStorage"; /// - /// Maximum amount of energy the internal battery can store + /// Maximum amount of energy the internal battery can store. + /// In Joules. /// public float Capacity { get; private set; } = 10000; //arbitrary value replace /// - /// Energy the battery is currently storing + /// Energy the battery is currently storing. + /// In Joules. /// public float Charge { get; private set; } = 0; /// - /// Rate at which energy will be taken to charge internal battery + /// Rate at which energy will be taken to charge internal battery. + /// In Watts. /// public float ChargeRate { get; private set; } = 1000; /// - /// Rate at which energy will be distributed to the powernet if needed + /// Rate at which energy will be distributed to the powernet if needed. + /// In Watts. /// public float DistributionRate { get; private set; } = 1000; @@ -124,47 +128,33 @@ namespace Content.Server.GameObjects.Components.Power /// public void DeductCharge(float todeduct) { - Charge = Math.Min(0, Charge - todeduct); + Charge = Math.Max(0, Charge - todeduct); } - /// - /// Returns all possible charge available from the energy storage - /// - public float RequestAllCharge() + public void AddCharge(float charge) { - return Math.Min(ChargeRate, Capacity - Charge); + Charge = Math.Min(Capacity, Charge + charge); } /// /// Returns the charge available from the energy storage /// - public float RequestCharge() + public float RequestCharge(float frameTime) { - return Math.Min(ChargeRate, Capacity - Charge); + return Math.Min(ChargeRate * frameTime, Capacity - Charge); } /// /// Returns the charge available from the energy storage /// - public float AvailableCharge() + public float AvailableCharge(float frameTime) { - return Math.Min(DistributionRate, Charge); + return Math.Min(DistributionRate * frameTime, Charge); } - /// - /// Gives the storage one full tick of charging its energy storage - /// - public void ChargePowerTick() + public void ChargePowerTick(float frameTime) { - Charge = Math.Min(Charge + ChargeRate, Capacity); - } - - /// - /// Takes from the storage one full tick of energy - /// - public void RetrievePassiveStorage() - { - Charge = Math.Min(Charge - DistributionRate, 0); + AddCharge(RequestCharge(frameTime)); } /// diff --git a/Content.Server/GameObjects/Components/Power/Powernet.cs b/Content.Server/GameObjects/Components/Power/Powernet.cs index 7a1f429ec7..190915c52d 100644 --- a/Content.Server/GameObjects/Components/Power/Powernet.cs +++ b/Content.Server/GameObjects/Components/Power/Powernet.cs @@ -20,153 +20,147 @@ namespace Content.Server.GameObjects.Components.Power Uid = powerSystem.NewUid(); } + /// + /// Unique identifier per powernet, used for debugging mostly. + /// public int Uid { get; } /// - /// The entities that make up the powernet's physical location and allow powernet connection + /// The entities that make up the powernet's physical location and allow powernet connection /// - public List WireList { get; set; } = new List(); + public readonly List WireList = new List(); /// - /// Entities that connect directly to the powernet through PTC above to add power or add power load + /// Entities that connect directly to the powernet through above to add power or add power load /// - public List NodeList { get; set; } = new List(); + public readonly List NodeList = new List(); /// - /// Subset of nodelist that adds a continuous power supply to the network + /// Subset of nodelist that adds a continuous power supply to the network /// - public Dictionary GeneratorList { get; set; } = new Dictionary(); + private readonly Dictionary GeneratorList = new Dictionary(); /// - /// Subset of nodelist that draw power, stores information on current continuous powernet load + /// Subset of nodelist that draw power, stores information on current continuous powernet load /// - public SortedSet DeviceLoadList { get; set; } = new SortedSet(new DevicePriorityCompare()); + private readonly SortedSet DeviceLoadList = new SortedSet(new DevicePriorityCompare()); /// - /// Comparer that keeps the device dictionary sorted by powernet priority + /// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet /// - public class DevicePriorityCompare : IComparer - { - public int Compare(PowerDeviceComponent x, PowerDeviceComponent y) - { - int compare = y.Priority.CompareTo(x.Priority); - - //If the comparer returns 0 sortedset will believe it is a duplicate and return 0, so return 1 instead - if (compare == 0 && !x.Equals(y)) - { - return 1; - } - return compare; - } - } + private readonly List DepoweredDevices = new List(); /// - /// Priority that a device will receive power if powernet cannot supply every device + /// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself /// - public enum Priority - { - Necessary, - High, - Medium, - Low, - Provider, - Unnecessary - } + private readonly List PowerStorageSupplierList = new List(); /// - /// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet + /// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible /// - public List DepoweredDevices { get; set; } = new List(); + private readonly List PowerStorageConsumerList = new List(); /// - /// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself - /// - public List PowerStorageSupplierlist { get; set; } = new List(); - - /// - /// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible - /// - public List PowerStorageConsumerlist { get; set; } = new List(); - - /// - /// Static counter of all continuous load placed from devices on this power network + /// Static counter of all continuous load placed from devices on this power network. + /// In Watts. /// public float Load { get; private set; } = 0; /// - /// Static counter of all continiuous supply from generators on this power network + /// Static counter of all continiuous supply from generators on this power network. + /// In Watts. /// public float Supply { get; private set; } = 0; /// - /// Variable that causes powernet to be regenerated from its wires during the next update cycle + /// Variable that causes powernet to be regenerated from its wires during the next update cycle. /// public bool Dirty { get; set; } = false; - public void Update(float frametime) + public void Update(float frameTime) { - float activesupply = Supply; - float activeload = Load; + float activesupply = Supply * frameTime; + float activeload = Load * frameTime; - float storagedemand = 0; + float storageconsumerdemand = 0; - foreach (var supply in PowerStorageConsumerlist) + foreach (var supply in PowerStorageConsumerList) { - storagedemand += supply.RequestCharge(); + storageconsumerdemand += supply.RequestCharge(frameTime); } - float passivesupply = 0; - float passivedemand = 0; + float storagesupply = 0; + float storagesupplierdemand = 0; - foreach (var supply in PowerStorageSupplierlist) + foreach (var supply in PowerStorageSupplierList) { - passivesupply += supply.AvailableCharge(); - passivedemand += supply.RequestCharge(); + storagesupply += supply.AvailableCharge(frameTime); + storagesupplierdemand += supply.RequestCharge(frameTime); } //If we have enough power to feed all load and storage demand, then feed everything - if (activesupply > activeload + storagedemand + passivedemand) + if (activesupply > activeload + storageconsumerdemand + storagesupplierdemand) { PowerAllDevices(); - ChargeActiveStorage(); - ChargePassiveStorage(); + ChargeStorageConsumers(frameTime); + ChargeStorageSuppliers(frameTime); + return; } //We don't have enough power for the storage powernet suppliers, ignore powering them - else if (activesupply > activeload + storagedemand) + else if (activesupply > activeload + storageconsumerdemand) { PowerAllDevices(); - ChargeActiveStorage(); + ChargeStorageConsumers(frameTime); + return; } - //We require the storage powernet suppliers to power the remaining storage components and device load - else if (activesupply + passivesupply > activeload + storagedemand) - { - PowerAllDevices(); - ChargeActiveStorage(); - RetrievePassiveStorage(); - } - //We cant afford to fund the storage components, so lets try to power the basic load using our supply and storage supply - else if (activesupply + passivesupply > activeload) - { - PowerAllDevices(); - RetrievePassiveStorage(); - } - //We cant even cover the basic device load, start disabling devices in order of priority until the remaining load is lowered enough to be met - else if (activesupply + passivesupply < activeload) - { - PowerAllDevices(); //This merely makes our inevitable betrayal all the sweeter - RetrievePassiveStorage(); - var depowervalue = activeload - (activesupply + passivesupply); - //Providers use same method to recreate functionality - foreach (var device in DeviceLoadList) + float totalRemaining = activesupply + storagesupply; + + foreach (var device in DeviceLoadList) + { + var deviceLoad = device.Load * frameTime; + if (deviceLoad > totalRemaining) { device.ExternalPowered = false; DepoweredDevices.Add(device); - depowervalue -= device.Load; - if (depowervalue < 0) - break; + } + else + { + totalRemaining -= deviceLoad; + if (!device.ExternalPowered) + { + DepoweredDevices.Remove(device); + device.ExternalPowered = true; + } + } + } + + // What we have left goes into storage consumers. + foreach (var consumer in PowerStorageConsumerList) + { + if (totalRemaining < 0) + { + break; + } + var demand = consumer.RequestCharge(frameTime); + var taken = Math.Min(demand, totalRemaining); + totalRemaining -= taken; + consumer.AddCharge(taken); + } + + var supplierUsed = storagesupply - totalRemaining; + + foreach (var supplier in PowerStorageSupplierList) + { + var load = supplier.AvailableCharge(frameTime); + var added = Math.Min(load, supplierUsed); + supplierUsed -= added; + supplier.DeductCharge(added); + if (supplierUsed <= 0) + { + return; } } } @@ -180,27 +174,19 @@ namespace Content.Server.GameObjects.Components.Power DepoweredDevices.Clear(); } - private void ChargeActiveStorage() + private void ChargeStorageConsumers(float frametime) { - foreach (var storage in PowerStorageConsumerlist) + foreach (var storage in PowerStorageConsumerList) { - storage.ChargePowerTick(); + storage.ChargePowerTick(frametime); } } - private void ChargePassiveStorage() + private void ChargeStorageSuppliers(float frametime) { - foreach (var storage in PowerStorageSupplierlist) + foreach (var storage in PowerStorageSupplierList) { - storage.ChargePowerTick(); - } - } - - private void RetrievePassiveStorage() - { - foreach (var storage in PowerStorageSupplierlist) - { - storage.ChargePowerTick(); + storage.ChargePowerTick(frametime); } } @@ -217,8 +203,8 @@ namespace Content.Server.GameObjects.Components.Power GeneratorList.Clear(); DeviceLoadList.Clear(); DepoweredDevices.Clear(); - PowerStorageSupplierlist.Clear(); - PowerStorageConsumerlist.Clear(); + PowerStorageSupplierList.Clear(); + PowerStorageConsumerList.Clear(); RemoveFromSystem(); } @@ -259,11 +245,11 @@ namespace Content.Server.GameObjects.Components.Power DepoweredDevices.AddRange(toMerge.DepoweredDevices); toMerge.DepoweredDevices.Clear(); - PowerStorageSupplierlist.AddRange(toMerge.PowerStorageSupplierlist); - toMerge.PowerStorageSupplierlist.Clear(); + PowerStorageSupplierList.AddRange(toMerge.PowerStorageSupplierList); + toMerge.PowerStorageSupplierList.Clear(); - PowerStorageConsumerlist.AddRange(toMerge.PowerStorageConsumerlist); - toMerge.PowerStorageConsumerlist.Clear(); + PowerStorageConsumerList.AddRange(toMerge.PowerStorageConsumerList); + toMerge.PowerStorageConsumerList.Clear(); toMerge.RemoveFromSystem(); } @@ -366,25 +352,25 @@ namespace Content.Server.GameObjects.Components.Power public void AddPowerStorage(PowerStorageComponent storage) { if (storage.ChargePowernet) - PowerStorageSupplierlist.Add(storage); + PowerStorageSupplierList.Add(storage); else - PowerStorageConsumerlist.Add(storage); + PowerStorageConsumerList.Add(storage); } //How do I even call this? TODO: fix public void UpdateStorageType(PowerStorageComponent storage) { //If our chargepowernet settings change we need to tell the powernet of this new setting and remove traces of our old setting - if (PowerStorageSupplierlist.Contains(storage)) - PowerStorageSupplierlist.Remove(storage); - if (PowerStorageConsumerlist.Contains(storage)) - PowerStorageConsumerlist.Remove(storage); + if (PowerStorageSupplierList.Contains(storage)) + PowerStorageSupplierList.Remove(storage); + if (PowerStorageConsumerList.Contains(storage)) + PowerStorageConsumerList.Remove(storage); //Apply new setting if (storage.ChargePowernet) - PowerStorageSupplierlist.Add(storage); + PowerStorageSupplierList.Add(storage); else - PowerStorageConsumerlist.Add(storage); + PowerStorageConsumerList.Add(storage); } /// @@ -392,15 +378,46 @@ namespace Content.Server.GameObjects.Components.Power /// public void RemovePowerStorage(PowerStorageComponent storage) { - if (PowerStorageSupplierlist.Contains(storage)) + if (PowerStorageSupplierList.Contains(storage)) { - PowerStorageSupplierlist.Remove(storage); + PowerStorageSupplierList.Remove(storage); } - if (PowerStorageConsumerlist.Contains(storage)) + if (PowerStorageConsumerList.Contains(storage)) { - PowerStorageSupplierlist.Remove(storage); + PowerStorageSupplierList.Remove(storage); } } #endregion Registration + + /// + /// Priority that a device will receive power if powernet cannot supply every device + /// + public enum Priority + { + Necessary, + High, + Medium, + Low, + Provider, + Unnecessary + } + + /// + /// Comparer that keeps the device dictionary sorted by powernet priority + /// + public class DevicePriorityCompare : IComparer + { + public int Compare(PowerDeviceComponent x, PowerDeviceComponent y) + { + int compare = y.Priority.CompareTo(x.Priority); + + //If the comparer returns 0 sortedset will believe it is a duplicate and return 0, so return 1 instead + if (compare == 0 && !x.Equals(y)) + { + return 1; + } + return compare; + } + } } }