Power is now frame time aware and actually works correctly.

This commit is contained in:
Pieter-Jan Briers
2018-05-27 19:33:27 +02:00
parent 147aad5064
commit ab91d358ef
4 changed files with 167 additions and 157 deletions

View File

@@ -67,7 +67,8 @@ namespace Content.Server.GameObjects.Components.Power
private float _load = 100; //arbitrary magic number to start private float _load = 100; //arbitrary magic number to start
/// <summary> /// <summary>
/// Power load from this entity /// Power load from this entity.
/// In Watts.
/// </summary> /// </summary>
public float Load public float Load
{ {

View File

@@ -72,18 +72,20 @@ namespace Content.Server.GameObjects.Components.Power
return; return;
} }
if (storage.CanDeductCharge(Load))
if (storage.CanDeductCharge(Load * frametime))
{ {
PowerAllDevices(); PowerAllDevices();
storage.DeductCharge(Load); storage.DeductCharge(Load * frametime);
return; return;
} }
var remainingLoad = storage.AvailableCharge(); var remainingEnergy = storage.AvailableCharge(frametime);
var usedLoad = 0f; var usedEnergy = 0f;
foreach (var device in DeviceLoadList) foreach (var device in DeviceLoadList)
{ {
if (device.Load > remainingLoad) var deviceLoad = device.Load * frametime;
if (deviceLoad > remainingEnergy)
{ {
device.ExternalPowered = false; device.ExternalPowered = false;
DepoweredDevices.Add(device); DepoweredDevices.Add(device);
@@ -92,15 +94,15 @@ namespace Content.Server.GameObjects.Components.Power
{ {
if (!device.ExternalPowered) if (!device.ExternalPowered)
{ {
device.ExternalPowered = true;
DepoweredDevices.Remove(device); DepoweredDevices.Remove(device);
device.ExternalPowered = true;
} }
usedLoad += device.Load; usedEnergy += deviceLoad;
remainingLoad -= device.Load; remainingEnergy -= deviceLoad;
} }
} }
storage.DeductCharge(usedLoad); storage.DeductCharge(usedEnergy);
} }
private void PowerAllDevices() private void PowerAllDevices()
{ {

View File

@@ -15,22 +15,26 @@ namespace Content.Server.GameObjects.Components.Power
public override string Name => "PowerStorage"; public override string Name => "PowerStorage";
/// <summary> /// <summary>
/// Maximum amount of energy the internal battery can store /// Maximum amount of energy the internal battery can store.
/// In Joules.
/// </summary> /// </summary>
public float Capacity { get; private set; } = 10000; //arbitrary value replace public float Capacity { get; private set; } = 10000; //arbitrary value replace
/// <summary> /// <summary>
/// Energy the battery is currently storing /// Energy the battery is currently storing.
/// In Joules.
/// </summary> /// </summary>
public float Charge { get; private set; } = 0; public float Charge { get; private set; } = 0;
/// <summary> /// <summary>
/// Rate at which energy will be taken to charge internal battery /// Rate at which energy will be taken to charge internal battery.
/// In Watts.
/// </summary> /// </summary>
public float ChargeRate { get; private set; } = 1000; public float ChargeRate { get; private set; } = 1000;
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public float DistributionRate { get; private set; } = 1000; public float DistributionRate { get; private set; } = 1000;
@@ -124,47 +128,33 @@ namespace Content.Server.GameObjects.Components.Power
/// </summary> /// </summary>
public void DeductCharge(float todeduct) public void DeductCharge(float todeduct)
{ {
Charge = Math.Min(0, Charge - todeduct); Charge = Math.Max(0, Charge - todeduct);
} }
/// <summary> public void AddCharge(float charge)
/// Returns all possible charge available from the energy storage
/// </summary>
public float RequestAllCharge()
{ {
return Math.Min(ChargeRate, Capacity - Charge); Charge = Math.Min(Capacity, Charge + charge);
} }
/// <summary> /// <summary>
/// Returns the charge available from the energy storage /// Returns the charge available from the energy storage
/// </summary> /// </summary>
public float RequestCharge() public float RequestCharge(float frameTime)
{ {
return Math.Min(ChargeRate, Capacity - Charge); return Math.Min(ChargeRate * frameTime, Capacity - Charge);
} }
/// <summary> /// <summary>
/// Returns the charge available from the energy storage /// Returns the charge available from the energy storage
/// </summary> /// </summary>
public float AvailableCharge() public float AvailableCharge(float frameTime)
{ {
return Math.Min(DistributionRate, Charge); return Math.Min(DistributionRate * frameTime, Charge);
} }
/// <summary> public void ChargePowerTick(float frameTime)
/// Gives the storage one full tick of charging its energy storage
/// </summary>
public void ChargePowerTick()
{ {
Charge = Math.Min(Charge + ChargeRate, Capacity); AddCharge(RequestCharge(frameTime));
}
/// <summary>
/// Takes from the storage one full tick of energy
/// </summary>
public void RetrievePassiveStorage()
{
Charge = Math.Min(Charge - DistributionRate, 0);
} }
/// <summary> /// <summary>

View File

@@ -20,154 +20,148 @@ namespace Content.Server.GameObjects.Components.Power
Uid = powerSystem.NewUid(); Uid = powerSystem.NewUid();
} }
/// <summary>
/// Unique identifier per powernet, used for debugging mostly.
/// </summary>
public int Uid { get; } public int Uid { get; }
/// <summary> /// <summary>
/// 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
/// </summary> /// </summary>
public List<PowerTransferComponent> WireList { get; set; } = new List<PowerTransferComponent>(); public readonly List<PowerTransferComponent> WireList = new List<PowerTransferComponent>();
/// <summary> /// <summary>
/// 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 <see cref="PowerTransferComponent" /> above to add power or add power load
/// </summary> /// </summary>
public List<PowerNodeComponent> NodeList { get; set; } = new List<PowerNodeComponent>(); public readonly List<PowerNodeComponent> NodeList = new List<PowerNodeComponent>();
/// <summary> /// <summary>
/// Subset of nodelist that adds a continuous power supply to the network /// Subset of nodelist that adds a continuous power supply to the network
/// </summary> /// </summary>
public Dictionary<PowerGeneratorComponent, float> GeneratorList { get; set; } = new Dictionary<PowerGeneratorComponent, float>(); private readonly Dictionary<PowerGeneratorComponent, float> GeneratorList = new Dictionary<PowerGeneratorComponent, float>();
/// <summary> /// <summary>
/// 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
/// </summary> /// </summary>
public SortedSet<PowerDeviceComponent> DeviceLoadList { get; set; } = new SortedSet<PowerDeviceComponent>(new DevicePriorityCompare()); private readonly SortedSet<PowerDeviceComponent> DeviceLoadList = new SortedSet<PowerDeviceComponent>(new DevicePriorityCompare());
/// <summary>
/// Comparer that keeps the device dictionary sorted by powernet priority
/// </summary>
public class DevicePriorityCompare : IComparer<PowerDeviceComponent>
{
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;
}
}
/// <summary>
/// Priority that a device will receive power if powernet cannot supply every device
/// </summary>
public enum Priority
{
Necessary,
High,
Medium,
Low,
Provider,
Unnecessary
}
/// <summary> /// <summary>
/// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet /// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet
/// </summary> /// </summary>
public List<PowerDeviceComponent> DepoweredDevices { get; set; } = new List<PowerDeviceComponent>(); private readonly List<PowerDeviceComponent> DepoweredDevices = new List<PowerDeviceComponent>();
/// <summary> /// <summary>
/// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself /// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself
/// </summary> /// </summary>
public List<PowerStorageComponent> PowerStorageSupplierlist { get; set; } = new List<PowerStorageComponent>(); private readonly List<PowerStorageComponent> PowerStorageSupplierList = new List<PowerStorageComponent>();
/// <summary> /// <summary>
/// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible /// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible
/// </summary> /// </summary>
public List<PowerStorageComponent> PowerStorageConsumerlist { get; set; } = new List<PowerStorageComponent>(); private readonly List<PowerStorageComponent> PowerStorageConsumerList = new List<PowerStorageComponent>();
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public float Load { get; private set; } = 0; public float Load { get; private set; } = 0;
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public float Supply { get; private set; } = 0; public float Supply { get; private set; } = 0;
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public bool Dirty { get; set; } = false; public bool Dirty { get; set; } = false;
public void Update(float frametime) public void Update(float frameTime)
{ {
float activesupply = Supply; float activesupply = Supply * frameTime;
float activeload = Load; 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 storagesupply = 0;
float passivedemand = 0; float storagesupplierdemand = 0;
foreach (var supply in PowerStorageSupplierlist) foreach (var supply in PowerStorageSupplierList)
{ {
passivesupply += supply.AvailableCharge(); storagesupply += supply.AvailableCharge(frameTime);
passivedemand += supply.RequestCharge(); storagesupplierdemand += supply.RequestCharge(frameTime);
} }
//If we have enough power to feed all load and storage demand, then feed everything //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(); PowerAllDevices();
ChargeActiveStorage(); ChargeStorageConsumers(frameTime);
ChargePassiveStorage(); ChargeStorageSuppliers(frameTime);
return;
} }
//We don't have enough power for the storage powernet suppliers, ignore powering them //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(); 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 float totalRemaining = activesupply + storagesupply;
foreach (var device in DeviceLoadList) foreach (var device in DeviceLoadList)
{
var deviceLoad = device.Load * frameTime;
if (deviceLoad > totalRemaining)
{ {
device.ExternalPowered = false; device.ExternalPowered = false;
DepoweredDevices.Add(device); DepoweredDevices.Add(device);
depowervalue -= device.Load; }
if (depowervalue < 0) 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; 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(); 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(); storage.ChargePowerTick(frametime);
}
}
private void RetrievePassiveStorage()
{
foreach (var storage in PowerStorageSupplierlist)
{
storage.ChargePowerTick();
} }
} }
@@ -217,8 +203,8 @@ namespace Content.Server.GameObjects.Components.Power
GeneratorList.Clear(); GeneratorList.Clear();
DeviceLoadList.Clear(); DeviceLoadList.Clear();
DepoweredDevices.Clear(); DepoweredDevices.Clear();
PowerStorageSupplierlist.Clear(); PowerStorageSupplierList.Clear();
PowerStorageConsumerlist.Clear(); PowerStorageConsumerList.Clear();
RemoveFromSystem(); RemoveFromSystem();
} }
@@ -259,11 +245,11 @@ namespace Content.Server.GameObjects.Components.Power
DepoweredDevices.AddRange(toMerge.DepoweredDevices); DepoweredDevices.AddRange(toMerge.DepoweredDevices);
toMerge.DepoweredDevices.Clear(); toMerge.DepoweredDevices.Clear();
PowerStorageSupplierlist.AddRange(toMerge.PowerStorageSupplierlist); PowerStorageSupplierList.AddRange(toMerge.PowerStorageSupplierList);
toMerge.PowerStorageSupplierlist.Clear(); toMerge.PowerStorageSupplierList.Clear();
PowerStorageConsumerlist.AddRange(toMerge.PowerStorageConsumerlist); PowerStorageConsumerList.AddRange(toMerge.PowerStorageConsumerList);
toMerge.PowerStorageConsumerlist.Clear(); toMerge.PowerStorageConsumerList.Clear();
toMerge.RemoveFromSystem(); toMerge.RemoveFromSystem();
} }
@@ -366,25 +352,25 @@ namespace Content.Server.GameObjects.Components.Power
public void AddPowerStorage(PowerStorageComponent storage) public void AddPowerStorage(PowerStorageComponent storage)
{ {
if (storage.ChargePowernet) if (storage.ChargePowernet)
PowerStorageSupplierlist.Add(storage); PowerStorageSupplierList.Add(storage);
else else
PowerStorageConsumerlist.Add(storage); PowerStorageConsumerList.Add(storage);
} }
//How do I even call this? TODO: fix //How do I even call this? TODO: fix
public void UpdateStorageType(PowerStorageComponent storage) 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 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)) if (PowerStorageSupplierList.Contains(storage))
PowerStorageSupplierlist.Remove(storage); PowerStorageSupplierList.Remove(storage);
if (PowerStorageConsumerlist.Contains(storage)) if (PowerStorageConsumerList.Contains(storage))
PowerStorageConsumerlist.Remove(storage); PowerStorageConsumerList.Remove(storage);
//Apply new setting //Apply new setting
if (storage.ChargePowernet) if (storage.ChargePowernet)
PowerStorageSupplierlist.Add(storage); PowerStorageSupplierList.Add(storage);
else else
PowerStorageConsumerlist.Add(storage); PowerStorageConsumerList.Add(storage);
} }
/// <summary> /// <summary>
@@ -392,15 +378,46 @@ namespace Content.Server.GameObjects.Components.Power
/// </summary> /// </summary>
public void RemovePowerStorage(PowerStorageComponent storage) 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 #endregion Registration
/// <summary>
/// Priority that a device will receive power if powernet cannot supply every device
/// </summary>
public enum Priority
{
Necessary,
High,
Medium,
Low,
Provider,
Unnecessary
}
/// <summary>
/// Comparer that keeps the device dictionary sorted by powernet priority
/// </summary>
public class DevicePriorityCompare : IComparer<PowerDeviceComponent>
{
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;
}
}
} }
} }