Skip to content

Engine Decoupling — Phase 2: Headless adapter development #5

Description

@JohnLudlow

Summary

Provide a minimal, dependency-free adapter for CI, deterministic tests, and server/headless runs.

Objective

Implement HeadlessAdapter that records render commands (no GPU), simulates input via scripted events, and provides deterministic execution for reproducible integration tests.

Implementation Status

Component Status Notes
HeadlessAdapter ✅ Complete Implements all Phase 1 contracts
HeadlessRenderProvider ✅ Complete Records sprites, texts, and meshes in separate typed lists
HeadlessInputProvider ✅ Complete Stateful, scriptable input (HashSet-based)
HeadlessUserInterfaceProvider ✅ Complete (stub) Empty pending UI contract finalisation
HeadlessAssetProvider + HeadlessAssetLoader ✅ Complete In-memory cache; stub loader
HeadlessAudioPlayer ✅ Complete Records calls with correct method names (StartPlayback/StopPlayback)
RecordedCall DTO ✅ Complete
TestAdapter ✅ Complete Records both lifecycle and all provider calls via wrapper providers
DeterministicEngineRunner ✅ Complete Frame loop runs; SimulationTime exposed; RNG accessible; onTick callback provided
Integration test suite ✅ Complete AI, combat, and map-generation scenario tests all passing (132 tests total)

Technical Details

  • HeadlessAdapter implements IEngineAdapter and composes all headless providers. No native platform dependencies.
  • HeadlessRenderProvider records SubmitSprite, SubmitText, and SubmitMesh calls to separate typed lists (RecordedSprites, RecordedTexts, RecordedMeshes).
  • HeadlessInputProvider maintains scriptable key and mouse button state via sets of strings and integers.
  • HeadlessAudioPlayer records calls as (Method, AudioAssetId, Arg) tuples with correct method name strings.
  • TestAdapter wraps HeadlessAdapter and records both adapter lifecycle calls (InitializeAsync, ShutdownAsync) and all provider method calls via recording wrapper providers (RecordingRenderProvider, RecordingInputProvider, RecordingAssetProvider, RecordingAudioPlayer).
  • DeterministicEngineRunner stores a fixed timestep and seeded RNG. Exposes SimulationTime (incrementing after each frame), Random (seeded RNG for caller access), and accepts optional per-step onTick callback receiving DeterministicTickContext with time, RNG, and adapter.
  • Integration Tests:
    • AiIntegrationTests.NpcScenario_IsDeterministic: Verifies deterministic sprite generation with seeded RNG
    • CombatIntegrationTests.CombatScenario_IsDeterministic: Verifies deterministic call ordering via TestAdapter
    • CombatIntegrationTests.CombatScenario_AudioOnEvenStepsOnly: Verifies audio call recording and filtering
    • MapGenerationIntegrationTests.MapScenario_Produces1000Meshes: Verifies mesh recording and count stability
    • MapGenerationIntegrationTests.MapScenario_IsDeterministic: Verifies deterministic mesh generation with seeded RNG

Requirements

  • Headless adapter passes integration scenarios for AI, combat and map generation.
    • GIVEN deterministic seeds and scenario scripts
    • WHEN CI runs integration suite
    • THEN results are stable and asserted by tests.
    • All integration tests pass; results are verified as deterministic across multiple runs.

Outstanding Tasks

None. Phase 2 is complete.

Acceptance Criteria

  • HeadlessAdapter implements all Phase 1 contracts.
  • Deterministic tick mode and seeded RNG supported.
  • Integration tests for AI, combat, and map generation scenarios pass.
  • TestAdapter available for recording and asserting adapter calls.
  • No native platform dependencies.

Dependencies

Parent: #2
Phase 1 interfaces: complete (#1)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-libItems related to reusable librariesarea-technicalsystemsInternal systems to do with game developmentenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions