Commit graph

20 commits

Author SHA1 Message Date
Erik
fbe9c8a288 fix(plugins): close LA5 host lifecycle review 2026-08-14 19:05:13 +02:00
Erik
95f4be94db feat(plugins): complete Campaign LA5 cross-host hosting 2026-08-14 18:12:59 +02:00
Erik
75a6724d5b wip: Campaign LA LA1 fix round — INCOMPLETE, stopped mid-task
Agent was stopped for token budget partway through the LA1 review fix
round. Landed here: F1 best-effort SessionStatusWriter, F2 App reader
tolerance (paths/mode), F5 argument-parsing hardening, plus new tests.
NOT DONE: F4 shared-fixture production shape (was the next step), F3
reconnect disconnected edge + recorded limitation, F6 exited
idempotency/reasons, F7 structural redaction test, F8 platform-guard
test + comment fix, optional RuntimeOptions PrintMembers redaction.

Build/test state UNVERIFIED at this commit. Next session: finish the
remaining findings, run the suites, then narrow re-review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:32:52 +02:00
Erik
db9ad53c1c docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1
The LA3 Opus review process note was right: the contract both sides
implement lived only in orchestrator prompts, which is exactly the drift
mode the pin exists to prevent (and it produced the paths-key CRITICAL).
The schema, field rules, probe-mode discriminator, and status vocabulary
are now a binding plan section; amendments change this text first,
implementations second. Ledger: LA3 fix round dispatched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:04:32 +02:00
Erik
cb6502c8a5 feat(platform): Campaign LA LA0 — extract ApplicationPathSet to AcDream.Platform
The launcher (LA3/LA4) needs the XDG/Windows path contract
(ApplicationPathSet/IApplicationPathEnvironment) without pulling in any
gameplay assembly. Move it out of AcDream.Runtime into a new BCL-only
AcDream.Platform project so the launcher-side Launcher.Core project can
reference it directly per the campaign plan (docs/plans/2026-08-14-launcher-campaign.md,
LA0). Namespace renamed AcDream.Runtime.Platform -> AcDream.Platform;
code is otherwise byte-identical (no logic changes).

AcDream.Runtime now carries a ProjectReference to AcDream.Platform and
re-exports it transitively, so App and Headless keep resolving the type
without a direct reference and K0's Headless single-ProjectReference
guard (HeadlessAssemblyReferencesOnlyTheRuntimeProject) stands unchanged.
The sibling Runtime dependency-boundary guard
(RuntimeProjectDeclaresOnlyApprovedProjectDependencies) does assert
Runtime's own project-reference set, so it needed a deliberate,
documented addition of AcDream.Platform to its expected list.

Moved tests/AcDream.Runtime.Tests/Platform/ApplicationPathSetTests.cs to
a new tests/AcDream.Platform.Tests/ project (namespace
AcDream.Platform.Tests) referencing only AcDream.Platform. Registered
both new projects in AcDream.slnx.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:19:02 +02:00
Erik
844cf092a1 feat(render): Campaign V slice V11 commit 1 - delete ImGui, Studio, and the DevTools frontend
The ImGui developer-tools stack (AcDream.UI.ImGui), UI Studio
(src/AcDream.App/Studio), and the DevToolsFramePresenter/
SettingsDevToolsCompositionPhase ImGui composition machinery are removed.
Vulkan never composed a DevTools frontend (DevToolsEnabled already forced
false whenever the backend was Vulkan); this commit makes that permanent by
deleting the only implementation rather than leaving a dead branch behind.

What moved: Studio/SampleData.cs is a live production dependency
(InteractionRetainedUiComposition's character-sheet fallback, plus three
UI.Layout test files) - git mv'd to src/AcDream.App/UI/Layout/SampleData.cs,
namespace AcDream.App.UI.Layout, and trimmed to the SampleCharacter API that
is actually still called (BuildObjectTable/AddItem/AddEquipped/the item-guid
and icon constants had zero callers left once the Studio fixture provider
that used them was deleted).

What survives as backend-neutral seams, per the tests that still exercise
them: IDevToolsFrameLifecycle (moved into RenderFramePreparationController.cs,
now always bound to null), IFramebufferDevToolsTarget/FramebufferDevToolsBinding
in FramebufferResizeController.cs (its concrete DevToolsFramebufferTarget
adapter is deleted), and IDevToolsGameplayCommands in
GameplayInputCommandController.cs (DevToolsGameplayCommands becomes a
documented no-op instead of forwarding to the deleted presenter). A follow-up
re-homes Settings/Debug onto the retained UI through IPanelRenderer; until
then keybind remapping falls back to editing keybinds.json.

DevToolsEnabled is now `private const bool DevToolsEnabled = false`.
RuntimeOptions.DevTools is unchanged and still reaches VulkanGraphicsContext
for the optional debug-utils extensions; Program.cs now logs one line when
ACDREAM_DEVTOOLS=1 explaining that the ImGui UI is gone and the flag is
Vulkan-only now.

Removed: AcDream.UI.ImGui (project + ImGui.NET/Silk.NET.OpenGL.Extensions.ImGui
package refs), src/AcDream.App/Studio (minus SampleData.cs),
DevToolsFramePresenter.cs and everything only it constructed
(ISettingsDevToolsCompositionFactory, RetailSettingsDevToolsCompositionFactory,
DevToolsCompositionOwner, IGameWindowSettingsDevToolsPublication,
SettingsDevToolsOptionalDependencies, the "developer tools" shutdown-ledger
stage and its DevTools-typed fields on IngressShutdownRoots/
RenderShutdownRoots), the ui-studio Program.cs verb, and the cimgui native
manifest entries in GraphicalHostPlatformServices. GameWindow.cs's DevTools
composition branch, its _vitalsVm/_debugVm/_devToolsComposition/
_devToolsFramePresenter/_devToolsCommandBus fields, and every settingsDevTools
.DevTools?.* access across FrameRootComposition.cs/SessionPlayerComposition.cs
are gone with it.

Build green; complete Release solution suite 8,830 / 5 skips (App Tests
4,097/3 skips run standalone - one #250-family zero-allocation test flakes
under the full parallel `dotnet test AcDream.slnx` run, a pre-existing,
documented class unrelated to this change).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 23:56:04 +02:00
Erik
11501d52ca feat(linux): gate graphical backends and capabilities 2026-07-27 12:26:58 +02:00
Erik
66f114b258 feat(linux): add graphical platform services 2026-07-27 11:54:59 +02:00
Erik
b298f99f91 refactor(runtime): own canonical action state
Move selection, combat, and interaction target mode under one Runtime owner; make plugins, retained UI, session routing, and typed runtime views borrow its exact children; and add failure-safe reset, instance isolation, source ownership, and normalized checkpoint coverage without changing retail ordering.

Co-authored-by: Codex <codex@openai.com>
2026-07-26 10:44:09 +02:00
Erik
7983309d23 feat(ui): centralize retail selection state 2026-07-11 00:51:20 +02:00
Erik
df6b5b3b39 feat(studio): StudioWindow + LayoutSource — render a dat panel standalone
Task 2 of the acdream UI Studio plan. Adds three new files under
src/AcDream.App/Studio/ and one new test file:

- StudioOptions.cs: record + Parse() for the ui-studio CLI args (positional
  dat dir or ACDREAM_DAT_DIR, --layout 0xNNNN, --markup <path>; defaults to
  vitals 0x2100006C when neither layout nor markup given).
- LayoutSource.cs: wraps LayoutImporter.Import for dat-backed layouts; markup
  path sets LastError "markup unsupported (Task 6)" and returns null for now.
  Exposes Kind / LayoutId / MarkupPath / LastError / CurrentLayout / Reload().
- StudioWindow.cs: Silk.NET 1280×720 GL 4.3 window; boots RenderBootstrap,
  wires input to UiHost, loads the panel via LayoutSource, adds the root to
  UiHost.Root.AddChild. QualitySettings resolved the same way GameWindow.Run()
  does (SettingsStore → QualitySettings.From → WithEnvOverrides).
- Program.cs: ui-studio dispatch at the top of top-level statements (before
  the dat-dir parse) so `dotnet run -- ui-studio` routes to StudioWindow.
- LayoutSourceTests.cs: dat-gated test (skips when dats absent); verifies that
  the vitals LayoutDesc (0x2100006C) loads, root is non-null, byId contains the
  vitals root element (0x100005F9), and Kind == DatLayout. Passes (1/1 with dats
  present; silently skips on CI).

Note: the spec asserts FindElement(0x2100006Cu) — that id is the LayoutDesc
dat id, not a widget element id. The actual vitals root element id is 0x100005F9
(confirmed from the vitals_2100006C.json fixture). The test uses the correct
element id and documents the discrepancy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 14:26:47 +02:00
Erik
019350fa31 feat(D.2b): IUiRegistry plugin UI surface + buffered drain into UiHost
Adds the plugin-facing UI registration surface (Task 9, final D.2b task).
Plugins call host.Ui.AddMarkupPanel(path, binding) from Enable(); calls are
buffered in BufferedUiRegistry before the GL window opens, then drained into
UiHost.Root in GameWindow.OnLoad inside the RetailUi block after the first-
party vitals panel. Faulty plugin markup is isolated (try/catch per panel,
logged + skipped). IPluginHost.Ui added; AppPluginHost wired; StubHost in
Core.Tests updated; BufferedUiRegistryTests confirms drain-once semantics.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 17:46:37 +02:00
Erik
eda936dc4d refactor(app): extract typed RuntimeOptions for startup env vars (Step 1)
Lifts 13 startup-time environment variables out of GameWindow.cs into a
single typed AcDream.App.RuntimeOptions record read once in Program.cs.
Behavior-preservation only — no live behavior change, no visual change.
Verified end-to-end against ACE on 127.0.0.1:9000: full M1 demo loop
(walk Holtburg, click door, click NPC, portal entry) plus DEVTOOLS
ImGui panels load cleanly.

Why: GameWindow.cs is 10,304 LOC and scattered Environment.GetEnvironmentVariable
calls were one of the structural smells called out in the new
"Code Structure Rules" doc. Typed options is the safest cut to make
first because the substitution is mechanical and parsing semantics
get pinned by unit tests.

What lands:
  - CLAUDE.md: removed stale R1→R8 execution-phases line, replaced with
    pointers to the milestones doc + strategic roadmap (the actual
    source of truth). Tightened the "check ALL FOUR references"
    section to describe WB as the production rendering base, not
    just a reference. New "Code Structure Rules" section (6 rules)
    captures the discipline we're committing to.
  - docs/architecture/acdream-architecture.md: removed dangling link
    to the deleted memory/project_ui_architecture.md.
  - docs/architecture/code-structure.md (NEW, 376 LOC): rationale for
    the 6 rules + 6-step extraction sequence
    (RuntimeOptions → LiveSessionController → LiveEntityRuntime →
    SelectionInteractionController → RenderFrameOrchestrator →
    GameEntity aggregation). This PR is Step 1.
  - src/AcDream.App/RuntimeOptions.cs (NEW, 100 LOC): typed record
    with FromEnvironment(string) factory and Parse(datDir, env)
    overload for testability. Covers ACDREAM_LIVE, _TEST_HOST/PORT/
    USER/PASS, _DEVTOOLS, _DUMP_MOVE_TRUTH, _NO_AUDIO,
    _ENABLE_SKY_PES, _HIDE_PART, _RETAIL_CLOSE_DEGRADES,
    _DUMP_SCENERY_Z, _STREAM_RADIUS.
  - src/AcDream.App/Program.cs: builds RuntimeOptions once, passes
    to GameWindow.
  - src/AcDream.App/Rendering/GameWindow.cs: ctor takes RuntimeOptions;
    7 startup-cached env-var fields become expression-bodied
    properties or direct _options.X reads; TryStartLiveSession,
    audio init, legacy stream-radius branch all route through
    _options.
  - tests/AcDream.App.Tests/ (NEW project, 10 unit tests + csproj):
    pins parser semantics — default-off bools, the literal "0"
    gate for RETAIL_CLOSE_DEGRADES, the >=0 guard for
    STREAM_RADIUS, null-vs-empty for user/pass, exact-"1" check
    for diagnostic flags. Registered in AcDream.slnx.

Out of scope (per code-structure.md §4):
  - Per-call-site ACDREAM_DUMP_* / _REMOTE_VEL_DIAG diagnostic reads
    sprinkled through GameWindow (~40 sites). Rule 5 in CLAUDE.md
    commits us to migrating these opportunistically as larger
    extractions land, not in a bulk pass.
  - AcDream.Core's project-reference to Chorizite.OpenGLSDLBackend.
    Only the stateless .Lib namespace is used; tightening the project
    reference is documented as future work in code-structure.md §2.

Build: green.
Tests: AcDream.App.Tests 10/10 ✓, Core.Net.Tests 294/294 ✓,
       UI.Abstractions.Tests 419/419 ✓,
       AcDream.Core.Tests 1073/1081 (8 pre-existing failures verified
       against pre-refactor baseline by stash-and-rerun).

Visual verification: full M1 demo loop against ACE +Acdream login
including DEVTOOLS panel host load.

Next: Step 2 — extract LiveSessionController per code-structure.md §4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 09:16:55 +02:00
Erik
08097b6c7e feat(app): wire IGameState+IEvents into Program and SmokePlugin
Pass WorldGameState and WorldEvents into GameWindow so OnLoad fires
FireEntitySpawned and Add for each hydrated entity. SmokePlugin now
subscribes to EntitySpawned in Enable(), unsubscribes in Disable(),
and logs the replay count at subscribe time and total seen at disable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 20:31:50 +02:00
Erik
0c0c042dca feat(core): add IGameState, IEvents, WorldEvents with replay-on-subscribe
Adds WorldEntitySnapshot, IGameState, IEvents abstractions; WorldEvents
implements replay-on-subscribe with per-handler exception swallowing;
WorldGameState tracks entities; AppPluginHost exposes all three; stubs
wired in Program.cs to keep build green ahead of Task 9 live wiring.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 20:29:29 +02:00
Erik
6a100ef6e7 refactor(app): harden shutdown per final review
Addresses final code review of phase-1 branch (Important I-1, I-3):

- Move plugin Enable() loop inside the same try block as GameWindow.Run,
  and wrap each Enable() in per-plugin try/catch mirroring the Disable
  loop. Previously, a plugin Enable() throwing would skip the finally
  block entirely: plugins that had already enabled would never get
  disabled, Serilog would never flush, and the exception would escape
  ungracefully. Now Enable failures are logged and contained, and
  shutdown always runs.
- Add a comment at the Get<LandBlock> call in GameWindow.OnLoad explaining
  why TryGet was avoided (the [MaybeNullWhen(false)] nullable-generic
  analysis trips TreatWarningsAsErrors).

I-2 (camera aspect doesn't update on window resize) deferred to Phase 2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:52:19 +02:00
Erik
fb83e0bb6f feat(app): wire plugin host, ship smoke plugin, log lifecycle
Phase 1 MVP end-to-end. Program.cs initializes Serilog, builds an
AppPluginHost that hands plugins a SerilogAdapter (IPluginLogger),
discovers plugins from the App's output plugins/ dir, loads each via
PluginLoader, calls Enable on all of them before opening the GameWindow,
and calls Disable in a finally block on shutdown.

AcDream.Plugins.Smoke is a new first-party plugin that logs through
the host during Initialize / Enable / Disable. Its csproj references
the abstractions with Private=false + ExcludeAssets=runtime to avoid
shipping a second copy of AcDream.Plugin.Abstractions.dll (which would
break ALC type identity). An MSBuild Target on the App project copies
the plugin DLL into plugins/AcDream.Plugins.Smoke/ and writes the
plugin.json manifest next to it.

Smoke verified against real dats. Console output observed:
  [INF] scanning plugins in ...\plugins
  [INF] smoke plugin initialized
  [INF] loaded plugin acdream.smoke (Smoke Plugin)
  [INF] smoke plugin enabled
  loaded landblock 0xA9B4FFFF
  <window renders terrain>
  [INF] smoke plugin disabled  (on shutdown)

Phase 1 done.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:46:25 +02:00
Erik
8356fe65a0 feat(app): load landblock from dats and upload mesh to GPU
GameWindow now owns a DatCollection + TerrainRenderer. On load it
opens the dat directory passed as argv[0] (or ACDREAM_DAT_DIR), finds
Holtburg (landblock 0xA9B4FFFF) by default with a fallback to the
first landblock in the cell b-tree, builds the CPU mesh from
LandblockMesh.Build, and uploads VBO+EBO+VAO with a 3f/3f/2f attribute
layout. No draw call yet — shader and matrix uniforms land in Task 9.

Enabled AllowUnsafeBlocks on the App csproj so the fixed-buffer upload
in TerrainRenderer compiles. Uses dats.Get<LandBlock>(id) instead of
TryGet(..., out T) to sidestep the [MaybeNullWhen(false)] analysis that
TreatWarningsAsErrors was flagging.

Smoke verified against the real retail dats: prints
"loaded landblock 0xA9B4FFFF" and the window stays alive with no GL
errors or exceptions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:42:13 +02:00
Erik
6d18e0bd38 feat(app): Silk.NET window smoke — clear to navy
Minimal Silk.NET window wiring: 1280x720, OpenGL 4.3 core profile,
VSync, dark navy clear color, Escape to close. No rendering beyond
the clear call — terrain and shader land in Tasks 8 and 9.

Manual smoke: process starts, stays alive past GL context creation,
produces no stderr, no uncaught exceptions. Actual visual check
will happen end-to-end after Task 10.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:39:40 +02:00
Erik
caf57cca3e chore: phase 1 — add Core, Abstractions, App, Tests projects 2026-04-10 09:22:33 +02:00