Relay handling

master
D4VID 7 months ago
parent 66a3704ee4
commit 78e4f01b57

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Phasic/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

@ -1,7 +1,7 @@
ID: CriticalPathAnalyzer ID: CriticalPathAnalyzer
Name: CriticalPathAnalyzer Name: CriticalPathAnalyzer
Author: D4VID Author: D4VID
Version: 0.0.3 Version: 0.0.4
Priority: 0 Priority: 0
Dependencies: Dependencies:
- HarmonyForLogicWorld - HarmonyForLogicWorld

@ -9,7 +9,6 @@ using LogicWorld.Outlines;
namespace CriticalPathAnalyzer.Client.Tool { namespace CriticalPathAnalyzer.Client.Tool {
public class PathHighlighter { public class PathHighlighter {
private static List<ClusterDetails> _clusters = new List<ClusterDetails>(); private static List<ClusterDetails> _clusters = new List<ClusterDetails>();
private static List<ComponentAddress> _highlightedComponents = new List<ComponentAddress>();
private static List<WireAddress> _highlightedWires = new List<WireAddress>(); private static List<WireAddress> _highlightedWires = new List<WireAddress>();
public static void HighlightWires(List<ClusterDetails> clusters) { public static void HighlightWires(List<ClusterDetails> clusters) {
@ -18,22 +17,6 @@ namespace CriticalPathAnalyzer.Client.Tool {
foreach (ClusterDetails clusterDetails in _clusters) { foreach (ClusterDetails clusterDetails in _clusters) {
HighlightCluster(clusterDetails); HighlightCluster(clusterDetails);
} }
// IWorldData world = Instances.MainWorld.Data;
// foreach (ComponentAddress componentAddress in response.PerimeterComponents) {
// if (!world.Contains(componentAddress)) {
// continue;
// }
//
// Outliner.Outline(componentAddress, PerimeterColor);
// _highlightedComponents.Add(componentAddress);
// }
// _clusters.AddRange(response.NextClusters); // add to the collection to unhighlight
// foreach (ClusterDetails clusterDetails in response.NextClusters) {
// HighlightCluster(clusterDetails, NextColor);
// }
} }
/// <summary> /// <summary>
@ -52,14 +35,6 @@ namespace CriticalPathAnalyzer.Client.Tool {
Outliner.Outline(address, outline); Outliner.Outline(address, outline);
} }
foreach (ComponentAddress address in cluster.LinkingComponents) {
if (!world.Contains(address)) {
continue;
}
Outliner.Outline(address, outline);
}
foreach (PegAddress pegAddress in cluster.Pegs) { foreach (PegAddress pegAddress in cluster.Pegs) {
if (!world.Contains(pegAddress.ComponentAddress)) { if (!world.Contains(pegAddress.ComponentAddress)) {
continue; continue;
@ -100,13 +75,8 @@ namespace CriticalPathAnalyzer.Client.Tool {
Outliner.RemoveOutline(wireAddress); Outliner.RemoveOutline(wireAddress);
} }
foreach (ComponentAddress componentAddress in _highlightedComponents) {
Outliner.RemoveOutline(componentAddress);
}
_clusters = null; _clusters = null;
_highlightedWires.Clear(); _highlightedWires.Clear();
_highlightedComponents.Clear();
} }
private static void UnhighlightCluster(ClusterDetails cluster) { private static void UnhighlightCluster(ClusterDetails cluster) {
@ -117,10 +87,6 @@ namespace CriticalPathAnalyzer.Client.Tool {
foreach (ComponentAddress address in cluster.ConnectingComponents) { foreach (ComponentAddress address in cluster.ConnectingComponents) {
Outliner.RemoveOutline(address); Outliner.RemoveOutline(address);
} }
foreach (ComponentAddress address in cluster.LinkingComponents) {
Outliner.RemoveOutline(address);
}
} }
} }
} }

@ -8,15 +8,13 @@ using LogicAPI.Data;
using LogicAPI.Server.Components; using LogicAPI.Server.Components;
using LogicAPI.Services; using LogicAPI.Services;
using LogicLog; using LogicLog;
using LogicWorld.LogicCode;
using LogicWorld.Server.Circuitry; using LogicWorld.Server.Circuitry;
namespace CriticalPathAnalyzer.Server.Tool { namespace CriticalPathAnalyzer.Server.Tool {
public class ServerPathTracer { public class ServerPathTracer {
// Reflection/Delegate access helpers: // Reflection/Delegate access helpers:
private static readonly Func<InputPeg, Cluster> GetCluster; private static readonly Func<InputPeg, Cluster> GetCluster;
private static readonly Func<Cluster, ClusterLinker> GetLinker;
private static readonly Func<ClusterLinker, List<ClusterLinker>> GetFollowers;
// Services needed to lookup wires/pegs: // Services needed to lookup wires/pegs:
private static readonly ICircuitryManager Circuits; private static readonly ICircuitryManager Circuits;
@ -26,12 +24,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
GetCluster = Delegator.createPropertyGetter<InputPeg, Cluster>( GetCluster = Delegator.createPropertyGetter<InputPeg, Cluster>(
Properties.getPrivate(typeof(InputPeg), "Cluster") Properties.getPrivate(typeof(InputPeg), "Cluster")
); );
GetLinker = Delegator.createFieldGetter<Cluster, ClusterLinker>(
Fields.getPrivate(typeof(Cluster), "Linker")
);
GetFollowers = Delegator.createFieldGetter<ClusterLinker, List<ClusterLinker>>(
Fields.getPrivate(typeof(ClusterLinker), "LinkedFollowers")
);
Circuits = ServiceGetter.getService<ICircuitryManager>(); Circuits = ServiceGetter.getService<ICircuitryManager>();
World = ServiceGetter.getService<IWorldData>(); World = ServiceGetter.getService<IWorldData>();
@ -62,10 +54,10 @@ namespace CriticalPathAnalyzer.Server.Tool {
// An output peg however can be connected to multiple clusters. // An output peg however can be connected to multiple clusters.
// It only makes sense to then select all these clusters as primary cluster. // It only makes sense to then select all these clusters as primary cluster.
var startingClusters = new HashSet<Cluster>(); var startingClusters = new HashSet<Cluster>();
CollectMainClusters(start, startingClusters); CollectPegClusters(start, startingClusters);
var endingClusters = new HashSet<Cluster>(); var endingClusters = new HashSet<Cluster>();
CollectMainClusters(end, endingClusters); CollectPegClusters(end, endingClusters);
foreach (Cluster startingCluster in startingClusters) { foreach (Cluster startingCluster in startingClusters) {
// ignore already mapped clusters // ignore already mapped clusters
@ -204,35 +196,95 @@ namespace CriticalPathAnalyzer.Server.Tool {
// var perimeterComponents = new HashSet<ComponentAddress>(); // var perimeterComponents = new HashSet<ComponentAddress>();
var collectedNextClusters = new HashSet<Cluster>(); var collectedNextClusters = new HashSet<Cluster>();
var collectedNextInstantClusters = new HashSet<Cluster>();
foreach (Cluster cluster in node.Clusters) { foreach (Cluster cluster in node.Clusters) {
foreach (InputPeg inputPeg in cluster.ConnectedInputs) { foreach (InputPeg inputPeg in cluster.ConnectedInputs) {
if (inputPeg.LogicComponent == null) { LogicComponent component = inputPeg.LogicComponent;
if (component == null) {
// These are regular pegs, they are not attached to any logic component, skip them
continue;
}
if (component.GetType() == typeof(Delayer)) {
// Special handling of the delayer
var delayer = (Delayer) component;
//TODO: delayer.Data.DelayLengthInTicks;
CollectPegClusters(delayer.Outputs[0].Address, collectedNextClusters);
} else if (component.GetType() == typeof(Relay)) {
// Collect all next clusters connected via a relay (from the control input side)
if (inputPeg.Address == component.Inputs[0].Address) {
// the top control input
CollectPegClusters(component.Inputs[1].Address, collectedNextClusters);
CollectPegClusters(component.Inputs[2].Address, collectedNextClusters);
}
} else {
// Collect all next cluster connected via normal components
foreach (IOutputPeg outputPeg in component.Outputs) {
CollectPegClusters(outputPeg.Address, collectedNextClusters);
}
// Collect all next clusters connected via fast buffers
if (inputPeg.OneWayPhasicLinksFollowers != null) {
foreach (InputPeg followerPeg in inputPeg.OneWayPhasicLinksFollowers) {
CollectPegClusters(followerPeg.Address, collectedNextInstantClusters);
}
}
}
}
}
LinkNextClusters(queue, clusterToNodeMapping, clusterNodes, collectedNextInstantClusters, node, 0);
LinkNextClusters(queue, clusterToNodeMapping, clusterNodes, collectedNextClusters, node, 1);
}
logger.Info($"Finished after {iterations} iterations");
}
private static HashSet<Cluster> GetClustersConnectedThroughRelays(Cluster cluster) {
var clusters = new HashSet<Cluster>() {cluster};
foreach (InputPeg inputPeg in cluster.ConnectedInputs) {
LogicComponent component = inputPeg.LogicComponent;
if (component == null) {
// These are regular pegs, they are not attached to any logic component, skip them // These are regular pegs, they are not attached to any logic component, skip them
continue; continue;
} }
// perimeterComponents.Add(inputPeg.LogicComponent.Address); if (component.GetType() == typeof(Relay)) {
foreach (IOutputPeg outputPeg in inputPeg.LogicComponent.Outputs) { // Collect all next clusters connected via a relay (from the control input side)
CollectMainClusters(outputPeg.Address, collectedNextClusters); if (inputPeg.Address == component.Inputs[1].Address) {
// one of the sides
CollectPegClusters(component.Inputs[2].Address, clusters);
} else if (inputPeg.Address == component.Inputs[2].Address) {
// the other side
CollectPegClusters(component.Inputs[1].Address, clusters);
} }
} }
} }
return clusters;
}
private static void LinkNextClusters(Queue<ClusterNode> queue, Dictionary<Cluster, int> clusterToNodeMapping,
List<ClusterNode> clusterNodes, HashSet<Cluster> collectedNextClusters, ClusterNode node, int delay) {
foreach (Cluster nextCluster in collectedNextClusters) { foreach (Cluster nextCluster in collectedNextClusters) {
// ignore already mapped clusters // ignore already mapped clusters
if (clusterToNodeMapping.TryGetValue(nextCluster, out int nextNodeIndex)) { if (clusterToNodeMapping.TryGetValue(nextCluster, out int nextNodeIndex)) {
ClusterNode nextNode = clusterNodes[nextNodeIndex]; ClusterNode nextNode = clusterNodes[nextNodeIndex];
// only add the link, don't propagate // only add the link, don't propagate
nextNode.PrevNodeIndexes.Add(node.Index); nextNode.PrevNodeIndexes.Add(node.Index);
node.NextNodes.TryAdd(nextNodeIndex, 1); node.NextNodes.TryAdd(nextNodeIndex, delay);
} else { } else {
// var nextClusters = new HashSet<Cluster>(); HashSet<Cluster> nextClusters = GetClustersConnectedThroughRelays(nextCluster);
// var twoWayConnectedClusters = new HashSet<Cluster>();
// GetLinkedClusters(cluster, nextClusters, twoWayConnectedClusters, GetFollowers); if (nextClusters.Any(clusterToNodeMapping.ContainsKey)) {
// already discovered cluster node
continue;
}
nextNodeIndex = clusterNodes.Count; nextNodeIndex = clusterNodes.Count;
var nextNode = new ClusterNode() { var nextNode = new ClusterNode() {
Index = nextNodeIndex, Index = nextNodeIndex,
Clusters = new HashSet<Cluster>() {nextCluster}, Clusters = nextClusters,
Time = 0, Time = 0,
NextNodes = new Dictionary<int, int>(), NextNodes = new Dictionary<int, int>(),
PrevNodeIndexes = new HashSet<int>() {node.Index}, PrevNodeIndexes = new HashSet<int>() {node.Index},
@ -240,10 +292,10 @@ namespace CriticalPathAnalyzer.Server.Tool {
clusterNodes.Add(nextNode); clusterNodes.Add(nextNode);
node.NextNodes.TryAdd(nextNodeIndex, 1); node.NextNodes.TryAdd(nextNodeIndex, delay);
foreach (Cluster nextNodeCluster in nextNode.Clusters) { foreach (Cluster nextNodeCluster in nextNode.Clusters) {
clusterToNodeMapping.Add(nextNodeCluster, nextNodeIndex); clusterToNodeMapping.TryAdd(nextNodeCluster, nextNodeIndex);
} }
// propagate // propagate
@ -252,9 +304,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
} }
} }
logger.Info($"Finished after {iterations} iterations");
}
/// <summary> /// <summary>
/// Check if the peg exists in the world /// Check if the peg exists in the world
@ -288,18 +337,13 @@ namespace CriticalPathAnalyzer.Server.Tool {
return cluster; return cluster;
} }
private static bool GetLinkerAt(Cluster cluster, out ClusterLinker linker) {
linker = GetLinker(cluster);
return linker != null;
}
/// <summary> /// <summary>
/// An output peg can be connected to multiple clusters. Collect all of them. /// An output peg can be connected to multiple clusters. Collect all of them.
/// </summary> /// </summary>
/// <param name="pegAddress">Address of the peg</param> /// <param name="pegAddress">Address of the peg</param>
/// <param name="primaryClusters">Found clusters get added into this hash set</param> /// <param name="primaryClusters">Found clusters get added into this hash set</param>
/// <exception cref="Exception">A wire of the peg is not in the world</exception> /// <exception cref="Exception">A wire of the peg is not in the world</exception>
private static void CollectMainClusters(PegAddress pegAddress, HashSet<Cluster> primaryClusters) { private static void CollectPegClusters(PegAddress pegAddress, HashSet<Cluster> primaryClusters) {
if (pegAddress.IsInputAddress(out InputAddress inputAddress)) { if (pegAddress.IsInputAddress(out InputAddress inputAddress)) {
primaryClusters.Add(GetClusterAt(inputAddress)); primaryClusters.Add(GetClusterAt(inputAddress));
} else { } else {
@ -325,48 +369,11 @@ namespace CriticalPathAnalyzer.Server.Tool {
} }
} }
private static void GetLinkedClusters(
Cluster startingPoint,
HashSet<Cluster> oneWayConnectedClusters,
HashSet<Cluster> twoWayConnectedClusters,
Func<ClusterLinker, List<ClusterLinker>> linkedLinkerGetter
) {
var clustersToProcess = new Queue<ClusterLinker>();
if (!GetLinkerAt(startingPoint, out ClusterLinker startingLinker)) {
return; // No linker on this cluster => no link => nothing to collect
}
clustersToProcess.Enqueue(startingLinker);
twoWayConnectedClusters.Add(startingPoint);
// While the starting cluster is no source, the algorithm needs to skip it when encountered.
while (clustersToProcess.TryDequeue(out ClusterLinker linkerToCheck)) {
List<ClusterLinker> listOfLinkedLinkers = linkedLinkerGetter(linkerToCheck); // Is never null.
foreach (ClusterLinker linkedLinker in listOfLinkedLinkers) {
Cluster clusterOfLinkedLinker = linkedLinker.ClusterBeingLinked; // Should never be null.
List<ClusterLinker> listOfBackLinkers = linkedLinkerGetter(linkedLinker); // Is never null.
if (listOfBackLinkers.Contains(linkerToCheck)) {
// bidirectional - a relay
// TODO: will not find a relay connection if the relay is turned off
if (twoWayConnectedClusters.Add(clusterOfLinkedLinker)) {
// Element was not yet present in the array, so keep looking into it!
clustersToProcess.Enqueue(linkedLinker);
}
} else {
// unidirectional - a fast buffer
oneWayConnectedClusters.Add(clusterOfLinkedLinker);
// stop propagation
}
}
}
twoWayConnectedClusters.Remove(startingPoint);
}
private static ClusterDetails CollectClusterInformation(Cluster cluster, int time) { private static ClusterDetails CollectClusterInformation(Cluster cluster, int time) {
var details = new ClusterDetails { var details = new ClusterDetails {
Pegs = new List<PegAddress>(), Pegs = new List<PegAddress>(),
ConnectingComponents = new List<ComponentAddress>(), ConnectingComponents = new List<ComponentAddress>(),
LinkingComponents = new List<ComponentAddress>(),
Color = HsvToRgb(time * 20, 1, 1), Color = HsvToRgb(time * 20, 1, 1),
}; };
@ -380,14 +387,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
// Socket // Socket
details.ConnectingComponents.Add(peg.Address.ComponentAddress); details.ConnectingComponents.Add(peg.Address.ComponentAddress);
} }
if ((peg.PhasicLinks != null && peg.PhasicLinks.Any())
|| (peg.OneWayPhasicLinksFollowers != null && peg.OneWayPhasicLinksFollowers.Any())
|| (peg.OneWayPhasicLinksLeaders != null && peg.OneWayPhasicLinksLeaders.Any())
) {
// Relay / fast buffer
details.LinkingComponents.Add(peg.Address.ComponentAddress);
}
} }
foreach (OutputPeg peg in outputPegs) { foreach (OutputPeg peg in outputPegs) {

@ -27,11 +27,6 @@ namespace CriticalPathAnalyzer.Shared.Packets.S2C {
/// </summary> /// </summary>
[Key(1)] public List<ComponentAddress> ConnectingComponents; [Key(1)] public List<ComponentAddress> ConnectingComponents;
/// <summary>
/// Relays and fast buffers
/// </summary>
[Key(2)] public List<ComponentAddress> LinkingComponents;
[Key(3)] public int Color; [Key(3)] public int Color;
} }
} }
Loading…
Cancel
Save