Files
OpenMaidEngine/engine/Age.Engine.Frontend/GodotTimelineLog.cs
2026-08-03 11:25:08 -04:00

73 lines
2.5 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Text.Json;
/// <summary>Diagnostic-only synchronized JSONL stream for correlating VM execution, host waits/audio,
/// and compositor object changes. All producers share one lock, so event order is unambiguous even though
/// the VM and compositor run on different threads.</summary>
public sealed class GodotTimelineLog : System.IDisposable
{
private readonly object _lock = new();
private readonly StreamWriter _writer;
private long _sequence;
private int _frame;
private long _nowMs;
private string _script = "<startup>";
private int _offset = -1;
private int _opcode = -1;
private string _state = "starting";
private bool _disposed;
public GodotTimelineLog(string path)
{
var dir = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
_writer = new StreamWriter(path) { AutoFlush = true };
Record("start", new() { ["transition"] = "surface-alpha-lifecycle" });
}
public void SetFrame(int frame, long nowMs)
{
lock (_lock) { _frame = frame; _nowMs = nowMs; }
}
public void Step(string script, int offset, int opcode, int depth)
{
lock (_lock)
{
_script = script; _offset = offset; _opcode = opcode; _state = "running";
WriteLocked("step", new() { ["depth"] = depth });
}
}
public void State(string state, Dictionary<string, object?>? detail = null)
{
lock (_lock) { _state = state; WriteLocked(state, detail); }
}
public void Event(string kind, Dictionary<string, object?>? detail = null)
{
lock (_lock) WriteLocked(kind, detail);
}
private void Record(string kind, Dictionary<string, object?>? detail)
{
lock (_lock) WriteLocked(kind, detail);
}
private void WriteLocked(string kind, Dictionary<string, object?>? detail)
{
if (_disposed) return;
var row = new Dictionary<string, object?>
{
["seq"] = ++_sequence, ["kind"] = kind, ["frame"] = _frame, ["now_ms"] = _nowMs,
["script"] = _script, ["offset"] = _offset < 0 ? null : $"0x{_offset:x}",
["opcode"] = _opcode < 0 ? null : $"0x{_opcode:x}", ["vm_state"] = _state,
};
if (detail != null) foreach (var kv in detail) row[kv.Key] = kv.Value;
_writer.WriteLine(JsonSerializer.Serialize(row));
}
public void Dispose() { lock (_lock) { if (_disposed) return; _disposed = true; _writer.Dispose(); } }
}