Some cleanups
This commit is contained in:
@@ -17,60 +17,21 @@ namespace RemoteControl.Patches
|
|||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public class LogicStack_LogicStackTick
|
public class LogicStack_LogicStackTick
|
||||||
{
|
{
|
||||||
private struct State
|
|
||||||
{
|
|
||||||
internal object LockObj;
|
|
||||||
internal bool Taken;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
private static void Prefix(out State __state)
|
private static void Prefix()
|
||||||
{
|
{
|
||||||
__state = new State
|
|
||||||
{
|
|
||||||
LockObj = SubscriptionManager.Lock,
|
|
||||||
Taken = false
|
|
||||||
};
|
|
||||||
// System.Threading.Monitor.Enter(__state.LockObj, ref __state.Taken);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RemoteControl.Log("logic stack tick: start prefix");
|
|
||||||
RemoteControl.Subscribers.SendUpdate();
|
RemoteControl.Subscribers.SendUpdate();
|
||||||
RemoteControl.Log("logic stack tick: end prefix");
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
RemoteControl.Log($"prefix: Exception {e}:\n {e.StackTrace}");
|
RemoteControl.Log($"prefix: Exception {e}:\n {e.StackTrace}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
private static void Postfix(State __state)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!GameManager.RunSimulation) return;
|
|
||||||
RemoteControl.Log("logic stack tick: start prefix");
|
|
||||||
SubscriptionManager.RescanNetworks();
|
|
||||||
RemoteControl.Log("logic stack tick: start postfix");
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
RemoteControl.Log("logic stack tick: start postfix");
|
|
||||||
RemoteControl.Log($"postfix: Exception {e}: \n{e.StackTrace}");
|
|
||||||
RemoteControl.Log("logic stack tick: end postfix");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// if (__state.Taken)
|
|
||||||
// {
|
|
||||||
// System.Threading.Monitor.Exit(__state.LockObj);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
<Compile Include="Patches\WorldManagerStop.cs"/>
|
<Compile Include="Patches\WorldManagerStop.cs"/>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs"/>
|
<Compile Include="Properties\AssemblyInfo.cs"/>
|
||||||
<Compile Include="Scripts\Remotecontrol.cs"/>
|
<Compile Include="Scripts\Remotecontrol.cs"/>
|
||||||
<Compile Include="Scripts\SubscriptionManager.cs"/>
|
<Compile Include="Scripts\LogTimer.cs" />
|
||||||
<Compile Include="Scripts\SubscriptionModule.cs"/>
|
<Compile Include="Scripts\SubscriptionModule.cs"/>
|
||||||
<Compile Include="Utils\Extensions.cs"/>
|
<Compile Include="Utils\Extensions.cs"/>
|
||||||
<Compile Include="Vendor\EmbedIO-3.5.2\Actions\ActionModule.cs"/>
|
<Compile Include="Vendor\EmbedIO-3.5.2\Actions\ActionModule.cs"/>
|
||||||
|
|||||||
37
Scripts/LogTimer.cs
Normal file
37
Scripts/LogTimer.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Assets.Scripts;
|
||||||
|
using Assets.Scripts.Networks;
|
||||||
|
using Assets.Scripts.Objects.Electrical;
|
||||||
|
using Assets.Scripts.Objects.Motherboards;
|
||||||
|
using Assets.Scripts.Util;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using RemoteControl.Message;
|
||||||
|
using Swan;
|
||||||
|
using UnityEngine;
|
||||||
|
using GameDevice = Assets.Scripts.Objects.Pipes.Device;
|
||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
namespace RemoteControl
|
||||||
|
{
|
||||||
|
|
||||||
|
public class LogTimer : IDisposable
|
||||||
|
{
|
||||||
|
private readonly DateTime _startTime = DateTime.Now;
|
||||||
|
private string _action;
|
||||||
|
|
||||||
|
public LogTimer(string action)
|
||||||
|
{
|
||||||
|
RemoteControl.Log($"Beginning {action}");
|
||||||
|
_action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var endTime = DateTime.Now;
|
||||||
|
var elapsed = endTime - _startTime;
|
||||||
|
Debug.Log($"Time taken for {_action}: " + elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -204,7 +204,18 @@ namespace RemoteControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Route(HttpVerbs.Get, "/networks")]
|
[Route(HttpVerbs.Get, "/networks")]
|
||||||
public Task<IList<string>> ListNetworks() => Task.FromResult(SubscriptionManager.GetProbes());
|
public Task<IList<string>> ListNetworks()
|
||||||
|
{
|
||||||
|
IList<string> probeNames = new List<string>();
|
||||||
|
GameDevice.AllDevices.ForEach(dev =>
|
||||||
|
{
|
||||||
|
if (dev is CableAnalyser analyser)
|
||||||
|
{
|
||||||
|
probeNames.Add(analyser.DisplayName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Task.FromResult(probeNames);
|
||||||
|
}
|
||||||
|
|
||||||
[Route(HttpVerbs.Get, "/networks/{networkId}")]
|
[Route(HttpVerbs.Get, "/networks/{networkId}")]
|
||||||
public Task<IDictionary<long, Device>> ListDevices(string networkId)
|
public Task<IDictionary<long, Device>> ListDevices(string networkId)
|
||||||
|
|||||||
@@ -1,390 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Assets.Scripts;
|
|
||||||
using Assets.Scripts.Networks;
|
|
||||||
using Assets.Scripts.Objects.Electrical;
|
|
||||||
using Assets.Scripts.Objects.Motherboards;
|
|
||||||
using Assets.Scripts.Util;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using RemoteControl.Message;
|
|
||||||
using Swan;
|
|
||||||
using UnityEngine;
|
|
||||||
using GameDevice = Assets.Scripts.Objects.Pipes.Device;
|
|
||||||
// ReSharper disable ClassNeverInstantiated.Global
|
|
||||||
// ReSharper disable MemberCanBePrivate.Global
|
|
||||||
namespace RemoteControl
|
|
||||||
{
|
|
||||||
public class DataNetwork
|
|
||||||
{
|
|
||||||
public long ReferenceId { get; private set; }
|
|
||||||
internal readonly List<CableAnalyser> Probes = new ();
|
|
||||||
internal CableNetwork Network { get; private set; }
|
|
||||||
|
|
||||||
private readonly HashSet<long> _knownIds = new();
|
|
||||||
|
|
||||||
|
|
||||||
internal readonly Dictionary<long, Device> DeviceCache = new();
|
|
||||||
|
|
||||||
public DataNetwork(CableNetwork network)
|
|
||||||
{
|
|
||||||
|
|
||||||
ReferenceId = network.ReferenceId;
|
|
||||||
Network = network;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RescanNetworkComposition()
|
|
||||||
{
|
|
||||||
_knownIds.Clear();
|
|
||||||
_knownIds.UnionWith(Network.DataDeviceList.Select(device => device.ReferenceId));
|
|
||||||
DeviceCache.Keys.Except(_knownIds).ToList().ForEach(device => DeviceCache.Remove(device));
|
|
||||||
|
|
||||||
_knownIds.Except(DeviceCache.Keys).ToList().ForEach(device =>
|
|
||||||
{
|
|
||||||
var newDev = new Device();
|
|
||||||
var dev = Referencable.Find<GameDevice>(device);
|
|
||||||
if (dev == null) return;
|
|
||||||
newDev.ReferenceId = device;
|
|
||||||
newDev.PrefabHash = dev.PrefabHash;
|
|
||||||
newDev.PrefabName = dev.PrefabName;
|
|
||||||
DeviceCache.Add(device, newDev);
|
|
||||||
});
|
|
||||||
UpdateAllDevices();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAllDevices()
|
|
||||||
{
|
|
||||||
foreach (var kvp in DeviceCache)
|
|
||||||
{
|
|
||||||
var device = kvp.Value;
|
|
||||||
var gameObj = Referencable.Find<GameDevice>(kvp.Key);
|
|
||||||
if (gameObj == null) continue;
|
|
||||||
device.LogicValues.Clear();
|
|
||||||
foreach (var type in EnumCollections.LogicTypes.Values)
|
|
||||||
{
|
|
||||||
if (gameObj.CanLogicRead(type))
|
|
||||||
{
|
|
||||||
device.LogicValues[type] = gameObj.GetLogicValue(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var slotCount = gameObj.TotalSlots;
|
|
||||||
while (device.Slots.Count > slotCount)
|
|
||||||
{
|
|
||||||
device.Slots.RemoveAt(device.Slots.Count - 1);
|
|
||||||
}
|
|
||||||
for (int slotIndex = 0; slotIndex < slotCount; slotIndex++)
|
|
||||||
{
|
|
||||||
if (device.Slots.Count <= slotIndex)
|
|
||||||
{
|
|
||||||
device.Slots.Add(new Dictionary<LogicSlotType, double>());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var type in EnumCollections.LogicSlotTypes.Values)
|
|
||||||
{
|
|
||||||
if (gameObj.CanLogicRead(type, slotIndex))
|
|
||||||
{
|
|
||||||
device.Slots[slotIndex][type] = gameObj.GetLogicValue(type, slotIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public DataNetwork Reset(CableNetwork cnet)
|
|
||||||
{
|
|
||||||
ReferenceId = cnet.ReferenceId;
|
|
||||||
Network = cnet;
|
|
||||||
Probes.Clear();
|
|
||||||
DeviceCache.Clear();
|
|
||||||
_knownIds.Clear();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct LogicUpdate : IEquatable<LogicUpdate>
|
|
||||||
{
|
|
||||||
public readonly long TargetReferenceId;
|
|
||||||
public readonly LogicType LogicType;
|
|
||||||
|
|
||||||
public LogicUpdate(long targetReferenceId, LogicType logicType)
|
|
||||||
{
|
|
||||||
TargetReferenceId = targetReferenceId;
|
|
||||||
LogicType = logicType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(LogicUpdate other)
|
|
||||||
{
|
|
||||||
return TargetReferenceId == other.TargetReferenceId && LogicType == other.LogicType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is LogicUpdate other && Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return CompositeHashCode.Using(TargetReferenceId, LogicType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct SlotUpdate : IEquatable<SlotUpdate>
|
|
||||||
{
|
|
||||||
public readonly long ReferenceId;
|
|
||||||
public readonly int Slot;
|
|
||||||
public readonly LogicSlotType Type;
|
|
||||||
|
|
||||||
public SlotUpdate(long referenceID, int slot, LogicSlotType type)
|
|
||||||
{
|
|
||||||
ReferenceId = referenceID;
|
|
||||||
Slot = slot;
|
|
||||||
Type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(SlotUpdate other)
|
|
||||||
{
|
|
||||||
return ReferenceId == other.ReferenceId && Slot == other.Slot && Type == other.Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is SlotUpdate other && Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return CompositeHashCode.Using(ReferenceId, Slot, Type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SubscriptionManager
|
|
||||||
{
|
|
||||||
public static readonly object Lock = new();
|
|
||||||
|
|
||||||
private static readonly Dictionary<long, DataNetwork> DataNetworks = new();
|
|
||||||
// private Dictionary<string, List<RemoteControlLimpet>> _probesByName = new();
|
|
||||||
// private Dictionary<long, RemoteControlLimpet> _probesById = new();
|
|
||||||
private static readonly Dictionary<LogicUpdate, double> PendingUpdates = new();
|
|
||||||
private static readonly Dictionary<SlotUpdate, double> PendingSlotUpdates = new();
|
|
||||||
|
|
||||||
[CanBeNull]
|
|
||||||
public static Device FindCachedDevice(string probeName, long referenceID)
|
|
||||||
{
|
|
||||||
foreach (var analyzer in GetDataNetwork(probeName))
|
|
||||||
{
|
|
||||||
if (analyzer.DeviceCache.TryGetValue(referenceID, out var device))
|
|
||||||
{
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can be called from any thread
|
|
||||||
public static bool SetLogic(string probeName, long referenceID, LogicType type, double value)
|
|
||||||
{
|
|
||||||
// lock (Lock)
|
|
||||||
{
|
|
||||||
var dev = FindCachedDevice(probeName, referenceID);
|
|
||||||
if (dev == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev.LogicValues[type] = value;
|
|
||||||
PendingUpdates[new LogicUpdate(referenceID, type)] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can be called from any thread
|
|
||||||
public static bool SetSlot(string probeName, long referenceID, int slot, LogicSlotType type, double value)
|
|
||||||
{
|
|
||||||
// lock (Lock)
|
|
||||||
{
|
|
||||||
|
|
||||||
var dev = FindCachedDevice(probeName, referenceID);
|
|
||||||
if (dev != null && dev.Slots.Count > slot && dev.Slots[slot].ContainsKey(type))
|
|
||||||
{
|
|
||||||
dev.Slots[slot][type] = value;
|
|
||||||
PendingSlotUpdates[new SlotUpdate(referenceID, slot, type)] = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataNetwork GetDataNetwork(CableNetwork network)
|
|
||||||
{
|
|
||||||
if (DataNetworks.TryGetValue(network.ReferenceId, out var dataNetwork))
|
|
||||||
{
|
|
||||||
return dataNetwork;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var ret = new DataNetwork(network);
|
|
||||||
DataNetworks.Add(network.ReferenceId, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<DataNetwork> GetDataNetwork(string probeName)
|
|
||||||
{
|
|
||||||
// lock (Lock)
|
|
||||||
{
|
|
||||||
List<DataNetwork> networks = new();
|
|
||||||
GameDevice.AllDevices.ForEach(dev =>
|
|
||||||
{
|
|
||||||
if (dev is CableAnalyser analyzer)
|
|
||||||
{
|
|
||||||
networks.Add(GetDataNetwork(analyzer.CableNetwork));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return networks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static GameDevice GetDevice(long referenceID)
|
|
||||||
{
|
|
||||||
var device = Referencable.Find<GameDevice>(referenceID);
|
|
||||||
if (!DataNetworks.Values.Any((item) => item.Network.DataDeviceList.Contains(device)))
|
|
||||||
return null;
|
|
||||||
return device;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GameDevice GetDevice(string probeName, long referenceID)
|
|
||||||
{
|
|
||||||
var device = Referencable.Find<GameDevice>(referenceID);
|
|
||||||
if (GetDataNetwork(probeName).Any(network => network.DeviceCache.ContainsKey(referenceID)))
|
|
||||||
{
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called from Unity thread pool before the logic tick
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
|
||||||
public static void ApplyUpdates()
|
|
||||||
{
|
|
||||||
lock (Lock)
|
|
||||||
{
|
|
||||||
foreach (var update in PendingUpdates)
|
|
||||||
{
|
|
||||||
var device = GetDevice(update.Key.TargetReferenceId);
|
|
||||||
|
|
||||||
if (!device.CanLogicWrite(update.Key.LogicType))
|
|
||||||
{
|
|
||||||
device.SetLogicValue(update.Key.LogicType, update.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var update in PendingSlotUpdates)
|
|
||||||
{
|
|
||||||
var device = Referencable.Find<Assets.Scripts.Objects.Pipes.Device>(update.Key.ReferenceId);
|
|
||||||
if (!DataNetworks.Values.Any((item) => item.Network.DataDeviceList.Contains(device)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!device.CanLogicWrite(update.Key.Type, update.Key.Slot))
|
|
||||||
{
|
|
||||||
device.SetLogicValue(update.Key.Type, update.Key.Slot, update.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PendingUpdates.Clear();
|
|
||||||
PendingSlotUpdates.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RescanNetworks()
|
|
||||||
{
|
|
||||||
using (new LogTimer("RescanNetworks"))
|
|
||||||
{
|
|
||||||
HashSet<CableAnalyser> scannedAnalyzers = new();
|
|
||||||
HashSet<CableNetwork> scannedNetworks = new();
|
|
||||||
|
|
||||||
scannedNetworks.Clear();
|
|
||||||
GameDevice.AllDevices.ForEach(dev =>
|
|
||||||
{
|
|
||||||
if (dev is CableAnalyser analyser)
|
|
||||||
{
|
|
||||||
scannedAnalyzers.Add(analyser);
|
|
||||||
scannedNetworks.Add(analyser.CableNetwork);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var removed =
|
|
||||||
new List<DataNetwork>(DataNetworks.Values.Where(dataNetwork =>
|
|
||||||
!scannedNetworks.Contains(dataNetwork.Network)));
|
|
||||||
foreach (var dataNet in removed)
|
|
||||||
{
|
|
||||||
DataNetworks.Remove(dataNet.ReferenceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var dataNet in DataNetworks.Values)
|
|
||||||
{
|
|
||||||
dataNet.Probes.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var analyzer in scannedAnalyzers)
|
|
||||||
{
|
|
||||||
if (DataNetworks.ContainsKey(analyzer.ReferenceId)) continue;
|
|
||||||
|
|
||||||
var dataNet = removed.Pop()?.Reset(analyzer.CableNetwork) ?? new DataNetwork(analyzer.CableNetwork);
|
|
||||||
dataNet.Probes.Add(analyzer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: when we have our own device for tagging a network, make this triggered by on the DataNetworkChange method
|
|
||||||
foreach (var dataNet in DataNetworks.Values)
|
|
||||||
{
|
|
||||||
dataNet.RescanNetworkComposition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IList<string> GetProbes()
|
|
||||||
{
|
|
||||||
List<string> probes = new();
|
|
||||||
GameDevice.AllDevices.ForEach(dev =>
|
|
||||||
{
|
|
||||||
if (dev is CableAnalyser)
|
|
||||||
{
|
|
||||||
probes.Add(dev.DisplayName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return probes;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LogTimer : IDisposable
|
|
||||||
{
|
|
||||||
private readonly DateTime _startTime = DateTime.Now;
|
|
||||||
private string _action;
|
|
||||||
|
|
||||||
public LogTimer(string action)
|
|
||||||
{
|
|
||||||
RemoteControl.Log($"Beginning {action}");
|
|
||||||
_action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
var endTime = DateTime.Now;
|
|
||||||
var elapsed = endTime - _startTime;
|
|
||||||
Debug.Log($"Time taken for {_action}: " + elapsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user