using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Nodes; using MiGu.Server.Launcher; namespace MiGu.Server.Signal; public sealed record SignalColumn(string Key, string Label, string Type, IReadOnlyList? Options = null, string? Group = null); public sealed record SignalTableDef( string Id, string Title, string Category, string FileName, IReadOnlyList Columns); /// /// 读写 SimpleLite 工作目录 Config/Signal/*.json,供迷毂「数据中心」表格编辑。 /// 表结构优先从 StandardScene.Signal.dll 反射;无插件时才读 signal-tables.json。 /// public sealed class SignalDataStore { private static readonly JsonSerializerOptions FileJson = new() { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; private static readonly object FileLock = new(); private readonly SimpleLiteLauncher _launcher; private IReadOnlyList? _cachedTables; private long _cachedSignature; public SignalDataStore(SimpleLiteLauncher launcher) => _launcher = launcher; public IReadOnlyList GetTables() { var sig = SignalTableManifestLoader.ComputeManifestSignature(_launcher); if (_cachedTables == null || sig != _cachedSignature) { _cachedTables = SignalTableManifestLoader.Load(_launcher); _cachedSignature = sig; } return _cachedTables; } public SignalTableDef? Find(string id) => GetTables().FirstOrDefault(t => string.Equals(t.Id, id, StringComparison.OrdinalIgnoreCase)); public string? ResolveWorkingDirectory() => _launcher.ResolveWorkingDirectory(); public (string? Path, string? Error) ResolveFile(SignalTableDef table, bool createDir) { var wd = _launcher.ResolveWorkingDirectory(); if (string.IsNullOrWhiteSpace(wd)) return (null, "未找到 SimpleLite 工作目录,无法定位 Config/Signal"); var dest = Path.GetFullPath(Path.Combine(wd, "Config", "Signal", table.FileName)); if (File.Exists(dest)) return (dest, null); if (createDir) { try { var dir = Path.GetDirectoryName(dest); if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir); } catch (Exception ex) { return (null, ex.Message); } } return (dest, null); } public JsonArray LoadRows(SignalTableDef table) { var (path, _) = ResolveFile(table, createDir: false); if (path == null || !File.Exists(path)) return new JsonArray(); lock (FileLock) { var json = File.ReadAllText(path); if (string.IsNullOrWhiteSpace(json)) return new JsonArray(); var node = JsonNode.Parse(json); if (node is JsonArray arr) return arr; return new JsonArray(); } } public void SaveRows(SignalTableDef table, JsonArray rows) { var (path, error) = ResolveFile(table, createDir: true); if (path == null) throw new InvalidOperationException(error ?? "无法解析信号配置文件路径"); var dir = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir); lock (FileLock) File.WriteAllText(path, rows.ToJsonString(FileJson)); } }