acdream/docs/research/2026-07-27-slice-k2-deterministic-bot-parity.md

4 KiB

Slice K2 — deterministic scheduler and bot parity closeout

Date: 2026-07-27 Implementation: 38e83640d907ee3e0819f3b38ca117ca60914d6b Status: COMPLETE

Outcome

Graphical and headless clients now drive the same Runtime-owned local-player frame sequence, movement controller, action owners, typed commands, and ordered event stream. Headless is not a second gameplay implementation and does not poll mutable App state.

One monotonic HeadlessProcessScheduler owns absolute deadlines for every configured session. Due sessions execute in stable configuration order, long pauses use bounded catch-up while preserving elapsed time, and reconnect quiescence is an absolute deadline rather than a blocking sleep.

Shipped mechanisms

  • Complete generation-gated direct command coverage for selection, use, appraisal, pickup, combat, spell casting, sustained movement, stance, inventory shortcuts, spellbook state, advancement, character options, social state, chat, and recalls.
  • Exact object selection and persistent movement intent are first-class typed commands shared by graphical and headless consumers.
  • RuntimeLocalPlayerFrameController owns retail's pre-network object phase, inbound barrier, and post-network AutonomousPosition phase. App is a thin graphical adapter; headless uses the same controller with no render mirror.
  • Runtime-owned hostile-target query and retail spell-target policy are presentation-independent.
  • Runtime remote-physics wall-clock reads use the instance TimeProvider.
  • Headless combat and magic operations route through the same canonical action owners and live WorldSession as the graphical client.
  • The lazy event hub now publishes combat-state outcomes in addition to lifecycle, entity, inventory, chat, movement, portal, and command outcomes. With no subscriber, it attaches no gameplay callbacks.
  • Accepted inbound work remains serialized inside each session's Runtime turn; bot policy runs after inbound, post-network position, and combat simulation.

The synchronous Core.Net connect handshake retains its bounded wall-clock timeout and retail/ACE 200 ms ConnectResponse race suffix. It is outside the steady scheduler, creates no per-session thread, and is not used for movement or packet cadence.

Deterministic gates

  • Absolute-deadline stable-order execution for multiple sessions.
  • Bounded catch-up with exact elapsed-time preservation.
  • Monotonic reconnect deadline without a blocked scheduler.
  • 100,000 idle pre-deadline dispatch probes: zero turns and zero managed allocation.
  • Graphical wrapper versus direct Runtime local-frame trace equality.
  • Headless packet order: MoveToState before AutonomousPosition.
  • AutonomousPosition cadence remains elapsed-time based at the retail one-second boundary; unchanged movement emits no duplicate MoveToState.
  • Stale generation movement and selection commands are inert.
  • Horizontal/vertical landblock target distance and hostile-only filters.
  • Combat-state and command outcomes share the generation-stamped ordered event sequence.

Automated gates

  • Runtime: 411/411.
  • Headless: 31/31.
  • App: 3,732 passed / 3 intentional skips.
  • Complete Release solution: 8,744 passed / 5 intentional skips.
  • Release build succeeds. The 17 warnings are pre-existing nullable/analyzer warnings in Core test fixtures; the changed projects build without warnings.

K3 carry

K3 introduces a process content owner with exact leases:

  • one mapped acdream.pak, synchronized DAT access, and immutable lookup tables per process;
  • per-session physics engine, mutable cache/bookkeeping, body graph, scratch, command state, and diagnostics;
  • content-backed local-player physics/spell hydration in headless sessions;
  • 1/5/10-session isolation, same-GUID, reconnect, and policy-fault gates.

Rollback

The scheduler foundation landed separately:

git revert 7e8acb74dd20931d5ba88430fd100cd074a32e4d

The K2 parity cutover is:

git revert 38e83640d907ee3e0819f3b38ca117ca60914d6b