Some cleanups

This commit is contained in:
2026-01-15 00:10:22 +01:00
parent 0bbf7c2a30
commit 6c2cd0ced1
5 changed files with 52 additions and 433 deletions

37
Scripts/LogTimer.cs Normal file
View 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);
}
}
}

View File

@@ -204,7 +204,18 @@ namespace RemoteControl
}
[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}")]
public Task<IDictionary<long, Device>> ListDevices(string networkId)

View File

@@ -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);
}
}
}