diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj b/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj
index f63a892..42d8301 100644
--- a/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj
@@ -14,6 +14,9 @@
..\LogicWorld\Logic_World_Data\Managed\FancyInput.dll
+
+ ..\LogicWorld\Logic_World_Data\Managed\JimmysUnityUtilities.dll
+
..\LogicWorld\Logic_World_Data\Managed\LogicAPI.dll
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs
index 36526ee..4ecad61 100644
--- a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs
@@ -33,7 +33,21 @@ namespace CriticalPathAnalyzer.Client {
}
public static void OnAnalyzePathResponse(AnalyzePathResponse response) {
- LoggerInstance.Info($"Got response from server: {response.Message}");
+ LoggerInstance.Info($"Got response from server");
+ PathHighLighter.HighlightWires(response);
+ // if(!CurrentRequestID.HasValue || response.requestGuid != currentRequestID.Value)
+ // {
+ // //Not matching Guid, old or wrong request, discard.
+ // return;
+ // }
+ // currentRequestID = null; //Received response, clear GUID.
+ //
+ // //Clear up all data immediately:
+ // if(currentTracer != null)
+ // {
+ // currentTracer.stop();
+ // currentTracer = null;
+ // }
}
}
}
\ No newline at end of file
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerGameState.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerGameState.cs
index cbfa027..a859192 100644
--- a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerGameState.cs
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerGameState.cs
@@ -34,6 +34,7 @@ namespace CriticalPathAnalyzer.Client {
public override void OnExit() {
CriticalPathAnalyzerClient.LoggerInstance.Info("CPA exit");
+ PathHighLighter.RemoveHighLighting();
}
}
}
\ No newline at end of file
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/CriticalPathAnalyzerTool.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/CriticalPathAnalyzerTool.cs
index 42bb191..8f945d8 100644
--- a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/CriticalPathAnalyzerTool.cs
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/CriticalPathAnalyzerTool.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using CriticalPathAnalyzer.Shared.Packets.C2S;
using LogicAPI.Data;
using LogicLog;
@@ -29,12 +28,12 @@ namespace CriticalPathAnalyzer.Client.Tool {
// Resolve hit target:
if (hitInfo.HitPeg) {
- CriticalPathAnalyzerClient.LoggerInstance.Info("Hit peg");
+ _logger.Info("Hit peg");
return _startPegAddress = hitInfo.pAddress;
}
if (hitInfo.HitWire) {
- CriticalPathAnalyzerClient.LoggerInstance.Info("Hit wire");
+ _logger.Info("Hit wire");
WireAddress wireAddress = hitInfo.wAddress;
Wire wire = Instances.MainWorld.Data.Lookup(wireAddress);
// Assume that wire is never null, as we did just ray-casted it.
@@ -45,7 +44,7 @@ namespace CriticalPathAnalyzer.Client.Tool {
}
public static void SelectPathStart() {
- CriticalPathAnalyzerClient.LoggerInstance.Info("Analyze Path Start");
+ _logger.Info("Analyze Path Start");
PegAddress? pegAddress = RayCastPeg();
if (pegAddress != null) {
_startPegAddress = pegAddress.Value;
@@ -56,7 +55,7 @@ namespace CriticalPathAnalyzer.Client.Tool {
}
public static void SelectPathEnd() {
- CriticalPathAnalyzerClient.LoggerInstance.Info("Analyze Path End");
+ _logger.Info("Analyze Path End");
PegAddress? pegAddress = RayCastPeg();
if (pegAddress != null) {
_endPegAddress = pegAddress.Value;
@@ -67,35 +66,16 @@ namespace CriticalPathAnalyzer.Client.Tool {
}
private static void CalculateCriticalPath() {
+ if (_startPegAddress == null || _endPegAddress == null) {
+ _logger.Error("Invalid pegs");
+ return;
+ }
+
Instances.SendData.Send(new AnalyzePathRequest {
RequestGuid = Guid.NewGuid(),
StartPegAddress = _startPegAddress.Value,
EndPegAddress = _endPegAddress.Value,
});
- HashSet wires = Instances.MainWorld.Data.LookupPegWires(_startPegAddress.Value);
- _logger.Info($"Start peg has {wires.Count} wires");
-
- ComponentType andGate = Instances.MainWorld.ComponentTypes.GetComponentType("MHG.AndGate");
- ComponentType xorGate = Instances.MainWorld.ComponentTypes.GetComponentType("MHG.XorGate");
-
- foreach (WireAddress wireAddress in wires) {
- Wire wire = Instances.MainWorld.Data.Lookup(wireAddress);
- PegAddress nextPeg = wire.Point1 == _startPegAddress ? wire.Point2 : wire.Point1;
- if (nextPeg.PegType != PegType.Input) {
- // ignore
- continue;
- }
-
- IComponentInWorld component = Instances.MainWorld.Data.Lookup(nextPeg.ComponentAddress);
- ComponentType type = component.Data.Type;
- if (type == andGate) {
- _logger.Info($"Connected to AND gate");
- } else if (type == xorGate) {
- _logger.Info($"Connected to XOR gate");
- } else {
- _logger.Info($"Connected to unknown gate");
- }
- }
}
}
}
\ No newline at end of file
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/PathHighLighter.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/PathHighLighter.cs
new file mode 100644
index 0000000..f6e5f5b
--- /dev/null
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/PathHighLighter.cs
@@ -0,0 +1,111 @@
+using System.Collections.Generic;
+using CriticalPathAnalyzer.client.tool;
+using CriticalPathAnalyzer.Shared.Packets.S2C;
+using LogicAPI.Data;
+using LogicAPI.Services;
+using LogicWorld.Interfaces;
+using LogicWorld.Outlines;
+
+namespace CriticalPathAnalyzer.Client.Tool {
+ public class PathHighLighter {
+ private static List _clusters;
+
+ public static void HighlightWires(AnalyzePathResponse response) {
+ _clusters = response.SelectedClusters;
+
+ IWorldData world = Instances.MainWorld.Data;
+
+ foreach (ClusterDetails clusterDetails in _clusters) {
+ foreach (ComponentAddress address in clusterDetails.ConnectingComponents) {
+ if (!world.Contains(address)) {
+ continue;
+ }
+
+ Outliner.Outline(address, WireTracerColors.primaryConnected);
+ }
+
+ foreach (ComponentAddress address in clusterDetails.LinkingComponents) {
+ if (!world.Contains(address)) {
+ continue;
+ }
+
+ Outliner.Outline(address, WireTracerColors.linking);
+ }
+
+ clusterDetails.HighlightedWires = new List();
+ clusterDetails.HighlightedOutputWires = new List();
+
+ foreach (PegAddress pegAddress in clusterDetails.Pegs) {
+ if (!world.Contains(pegAddress.ComponentAddress)) {
+ continue;
+ }
+
+ if (!pegAddress.IsInputAddress()) {
+ Outliner.Outline(pegAddress, WireTracerColors.primaryOutput);
+ continue;
+ }
+
+ Outliner.Outline(pegAddress, WireTracerColors.primaryNormal);
+
+ HashSet wires = Instances.MainWorld.Data.LookupPegWires(pegAddress);
+ if (wires == null) {
+ continue;
+ }
+
+ foreach (WireAddress wireAddress in wires) {
+ Wire wire = Instances.MainWorld.Data.Lookup(wireAddress);
+ if (wire == null) {
+ return;
+ }
+
+ // We do not collect wires from output pegs. So if the first is an output peg, the other side must be an input -> collect.
+ if (wire.Point1 == pegAddress || !wire.Point1.IsInputAddress()) {
+ if (wire.Point1.IsInputAddress() && wire.Point2.IsInputAddress()) {
+ clusterDetails.HighlightedWires.Add(wireAddress);
+ } else {
+ clusterDetails.HighlightedOutputWires.Add(wireAddress);
+ }
+ }
+ }
+ }
+
+ foreach (WireAddress address in clusterDetails.HighlightedWires) {
+ Outliner.Outline(address, WireTracerColors.primaryNormal);
+ }
+
+ foreach (WireAddress address in clusterDetails.HighlightedOutputWires) {
+ Outliner.Outline(address, WireTracerColors.primaryOutput);
+ }
+ }
+ }
+
+ public static void RemoveHighLighting() {
+ if (_clusters == null) return;
+ foreach (ClusterDetails cluster in _clusters) {
+ UnhighlightCluster(cluster);
+ }
+ }
+
+ private static void UnhighlightCluster(ClusterDetails cluster) {
+ foreach (PegAddress address in cluster.Pegs) {
+ Outliner.RemoveOutline(address);
+ }
+
+ foreach (ComponentAddress address in cluster.ConnectingComponents) {
+ Outliner.RemoveOutline(address);
+ }
+
+ foreach (ComponentAddress address in cluster.LinkingComponents) {
+ Outliner.RemoveOutline(address);
+ }
+
+ foreach (WireAddress address in cluster.HighlightedWires) {
+ Outliner.RemoveOutline(address);
+ }
+
+ foreach (WireAddress address in cluster.HighlightedOutputWires) {
+ Outliner.RemoveOutline(address);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/WireTracerColors.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/WireTracerColors.cs
new file mode 100644
index 0000000..7faec00
--- /dev/null
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/WireTracerColors.cs
@@ -0,0 +1,34 @@
+using JimmysUnityUtilities;
+using LogicWorld.Outlines;
+
+namespace CriticalPathAnalyzer.client.tool
+{
+ public static class WireTracerColors
+ {
+ //All clusters:
+ //Linking color is the intersection between clusters, it does not make sense to have this once per cluster type.
+ // The only change that could be done is to give linking separators between two non-primary clusters a different color.
+ // That is currently not supported nor detected.
+ public static readonly OutlineData linking = new OutlineData(new Color24(200, 200, 200));
+
+ //Primary cluster:
+ public static readonly OutlineData primaryNormal = new OutlineData(new Color24( 50, 255, 50));
+ public static readonly OutlineData primaryConnected = new OutlineData(new Color24( 20, 150, 20));
+ public static readonly OutlineData primaryOutput = new OutlineData(new Color24( 50, 50, 255));
+
+ //Sourcing cluster:
+ public static readonly OutlineData sourcingNormal = new OutlineData(new Color24(255, 50, 255));
+ public static readonly OutlineData sourcingConnected = new OutlineData(new Color24(150, 20, 150));
+ public static readonly OutlineData sourcingOutput = new OutlineData(new Color24( 80, 0, 255));
+
+ //Connected cluster:
+ public static readonly OutlineData connectedNormal = new OutlineData(new Color24(255, 255, 50));
+ public static readonly OutlineData connectedConnected = new OutlineData(new Color24(150, 150, 20));
+ public static readonly OutlineData connectedOutput = new OutlineData(new Color24( 80, 80, 255));
+
+ //Draining cluster:
+ public static readonly OutlineData drainingNormal = new OutlineData(new Color24( 50, 255, 255));
+ public static readonly OutlineData drainingConnected = new OutlineData(new Color24( 20, 150, 150));
+ public static readonly OutlineData drainingOutput = new OutlineData(new Color24( 0, 50, 150));
+ }
+}
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/server/CriticalPathAnalyzerServer.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/server/CriticalPathAnalyzerServer.cs
index 6551dde..919dca3 100644
--- a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/server/CriticalPathAnalyzerServer.cs
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/server/CriticalPathAnalyzerServer.cs
@@ -16,6 +16,7 @@ using LogicWorld.Server;
using LogicWorld.SharedCode.Networking;
namespace CriticalPathAnalyzer.Server {
+ // ReSharper disable once ClassNeverInstantiated.Global
public class CriticalPathAnalyzerServer : ServerMod, IClientVerifier {
private const string ModId = "CriticalPathAnalyzer";
private const string ModVersion = "0.0.2";
@@ -57,12 +58,13 @@ namespace CriticalPathAnalyzer.Server {
}
}
- public void AnalyzePath(Connection sender, Guid packetRequestGuid, PegAddress start, PegAddress end) {
+ public void AnalyzePath(Connection sender, Guid requestGuid, PegAddress start, PegAddress end) {
LoggerInstance.Info("Got AnalyzePath request");
- AnalyzePathResponse response = new AnalyzePathResponse() {
- RequestGuid = packetRequestGuid,
- Message = "Lmao Yeet",
- };
+ if (!ServerPathTracer.TracePath(requestGuid, start, end, out AnalyzePathResponse response)) {
+ Logger.Error("Failed to trace path");
+ return;
+ }
+
_networkServer.Send(sender, response);
}
}
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/server/ServerPathTracer.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/server/ServerPathTracer.cs
new file mode 100644
index 0000000..3c5da03
--- /dev/null
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/server/ServerPathTracer.cs
@@ -0,0 +1,238 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using CriticalPathAnalyzer.Shared.Packets.S2C;
+using EccsLogicWorldAPI.Server;
+using EccsLogicWorldAPI.Shared.AccessHelper;
+using LogicAPI.Data;
+using LogicAPI.Services;
+using LogicWorld.Server.Circuitry;
+
+namespace CriticalPathAnalyzer.Server {
+ public class ServerPathTracer {
+ // Reflection/Delegate access helpers:
+ private static readonly Func GetCluster;
+ private static readonly Func GetLinker;
+ private static readonly Func> GetLeaders;
+
+ private static readonly Func> GetFollowers;
+
+ // Services needed to lookup wires/pegs:
+ private static readonly ICircuitryManager Circuits;
+ private static readonly IWorldData World;
+
+ static ServerPathTracer() {
+ GetCluster = Delegator.createPropertyGetter(
+ Properties.getPrivate(typeof(InputPeg), "Cluster")
+ );
+ GetLinker = Delegator.createFieldGetter(
+ Fields.getPrivate(typeof(Cluster), "Linker")
+ );
+ GetLeaders = Delegator.createFieldGetter>(
+ Fields.getPrivate(typeof(ClusterLinker), "LinkedLeaders")
+ );
+ GetFollowers = Delegator.createFieldGetter>(
+ Fields.getPrivate(typeof(ClusterLinker), "LinkedFollowers")
+ );
+
+ Circuits = ServiceGetter.getService();
+ World = ServiceGetter.getService();
+ }
+
+ public static bool TracePath(
+ Guid requestGuid,
+ PegAddress start,
+ PegAddress end,
+ out AnalyzePathResponse response
+ ) {
+ response = null;
+
+ CriticalPathAnalyzerServer.LoggerInstance.Info("Trace start");
+
+ // Validate, that the peg is actually existing:
+ if (!PegExists(start) || !PegExists(end)) {
+ CriticalPathAnalyzerServer.LoggerInstance.Error("Peg not found");
+ return false;
+ }
+
+ // An input peg, only has a single cluster.
+ // An output peg however can be connected to multiple clusters.
+ // It only makes sense to then select all these clusters as primary cluster.
+ if (!CollectMainClusters(start, out HashSet primaryClusters)) {
+ return false; // Whoops, cannot collect the primary clusters, probably probing an output peg.
+ }
+
+ // Collect clusters that get powered by the original cluster or will power it.
+ var collectedSources = new HashSet();
+ var collectedDrains = new HashSet();
+ foreach (Cluster cluster in primaryClusters) {
+ CollectClusters(cluster, collectedSources, GetLeaders);
+ CollectClusters(cluster, collectedDrains, GetFollowers);
+ }
+
+ foreach (Cluster cluster in primaryClusters) {
+ collectedSources.Remove(cluster);
+ collectedDrains.Remove(cluster);
+ }
+
+ // Collect and filter clusters that are both source and drain:
+ var collectedEquals = new HashSet();
+ foreach (Cluster collectedSource in collectedSources) {
+ if (collectedDrains.Remove(collectedSource)) {
+ collectedEquals.Add(collectedSource);
+ }
+ }
+
+ foreach (Cluster collectedEqual in collectedEquals) {
+ collectedSources.Remove(collectedEqual);
+ }
+
+ //Collect information about each cluster:
+ response = new AnalyzePathResponse() {
+ RequestGuid = requestGuid,
+ SelectedClusters = new List(),
+ };
+ foreach (Cluster cluster in primaryClusters) {
+ response.SelectedClusters.Add(CollectClusterInformation(cluster));
+ }
+
+ // response.sourcingClusters = new List();
+ // foreach (var cluster in collectedSources) {
+ // response.sourcingClusters.Add(CollectClusterInformation(cluster));
+ // }
+ //
+ // response.connectedClusters = new List();
+ // foreach (var cluster in collectedEquals) {
+ // response.connectedClusters.Add(CollectClusterInformation(cluster));
+ // }
+ //
+ // response.drainingClusters = new List();
+ // foreach (var cluster in collectedDrains) {
+ // response.drainingClusters.Add(CollectClusterInformation(cluster));
+ // }
+
+
+ return true;
+ }
+
+
+ private static bool PegExists(PegAddress address) {
+ IComponentInWorld component = World.Lookup(address.ComponentAddress);
+ if (component == null) {
+ return false; // Component of the peg does not exist in world.
+ }
+
+ int pegAmount = address.IsInputAddress() ? component.Data.InputCount : component.Data.OutputCount;
+ //If false: Component does not have this peg, as the peg index is bigger than the actual components input/output count.
+ return pegAmount >= address.PegIndex;
+ }
+
+ private static Cluster GetClusterAt(InputAddress peg) {
+ InputPeg originPeg = Circuits.LookupInput(peg);
+ if (originPeg == null) {
+ throw new Exception(
+ "Tried to lookup cluster on input peg, but the peg was not present in the circuit model! This should never happen, as the peg is present in the world.");
+ }
+
+ Cluster cluster = GetCluster(originPeg);
+ if (cluster == null) {
+ throw new Exception(
+ "Tried to lookup cluster on input peg, but the cluster was 'null', this should never happen! As the peg is present in the world.");
+ }
+
+ return cluster;
+ }
+
+ private static bool GetLinkerAt(Cluster cluster, out ClusterLinker linker) {
+ linker = GetLinker(cluster);
+ return linker != null;
+ }
+
+ private static bool CollectMainClusters(PegAddress pegAddress, out HashSet primaryClusters) {
+ primaryClusters = new HashSet();
+ if (pegAddress.IsInputAddress(out InputAddress inputAddress)) {
+ primaryClusters.Add(GetClusterAt(inputAddress));
+ } else {
+ HashSet wires = World.LookupPegWires(pegAddress);
+ if (wires == null) {
+ return true;
+ }
+
+ foreach (WireAddress wireAddress in wires) {
+ Wire wire = World.Lookup(wireAddress);
+ if (wire == null) {
+ throw new Exception(
+ "Tried to lookup wire given its address, but the world did not contain it. World must be corrupted.");
+ }
+
+ PegAddress otherSide = wire.Point1 == pegAddress ? wire.Point2 : wire.Point1;
+ if (!otherSide.IsInputAddress(out var otherSideInputAddress)) {
+ continue; //Not supported, this wire would be invalid anyway.
+ }
+
+ primaryClusters.Add(GetClusterAt(otherSideInputAddress));
+ }
+ }
+
+ return true;
+ }
+
+ private static void CollectClusters(
+ Cluster startingPoint,
+ HashSet collectedClusters,
+ Func> linkedLinkerGetter
+ ) {
+ var clustersToProcess = new Queue();
+ if (!GetLinkerAt(startingPoint, out ClusterLinker startingLinker)) {
+ return; // No linker on this cluster => no link => nothing to collect
+ }
+
+ clustersToProcess.Enqueue(startingLinker);
+ // While the starting cluster is no source, the algorithm needs to skip it when encountered.
+ collectedClusters.Add(startingPoint);
+ while (clustersToProcess.TryDequeue(out ClusterLinker linkerToCheck)) {
+ List listOfLinkedLinkers = linkedLinkerGetter(linkerToCheck); // Is never null.
+ foreach (ClusterLinker linkedLinker in listOfLinkedLinkers) {
+ Cluster clusterOfLinkedLinker = linkedLinker.ClusterBeingLinked; // Should never be null.
+ if (collectedClusters.Add(clusterOfLinkedLinker)) {
+ // Element was not yet present in the array, so keep looking into it!
+ clustersToProcess.Enqueue(linkedLinker);
+ }
+ }
+ }
+ }
+
+ private static ClusterDetails CollectClusterInformation(Cluster cluster) {
+ var details = new ClusterDetails {
+ Pegs = new List(),
+ ConnectingComponents = new List(),
+ LinkingComponents = new List(),
+ };
+
+ // Two lists are never null, according to how it is created and used:
+ IReadOnlyList inputPegs = cluster.ConnectedInputs;
+ IReadOnlyList outputPegs = cluster.ConnectedOutputs;
+
+ foreach (InputPeg peg in inputPegs) {
+ details.Pegs.Add(peg.Address);
+ if (peg.SecretLinks != null && peg.SecretLinks.Any()) {
+ // Highlight this component somehow.
+ 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())
+ ) {
+ details.LinkingComponents.Add(peg.Address.ComponentAddress);
+ }
+ }
+
+ foreach (OutputPeg peg in outputPegs) {
+ details.Pegs.Add(peg.Address);
+ }
+
+ return details;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/shared/packets/s2c/AnalyzePathResponse.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/shared/packets/s2c/AnalyzePathResponse.cs
index 102a753..05056ad 100644
--- a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/shared/packets/s2c/AnalyzePathResponse.cs
+++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/shared/packets/s2c/AnalyzePathResponse.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using LogicAPI.Data;
using LogicAPI.Networking.Packets;
using MessagePack;
@@ -6,6 +8,25 @@ namespace CriticalPathAnalyzer.Shared.Packets.S2C {
[MessagePackObject]
public class AnalyzePathResponse : Packet {
[Key(0)] public Guid RequestGuid;
- [Key(1)] public string Message;
+ [Key(1)]
+ public List SelectedClusters;
}
+
+ [MessagePackObject]
+ public sealed class ClusterDetails
+ {
+ [Key(0)]
+ public List Pegs;
+ [Key(1)]
+ public List ConnectingComponents;
+ [Key(2)]
+ public List LinkingComponents;
+
+ // The following two entries are used on the client to temporary store wires until their outline is being removed again.
+ // This information is just not collected on the server, hence the client collects them.
+ [IgnoreMember]
+ public List HighlightedWires;
+ [IgnoreMember]
+ public List HighlightedOutputWires;
+ }
}
\ No newline at end of file