Implement History timed callback scrolling
This commit is contained in:
@@ -6,6 +6,8 @@ namespace Age.Engine.Vm;
|
||||
/// are shared across frames; everything here is per-call and discarded on return.</summary>
|
||||
internal sealed class ExecFrame
|
||||
{
|
||||
internal readonly record struct TimedCallback(long DeadlineMs, int PrimaryOffset, int CatchUpOffset);
|
||||
|
||||
public readonly Script Script;
|
||||
public int Pc; // entry instruction index
|
||||
public readonly Frame Locals = new();
|
||||
@@ -22,6 +24,10 @@ internal sealed class ExecFrame
|
||||
public int MouseCallbackTarget = -1; // op 0xcc target dword offset
|
||||
public long MouseCallbackIntervalMs;
|
||||
public long MouseCallbackNextAtMs;
|
||||
public readonly List<TimedCallback> TimedCallbacks = new(); // ops 0xd3/0xd4/0xd5
|
||||
public int TimedCallbackCursor;
|
||||
public long? TimedCallbackStartedAtMs;
|
||||
public int TimedCallbackAbortOffset = -1;
|
||||
public readonly HotspotRegistry Hotspots = new();
|
||||
public ExecFrame(Script script, int pc) { Script = script; Pc = pc; }
|
||||
}
|
||||
|
||||
@@ -739,6 +739,63 @@ public sealed class VirtualMachine
|
||||
UpdatePointer((int)Read(a[0]), (int)Read(a[1])); return pc + 1;
|
||||
case "sleep": // 0xc8 (duration) — pause the host duration ms; headless hosts no-op (parity). Frame pacing.
|
||||
_host.Sleep(Read(a[0])); return pc + 1;
|
||||
case "u00425960":
|
||||
case "begin-timed-callback-sequence": // 0xd3: clear the frame-local relative schedule
|
||||
_cur.TimedCallbacks.Clear();
|
||||
_cur.TimedCallbackCursor = 0;
|
||||
_cur.TimedCallbackStartedAtMs = null;
|
||||
_cur.TimedCallbackAbortOffset = -1;
|
||||
return pc + 1;
|
||||
case "u004266F0":
|
||||
case "append-relative-timed-callbacks": // 0xd4: (interval ms, count, on-time PC, catch-up PC)
|
||||
{
|
||||
long intervalMs = Read(a[0]);
|
||||
int count = System.Math.Max(0, unchecked((int)Read(a[1])));
|
||||
int primaryOffset = unchecked((int)Read(a[2]));
|
||||
int catchUpOffset = unchecked((int)Read(a[3]));
|
||||
long deadlineMs = _cur.TimedCallbacks.Count == 0 ? 0 : _cur.TimedCallbacks[^1].DeadlineMs;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
deadlineMs += intervalMs;
|
||||
_cur.TimedCallbacks.Add(new ExecFrame.TimedCallback(
|
||||
deadlineMs, primaryOffset, catchUpOffset));
|
||||
}
|
||||
return pc + 1;
|
||||
}
|
||||
case "u004262C0":
|
||||
case "run-timed-callback-sequence": // 0xd5: dispatch each scheduled local callback, resuming here after ret
|
||||
{
|
||||
if (_cur.TimedCallbackStartedAtMs == null)
|
||||
{
|
||||
_cur.TimedCallbackStartedAtMs = _host.InputClockMilliseconds;
|
||||
_cur.TimedCallbackAbortOffset = unchecked((int)Read(a[0]));
|
||||
}
|
||||
// Native op 0xd5 stops at last_index rather than count. The final entry is a
|
||||
// look-ahead sentinel: it supplies the next deadline for the preceding event but
|
||||
// is not itself dispatched.
|
||||
if (_cur.TimedCallbackCursor >= _cur.TimedCallbacks.Count - 1)
|
||||
{
|
||||
_cur.TimedCallbackStartedAtMs = null;
|
||||
return pc + 1;
|
||||
}
|
||||
|
||||
var callback = _cur.TimedCallbacks[_cur.TimedCallbackCursor];
|
||||
long elapsedMs = _host.InputClockMilliseconds - _cur.TimedCallbackStartedAtMs.Value;
|
||||
if (elapsedMs < callback.DeadlineMs)
|
||||
{
|
||||
_host.Sleep(callback.DeadlineMs - elapsedMs);
|
||||
elapsedMs = _host.InputClockMilliseconds - _cur.TimedCallbackStartedAtMs.Value;
|
||||
}
|
||||
|
||||
bool fellBehind = _cur.TimedCallbackCursor + 1 < _cur.TimedCallbacks.Count
|
||||
&& _cur.TimedCallbacks[_cur.TimedCallbackCursor + 1].DeadlineMs < elapsedMs;
|
||||
int targetOffset = fellBehind ? callback.CatchUpOffset : callback.PrimaryOffset;
|
||||
_cur.TimedCallbackCursor++;
|
||||
if (targetOffset < 0 || !_cur.Script.IndexByOffset.TryGetValue(targetOffset, out int target))
|
||||
return pc;
|
||||
_cur.CallStack.Add(pc);
|
||||
return target;
|
||||
}
|
||||
case "u0041B290":
|
||||
case "set-message-skip": // 0x88: persistent all-message fast-forward service state
|
||||
_messageSkipEnabled = Read(a[0]) != 0;
|
||||
|
||||
Reference in New Issue
Block a user