diff --git a/godot/GodotAdvHost.cs b/godot/GodotAdvHost.cs index f86cea8..e5b1704 100644 --- a/godot/GodotAdvHost.cs +++ b/godot/GodotAdvHost.cs @@ -42,12 +42,6 @@ public sealed class GodotAdvHost : IHost // called from the main thread (click) or the selftest auto-clicker public void SignalInput() { if (_gate.CurrentCount == 0) _gate.Release(); } - // Records each call-script the VM dispatches (runs on the VM thread, so collect thread-safely and - // let the main thread report it — Godot drops GD.Print from background threads). - public readonly System.Collections.Concurrent.ConcurrentQueue Dispatched = new(); - public void CallScript(long id) => Dispatched.Enqueue(id); - public void OnStub(int opcode) { } - // ---- texture ops (run on the VM thread; marshal Godot node work to the main thread) ---- public void CreateTexture(int slot, int width, int height) { _slotBmp[slot] = null; _slotDims[slot] = (width, height); } diff --git a/godot/GodotTraceSink.cs b/godot/GodotTraceSink.cs new file mode 100644 index 0000000..f34dd4b --- /dev/null +++ b/godot/GodotTraceSink.cs @@ -0,0 +1,16 @@ +using System.Collections.Concurrent; +using Age.Engine.Diagnostics; + +// Frontend-side trace consumer. Runs on the VM background thread, so it just queues the dispatched +// call-script ids; the main thread drains them (Godot drops GD.Print from background threads). This +// replaces the old IHost.CallScript -> GodotAdvHost.Dispatched hack: subroutine visibility is now an +// engine fact delivered over the trace seam. +public sealed class GodotTraceSink : ITraceSink +{ + public bool TracingSteps => false; + public readonly ConcurrentQueue CallScripts = new(); + public void Emit(in TraceEvent e) + { + if (e.Kind == TraceEventKind.CallScript) CallScripts.Enqueue(e.Id); + } +} diff --git a/godot/Main.cs b/godot/Main.cs index 138a43e..9e6cbc3 100644 --- a/godot/Main.cs +++ b/godot/Main.cs @@ -19,6 +19,7 @@ public partial class Main : Godot.Control private AudioStreamPlayer _voice = null!; // interrupt-on-new voice private VirtualMachine _vm = null!; private GodotAdvHost _host = null!; + private GodotTraceSink _trace = null!; private volatile bool _done; private bool _ended; private bool _selftest; @@ -102,7 +103,8 @@ public partial class Main : Godot.Control if (_selftest) (script, provider) = BuildSelfTestScene(table); else { script = Sys4Loader.Load(Paths.Scripts()[scene.ToUpperInvariant() + ".BIN"], table); provider = Sys4ScriptProvider.Load(table); } _host = new GodotAdvHost(this, ResourceMap.Load(), scene); - _vm = new VirtualMachine(script, table, _host, null, provider); + _trace = new GodotTraceSink(); + _vm = new VirtualMachine(script, table, _host, null, provider, _trace); foreach (var (addr, val) in seeds) _vm.Globals[addr] = val; // seed initial state before running _ = Task.Run(() => { _vm.Run(); _done = true; }); @@ -203,7 +205,7 @@ public partial class Main : Godot.Control private void ReportSubroutines() { var ids = new List(); - while (_host.Dispatched.TryDequeue(out var id)) ids.Add(id); + while (_trace.CallScripts.TryDequeue(out var id)) ids.Add(id); if (ids.Count == 0) { GD.Print("[subroutines] none dispatched on this path"); return; } var distinct = new List(); foreach (var id in ids) { var h = "0x" + id.ToString("x"); if (!distinct.Contains(h)) distinct.Add(h); }