Pow3r: stage 1 (#4208)

Co-authored-by: 20kdc <asdd2808@gmail.com>
This commit is contained in:
Pieter-Jan Briers
2021-07-04 18:11:52 +02:00
committed by GitHub
parent ea60a81fdf
commit 103bc19508
212 changed files with 8584 additions and 4426 deletions

View File

@@ -1,37 +0,0 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
namespace Content.Server.NodeContainer.NodeGroups
{
public abstract class BaseNetConnectorNodeGroup<TNetConnector, TNetType> : BaseNodeGroup where TNetConnector : BaseNetConnectorComponent<TNetType>
{
private readonly Dictionary<Node, List<TNetConnector>> _netConnectorComponents = new();
protected override void OnAddNode(Node node)
{
var newNetConnectorComponents = node.Owner
.GetAllComponents<TNetConnector>()
.Where(powerComp => (NodeGroupID) powerComp.Voltage == node.NodeGroupID)
.ToList();
_netConnectorComponents[node] = newNetConnectorComponents;
foreach (var netConnectorComponent in newNetConnectorComponents)
{
SetNetConnectorNet(netConnectorComponent);
}
}
protected abstract void SetNetConnectorNet(TNetConnector netConnectorComponent);
protected override void OnRemoveNode(Node node)
{
foreach (var netConnectorComponent in _netConnectorComponents[node])
{
netConnectorComponent.ClearNet();
}
_netConnectorComponents.Remove(node);
}
}
}

View File

@@ -0,0 +1,91 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.ViewVariables;
namespace Content.Server.NodeContainer.NodeGroups
{
/// <summary>
/// Maintains a collection of <see cref="Node"/>s, and performs operations requiring a list of
/// all connected <see cref="Node"/>s.
/// </summary>
public interface INodeGroup
{
bool Remaking { get; }
IReadOnlyList<Node> Nodes { get; }
void Create(NodeGroupID groupId);
void Initialize(Node sourceNode);
void RemoveNode(Node node);
void LoadNodes(List<Node> groupNodes);
// In theory, the SS13 curse ensures this method will never be called.
void AfterRemake(IEnumerable<IGrouping<INodeGroup?, Node>> newGroups);
// TODO: Why is this method needed?
void QueueRemake();
}
[NodeGroup(NodeGroupID.Default, NodeGroupID.WireNet)]
public class BaseNodeGroup : INodeGroup
{
public bool Remaking { get; set; }
IReadOnlyList<Node> INodeGroup.Nodes => Nodes;
[ViewVariables] public readonly List<Node> Nodes = new();
[ViewVariables] public int NodeCount => Nodes.Count;
/// <summary>
/// Debug variable to indicate that this NodeGroup should not be being used by anything.
/// </summary>
[ViewVariables]
public bool Removed { get; set; } = false;
[ViewVariables]
protected GridId GridId { get; private set; }
[ViewVariables]
public int NetId;
[ViewVariables]
public NodeGroupID GroupId { get; private set; }
public void Create(NodeGroupID groupId)
{
GroupId = groupId;
}
public virtual void Initialize(Node sourceNode)
{
// TODO: Can we get rid of this GridId?
GridId = sourceNode.Owner.Transform.GridID;
}
public virtual void RemoveNode(Node node)
{
}
public virtual void LoadNodes(
List<Node> groupNodes)
{
Nodes.AddRange(groupNodes);
}
public virtual void AfterRemake(IEnumerable<IGrouping<INodeGroup?, Node>> newGroups) { }
public void QueueRemake()
{
EntitySystem.Get<NodeGroupSystem>().QueueRemakeGroup(this);
}
}
}

View File

@@ -1,132 +0,0 @@
#nullable enable
using System.Collections.Generic;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.ViewVariables;
namespace Content.Server.NodeContainer.NodeGroups
{
/// <summary>
/// Maintains a collection of <see cref="Node"/>s, and performs operations requiring a list of
/// all connected <see cref="Node"/>s.
/// </summary>
public interface INodeGroup
{
IReadOnlyList<Node> Nodes { get; }
void Initialize(Node sourceNode);
void AddNode(Node node);
void RemoveNode(Node node);
void CombineGroup(INodeGroup newGroup);
void RemakeGroup();
}
[NodeGroup(NodeGroupID.Default, NodeGroupID.WireNet)]
public class BaseNodeGroup : INodeGroup
{
[ViewVariables]
public IReadOnlyList<Node> Nodes => _nodes;
private readonly List<Node> _nodes = new();
[ViewVariables]
public int NodeCount => Nodes.Count;
/// <summary>
/// Debug variable to indicate that this NodeGroup should not be being used by anything.
/// </summary>
[ViewVariables]
public bool Removed { get; private set; } = false;
public static readonly INodeGroup NullGroup = new NullNodeGroup();
protected GridId GridId { get; private set;}
public virtual void Initialize(Node sourceNode)
{
GridId = sourceNode.Owner.Transform.GridID;
}
public void AddNode(Node node)
{
_nodes.Add(node);
OnAddNode(node);
}
public void RemoveNode(Node node)
{
_nodes.Remove(node);
OnRemoveNode(node);
EntitySystem.Get<NodeGroupSystem>().AddDirtyNodeGroup(this);
}
public void CombineGroup(INodeGroup newGroup)
{
if (newGroup.Nodes.Count < Nodes.Count)
{
newGroup.CombineGroup(this);
return;
}
OnGivingNodesForCombine(newGroup);
foreach (var node in Nodes)
{
node.NodeGroup = newGroup;
}
Removed = true;
}
/// <summary>
/// Causes all <see cref="Node"/>s to remake their groups. Called when a <see cref="Node"/> is removed
/// and may have split a group in two, so multiple new groups may need to be formed.
/// </summary>
public void RemakeGroup()
{
foreach (var node in Nodes)
{
node.ClearNodeGroup();
}
var newGroups = new HashSet<INodeGroup>();
foreach (var node in Nodes)
{
if (node.TryAssignGroupIfNeeded())
{
node.SpreadGroup();
newGroups.Add(node.NodeGroup);
}
}
AfterRemake(newGroups);
Removed = true;
}
protected virtual void OnAddNode(Node node) { }
protected virtual void OnRemoveNode(Node node) { }
protected virtual void OnGivingNodesForCombine(INodeGroup newGroup) { }
protected virtual void AfterRemake(IEnumerable<INodeGroup> newGroups) { }
protected class NullNodeGroup : INodeGroup
{
public IReadOnlyList<Node> Nodes => _nodes;
private readonly List<Node> _nodes = new();
public void Initialize(Node sourceNode) { }
public void AddNode(Node node) { }
public void CombineGroup(INodeGroup newGroup) { }
public void RemoveNode(Node node) { }
public void RemakeGroup() { }
}
}
}

View File

@@ -1,5 +1,6 @@
#nullable enable
using System;
using JetBrains.Annotations;
namespace Content.Server.NodeContainer.NodeGroups
{
@@ -9,6 +10,7 @@ namespace Content.Server.NodeContainer.NodeGroups
/// have the same type of <see cref="INodeGroup"/>. Used by <see cref="INodeGroupFactory"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
[MeansImplicitUse]
public class NodeGroupAttribute : Attribute
{
public NodeGroupID[] NodeGroupIDs { get; }

View File

@@ -19,7 +19,7 @@ namespace Content.Server.NodeContainer.NodeGroups
/// <summary>
/// Returns a new <see cref="INodeGroup"/> instance.
/// </summary>
INodeGroup MakeNodeGroup(Node sourceNode);
INodeGroup MakeNodeGroup(NodeGroupID id);
}
public class NodeGroupFactory : INodeGroupFactory
@@ -45,15 +45,14 @@ namespace Content.Server.NodeContainer.NodeGroups
}
}
public INodeGroup MakeNodeGroup(Node sourceNode)
public INodeGroup MakeNodeGroup(NodeGroupID id)
{
if (_groupTypes.TryGetValue(sourceNode.NodeGroupID, out var type))
{
var nodeGroup = _typeFactory.CreateInstance<INodeGroup>(type);
nodeGroup.Initialize(sourceNode);
return nodeGroup;
}
throw new ArgumentException($"{sourceNode.NodeGroupID} did not have an associated {nameof(INodeGroup)}.");
if (!_groupTypes.TryGetValue(id, out var type))
throw new ArgumentException($"{id} did not have an associated {nameof(INodeGroup)} implementation.");
var instance = _typeFactory.CreateInstance<INodeGroup>(type);
instance.Create(id);
return instance;
}
}

View File

@@ -1,14 +1,15 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Atmos;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.Interfaces;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Content.Server.NodeContainer.NodeGroups
@@ -24,17 +25,15 @@ namespace Content.Server.NodeContainer.NodeGroups
[NodeGroup(NodeGroupID.Pipe)]
public class PipeNet : BaseNodeGroup, IPipeNet
{
[ViewVariables]
public GasMixture Air { get; set; } = new() {Temperature = Atmospherics.T20C};
[ViewVariables] public GasMixture Air { get; set; } = new() {Temperature = Atmospherics.T20C};
public static readonly IPipeNet NullNet = new NullPipeNet();
[ViewVariables]
private readonly List<PipeNode> _pipes = new();
[ViewVariables] private readonly List<PipeNode> _pipes = new();
[ViewVariables] private AtmosphereSystem? _atmosphereSystem;
[ViewVariables] private IGridAtmosphereComponent? GridAtmos => _atmosphereSystem?.GetGridAtmosphere(GridId);
[ViewVariables]
private IGridAtmosphereComponent? GridAtmos =>
_atmosphereSystem?.GetGridAtmosphere(GridId);
public override void Initialize(Node sourceNode)
{
@@ -49,35 +48,30 @@ namespace Content.Server.NodeContainer.NodeGroups
_atmosphereSystem?.React(Air, this);
}
protected override void OnAddNode(Node node)
public override void LoadNodes(List<Node> groupNodes)
{
if (node is not PipeNode pipeNode)
return;
base.LoadNodes(groupNodes);
_pipes.Add(pipeNode);
pipeNode.JoinPipeNet(this);
Air.Volume += pipeNode.Volume;
foreach (var node in groupNodes)
{
var pipeNode = (PipeNode) node;
_pipes.Add(pipeNode);
pipeNode.JoinPipeNet(this);
Air.Volume += pipeNode.Volume;
}
}
protected override void OnRemoveNode(Node node)
public override void RemoveNode(Node node)
{
RemoveFromGridAtmos();
if (node is not PipeNode pipeNode)
return;
base.RemoveNode(node);
pipeNode.ClearPipeNet();
var pipeNode = (PipeNode) node;
Air.Volume -= pipeNode.Volume;
// TODO: Bad O(n^2)
_pipes.Remove(pipeNode);
}
protected override void OnGivingNodesForCombine(INodeGroup newGroup)
{
if (newGroup is not IPipeNet newPipeNet)
return;
EntitySystem.Get<AtmosphereSystem>().Merge(newPipeNet.Air, Air);
}
protected override void AfterRemake(IEnumerable<INodeGroup> newGroups)
public override void AfterRemake(IEnumerable<IGrouping<INodeGroup?, Node>> newGroups)
{
RemoveFromGridAtmos();
@@ -86,14 +80,15 @@ namespace Content.Server.NodeContainer.NodeGroups
foreach (var newGroup in newGroups)
{
if (newGroup is not IPipeNet newPipeNet)
if (newGroup.Key is not IPipeNet newPipeNet)
continue;
var newAir = newPipeNet.Air;
var newVolume = newGroup.Cast<PipeNode>().Sum(n => n.Volume);
buffer.Clear();
atmosphereSystem.Merge(buffer, Air);
buffer.Multiply(MathF.Min(newAir.Volume / Air.Volume, 1f));
buffer.Multiply(MathF.Min(newVolume / Air.Volume, 1f));
atmosphereSystem.Merge(newAir, buffer);
}
}
@@ -102,20 +97,5 @@ namespace Content.Server.NodeContainer.NodeGroups
{
GridAtmos?.RemovePipeNet(this);
}
private class NullPipeNet : NullNodeGroup, IPipeNet
{
private readonly GasMixture _air;
GasMixture IGasMixtureHolder.Air { get => _air; set { } }
public NullPipeNet()
{
_air = new GasMixture(1f) {Temperature = Atmospherics.T20C};
_air.MarkImmutable();
}
public void Update() { }
}
}
}

View File

@@ -1,164 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Content.Server.Power.Components;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.NodeContainer.NodeGroups
{
public interface IPowerNet
{
void AddSupplier(PowerSupplierComponent supplier);
void RemoveSupplier(PowerSupplierComponent supplier);
void UpdateSupplierSupply(PowerSupplierComponent supplier, int oldSupplyRate, int newSupplyRate);
void AddConsumer(PowerConsumerComponent consumer);
void RemoveConsumer(PowerConsumerComponent consumer);
void UpdateConsumerDraw(PowerConsumerComponent consumer, int oldDrawRate, int newDrawRate);
void UpdateConsumerPriority(PowerConsumerComponent consumer, Priority oldPriority, Priority newPriority);
void UpdateConsumerReceivedPower();
}
[NodeGroup(NodeGroupID.HVPower, NodeGroupID.MVPower)]
public class PowerNetNodeGroup : BaseNetConnectorNodeGroup<BasePowerNetComponent, IPowerNet>, IPowerNet
{
private static readonly Priority[] CachedPriorities = (Priority[]) Enum.GetValues(typeof(Priority));
[Dependency] private readonly IPowerNetManager _powerNetManager = default!;
[ViewVariables]
private readonly List<PowerSupplierComponent> _suppliers = new();
[ViewVariables]
private int _totalSupply = 0;
[ViewVariables]
private readonly Dictionary<Priority, List<PowerConsumerComponent>> _consumersByPriority = new();
[ViewVariables]
private readonly Dictionary<Priority, int> _drawByPriority = new();
public static readonly IPowerNet NullNet = new NullPowerNet();
public PowerNetNodeGroup()
{
foreach (Priority priority in Enum.GetValues(typeof(Priority)))
{
_consumersByPriority.Add(priority, new List<PowerConsumerComponent>());
_drawByPriority.Add(priority, 0);
}
}
protected override void SetNetConnectorNet(BasePowerNetComponent netConnectorComponent)
{
netConnectorComponent.Net = this;
}
#region IPowerNet Methods
public void AddSupplier(PowerSupplierComponent supplier)
{
_suppliers.Add(supplier);
_totalSupply += supplier.SupplyRate;
_powerNetManager.AddDirtyPowerNet(this);
}
public void RemoveSupplier(PowerSupplierComponent supplier)
{
Debug.Assert(_suppliers.Contains(supplier));
_suppliers.Remove(supplier);
_totalSupply -= supplier.SupplyRate;
_powerNetManager.AddDirtyPowerNet(this);
}
public void UpdateSupplierSupply(PowerSupplierComponent supplier, int oldSupplyRate, int newSupplyRate)
{
Debug.Assert(_suppliers.Contains(supplier));
_totalSupply -= oldSupplyRate;
_totalSupply += newSupplyRate;
_powerNetManager.AddDirtyPowerNet(this);
}
public void AddConsumer(PowerConsumerComponent consumer)
{
_consumersByPriority[consumer.Priority].Add(consumer);
_drawByPriority[consumer.Priority] += consumer.DrawRate;
_powerNetManager.AddDirtyPowerNet(this);
}
public void RemoveConsumer(PowerConsumerComponent consumer)
{
Debug.Assert(_consumersByPriority[consumer.Priority].Contains(consumer));
consumer.ReceivedPower = 0;
_consumersByPriority[consumer.Priority].Remove(consumer);
_drawByPriority[consumer.Priority] -= consumer.DrawRate;
_powerNetManager.AddDirtyPowerNet(this);
}
public void UpdateConsumerDraw(PowerConsumerComponent consumer, int oldDrawRate, int newDrawRate)
{
Debug.Assert(_consumersByPriority[consumer.Priority].Contains(consumer));
_drawByPriority[consumer.Priority] -= oldDrawRate;
_drawByPriority[consumer.Priority] += newDrawRate;
_powerNetManager.AddDirtyPowerNet(this);
}
public void UpdateConsumerPriority(PowerConsumerComponent consumer, Priority oldPriority, Priority newPriority)
{
Debug.Assert(_consumersByPriority[oldPriority].Contains(consumer));
_consumersByPriority[oldPriority].Remove(consumer);
_drawByPriority[oldPriority] -= consumer.DrawRate;
_consumersByPriority[newPriority].Add(consumer);
_drawByPriority[newPriority] += consumer.DrawRate;
_powerNetManager.AddDirtyPowerNet(this);
}
public void UpdateConsumerReceivedPower()
{
var remainingSupply = _totalSupply;
foreach (var priority in CachedPriorities)
{
var categoryPowerDemand = _drawByPriority[priority];
if (remainingSupply >= categoryPowerDemand) //can fully power all in category
{
remainingSupply -= categoryPowerDemand;
foreach (var consumer in _consumersByPriority[priority])
{
consumer.ReceivedPower = consumer.DrawRate;
}
}
else //cannot fully power all, split power
{
var availiablePowerFraction = (float) remainingSupply / categoryPowerDemand;
remainingSupply = 0;
foreach (var consumer in _consumersByPriority[priority])
{
consumer.ReceivedPower = (int) (consumer.DrawRate * availiablePowerFraction); //give each consumer a fraction of what they requested (rounded down to nearest int)
}
}
}
}
#endregion
private class NullPowerNet : IPowerNet
{
public void AddConsumer(PowerConsumerComponent consumer) { }
public void AddSupplier(PowerSupplierComponent supplier) { }
public void UpdateSupplierSupply(PowerSupplierComponent supplier, int oldSupplyRate, int newSupplyRate) { }
public void RemoveConsumer(PowerConsumerComponent consumer) { }
public void RemoveSupplier(PowerSupplierComponent supplier) { }
public void UpdateConsumerDraw(PowerConsumerComponent consumer, int oldDrawRate, int newDrawRate) { }
public void UpdateConsumerPriority(PowerConsumerComponent consumer, Priority oldPriority, Priority newPriority) { }
public void UpdateConsumerReceivedPower() { }
}
}
}