From c1e6e3da44ec422a59d06b1777b79694dcbdaef5 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 24 Aug 2026 12:32:42 +0200 Subject: [PATCH] =?UTF-8?q?fix=20#435=20(part=202,=20closes=20it):=20attri?= =?UTF-8?q?bute=20the=20unowned=20probes=20=E2=80=94=20delete=207,=20recla?= =?UTF-8?q?ssify=208,=20restore=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part 1 deleted probes whose owning issues were closed. These 14 named no issue at all, so each was traced to its introducing commit (git log -S) instead of guessed at. Attribution split them three ways: DELETED (7, investigations closed): ACDREAM_A8_DUMP_PV and ACDREAM_DUMP_LIVE_SPAWNS (Phase A8), ACDREAM_DUMP_CLOTHING (#37), ACDREAM_DUMP_EDGE_SLIDE (#32), ACDREAM_DUMP_STEPUP (L.2.3d-f), ACDREAM_DUMP_VENDOR (the vendor campaign, 25 call sites across 8 files), ACDREAM_DUMP_VITALS (#5, four independent read sites). VendorDiagnostics.cs went entirely. RECLASSIFIED (8, tools misfiled as probes): the DUMP_CELLS/DUMP_GFXOBJS fixture-extraction family (replay-harness tooling with a roundtrip test), PROBE_CELL (standing cell-transit tracer, pair of the permanent PROBE_RESOLVE), DUMP_SKY and HIDE_PART (generic isolation tools), and DUMP_STEEP_ROOF — which looked like an L.4 relic but observes LIVE divergence-register row AD-56; deleting it would have removed the only runtime lens on an active divergence. All moved to Permanent diagnostics with their attribution recorded. RESTORED (1): ACDREAM_DUMP_MOVE_TRUTH was deleted and un-deleted the same day. It is not a probe — the canonical nine-stop soak (run-connected-r6-soak.ps1) hard-fails every destination without its 'move-truth OUT' records, with a message that would misdirect the next operator. Under the no-workarounds rule the gate's mechanism is restored, not left broken with an IOU (#437, closed). Process lesson recorded on both issues: a closed owning issue is NOT sufficient to delete a probe — grep tools/ and the contract tests for consumers first. Also lands the owner-requested default-off invariant: every diagnostic in the codebase is inert until its env var is explicitly set. Exactly four flags default ON and none is a diagnostic — RETAIL_CHASE, CAMERA_COLLIDE, CAMERA_ALIGN_SLOPE, RETAIL_CLOSE_DEGRADES are retail behaviors wearing an A/B off-switch. That set is now FROZEN by LaunchOptionsDocumentationTests.OnlyTheFourRetailBehaviorFlagsDefaultOn; docs/launch-options.md's Conventions and CLAUDE.md state the rule, and CLAUDE.md now binds future probes to a documented row in the same commit. The client reads 137 environment variables (161 at audit start); 40 temporary probes remain, every one attributed. Full hermetic suite 15,322 passed / 0 failed. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 17 +- docs/ISSUES.md | 127 ++++++++++-- docs/launch-options.md | 67 ++++--- .../Composition/SessionPlayerComposition.cs | 4 +- .../PlayerInteractionMovementSink.cs | 8 - .../SelectionInteractionController.cs | 58 ------ .../Interaction/WorldSelectionQuery.cs | 6 - .../DatLiveEntityProjectionMaterializer.cs | 189 +----------------- .../Rendering/PortalVisibilityBuilder.cs | 51 ----- src/AcDream.App/RuntimeOptions.cs | 8 +- .../UI/ItemInteractionController.cs | 20 -- .../UI/Layout/SelectedObjectController.cs | 19 -- .../World/LiveEntityDeletionController.cs | 14 +- .../World/LiveEntityHydrationController.cs | 35 +--- src/AcDream.Core.Net/GameEventWiring.cs | 32 --- .../Messages/PlayerDescriptionParser.cs | 9 +- src/AcDream.Core.Net/WorldSession.cs | 7 - src/AcDream.Core/Items/VendorDiagnostics.cs | 27 --- .../Physics/PhysicsDiagnostics.cs | 4 +- src/AcDream.Core/Physics/TransitionTypes.cs | 145 +------------- .../RuntimeInteractionTransactionState.cs | 15 -- .../Gameplay/VendorShopItemMaterializer.cs | 11 - .../LaunchOptionsDocumentationTests.cs | 65 +++++- .../AcDream.App.Tests/RuntimeOptionsTests.cs | 7 +- .../Physics/CellarLipWedgeTests.cs | 2 - .../Physics/DoorCollisionApparatusTests.cs | 2 - 26 files changed, 256 insertions(+), 693 deletions(-) delete mode 100644 src/AcDream.Core/Items/VendorDiagnostics.cs diff --git a/CLAUDE.md b/CLAUDE.md index 808da717..90acfba6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1572,12 +1572,21 @@ Every environment variable and command-line argument the client reads — what it does, its exact value shape, and **what else it changes about the run** — is documented in [`docs/launch-options.md`](docs/launch-options.md). That file is the single -source of truth and is enforced by `LaunchOptionsDocumentationTests`: a -flag without a documented row fails the build, and so does a documented row -whose read site was deleted. +source of truth for every probe we have and how to turn one on, and it is +enforced by `LaunchOptionsDocumentationTests`: a flag without a documented +row fails the build, and so does a documented row whose read site was +deleted. **Any future probe that stays in the code gets its row there in +the same commit — no exceptions.** -Two habits that list exists to enforce: +The binding rules: +- **Every probe and dump is OFF by default.** Nothing that prints, records, + or costs performance may activate without its env var explicitly set + (`=1`). The only default-on flags are retail *behaviors* wearing an + A/B off-switch (`ACDREAM_RETAIL_CHASE`, `ACDREAM_CAMERA_COLLIDE`, + `ACDREAM_CAMERA_ALIGN_SLOPE`, `ACDREAM_RETAIL_CLOSE_DEGRADES` — `=0` + disables); that set is frozen by `LaunchOptionsDocumentationTests` — + never add a default-on diagnostic. - **Read the side-effects column before any measurement.** Flags that look inert are not: `ACDREAM_AUTOMATION_ARTIFACT_DIR` also builds a per-frame diagnostics referee (#432), and `ACDREAM_STREAM_RADIUS` measures a diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 44f49b0b..915cada9 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -24,16 +24,16 @@ What does NOT go here: - Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending. - Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed. -## #435 — PARTLY CLOSED: Probe debt: 17 temporary probes outlived their closed investigations, 14 more name no owner +## #435 — PARTLY CLOSED: Probe debt: 25 temporary probes outlived their closed investigations, 6 more name no owner -**Status:** The 17 orphaned probes are DELETED (2026-08-24) — 3,493 lines -removed, flag count 161 → 144, temporary probes 64 → 47. Build clean; full -hermetic suite 15,321 passed / 0 failed (baseline 15,333 minus the 12 tests -whose only subject was a deleted probe). Four files went entirely: -`WalkMissDiagnostic.cs`, `CollisionMeshWireframe.cs` and two probe-only test -files. `LaunchOptionsDocumentationTests` did its job during the cleanup — -it refused the deletion until the doc's rows moved to Retired and the frozen -direct-read counts were lowered (`PhysicsEngine.cs` to zero, +**Status:** The 17 orphaned probes from part 1 are DELETED (2026-08-24) — +3,493 lines removed, flag count 161 → 144, temporary probes 64 → 47. Build +clean; full hermetic suite 15,321 passed / 0 failed (baseline 15,333 minus +the 12 tests whose only subject was a deleted probe). Four files went +entirely: `WalkMissDiagnostic.cs`, `CollisionMeshWireframe.cs` and two +probe-only test files. `LaunchOptionsDocumentationTests` did its job during +the cleanup — it refused the deletion until the doc's rows moved to Retired +and the frozen direct-read counts were lowered (`PhysicsEngine.cs` to zero, `TransitionTypes.cs` 3 → 2). Notable: `TransitionTypes.SetContactPlane` shed its `CallerMemberName` / @@ -43,9 +43,45 @@ call site passed them, so no behavior changed. F2's collision overlay survives and reverts to the proxy-cylinder form, as intended when `ACDREAM_WIRE_MESH` went. -**STILL OPEN — the 14 unattributed probes.** They name no owning issue, so -nothing records when they are safe to remove. Deleting them on a guess is -how a future investigation loses apparatus it needed. The right next step is +**Part 2 (2026-08-24):** traced each of the 14 then-unattributed rows to +its introducing commit and confirmed 8 belonged to closed investigations — +`ACDREAM_A8_DUMP_PV` (Phase A8.F, closed), `ACDREAM_DUMP_CLOTHING` (#37, +DONE 2026-05-11), `ACDREAM_DUMP_EDGE_SLIDE` (#32, CLOSED 2026-08-07), +`ACDREAM_DUMP_LIVE_SPAWNS` (Phase A8, closed), `ACDREAM_DUMP_MOVE_TRUTH` +(#30/#34, DONE 2026-04-29), `ACDREAM_DUMP_STEPUP` (L.2.3d/e/f, closed), +`ACDREAM_DUMP_VENDOR` (vendor campaign, closed 2026-08-08), +`ACDREAM_DUMP_VITALS` (#5, DONE 2026-04-25). All 8 deleted along with their +call sites (`~130` net lines across 21 files), plus `VendorDiagnostics.cs` +(the `ACDREAM_DUMP_VENDOR` owner class, now fully unreferenced) and +`MovementTruthDiagnosticController`'s internals (kept as a permanent +no-op `IMovementTruthDiagnosticSink` implementation — `PlayerModeController`, +`GameWindow`, `LiveEntityNetworkUpdateController`, and +`SessionPlayerComposition` all still construct/wire it, so the DI graph is +unchanged). `docs/launch-options.md` rows moved to Retired; +`LaunchOptionsDocumentationTests`' `DirectReadDebt` lowered for +`PortalVisibilityBuilder.cs` (1→0, dropped), `GameEventWiring.cs` (1→0, +dropped), `PlayerDescriptionParser.cs` (1→0, dropped), +`TransitionTypes.cs` (2→0, dropped), `WorldSession.cs` (3→2). Build clean, +0 warnings; full filtered suite 15,315 passed / 0 failed / 0 skipped (no +`[Fact]`/`[Theory]` was removed — the small delta from part 1's 15,321 +baseline is pre-existing run-to-run count variance, not a test loss). + +**Found during part 2, not fixed (separate from #435's scope):** see #437 +— `tools/run-connected-r6-soak.ps1` sets `ACDREAM_DUMP_MOVE_TRUTH=1` and +greps its own log for `move-truth OUT` lines as part of its automated +movement-verification gate. That signal is now permanently dead (the flag +is a no-op), but the ps1's own text is unchanged so it and its pinning +contract test (`ConnectedWorldSoakRouteContractTests. +LaunchConfigurationIsDisclosedToTheArtifactDirectoryBeforeLaunch`) both +still pass — they assert the ps1's *text*, not that the mechanism it +describes still works. + +**STILL OPEN — the 6 remaining unattributed probes** (`ACDREAM_DUMP_CELLS_DIR`, +`ACDREAM_DUMP_GFXOBJS_DIR`, `ACDREAM_DUMP_SKY`, `ACDREAM_DUMP_STEEP_ROOF`, +`ACDREAM_HIDE_PART`, `ACDREAM_PROBE_CELL` — all deliberately left alone +this pass per the #435 part-2 scope). They name no owning issue, so nothing +records when they are safe to remove. Deleting them on a guess is how a +future investigation loses apparatus it needed. The right next step is attribution, not deletion: for each, find the commit that introduced it (`git log -S ACDREAM_PROBE_X`), record the issue in its `docs/launch-options.md` row, and only then decide. Deliberately deferred. @@ -131,6 +167,73 @@ differ from these two placeholder strings. --- +## #437 — CLOSED: The R6 soak's movement-verification signal died with a deleted probe — resolved by RESTORING the probe + +**Status:** CLOSED 2026-08-24, same day, by restoration — not by choosing a +replacement signal. On review, the deletion premise was wrong: +`ACDREAM_DUMP_MOVE_TRUTH` is not a spent investigation probe but +**automation apparatus** — `run-connected-r6-soak.ps1` (the canonical +nine-stop soak) HARD-FAILS every destination when `$moveTruthDelta < 2`, +with a message that would misdirect the next operator ("production +movement did not deliver outbound records" when in truth the diagnostic +was deleted). Under the no-workarounds rule the honest fix is to restore +the mechanism the gate depends on, not to leave the gate broken with an +IOU. Restored in full: `MovementTruthDiagnosticController`, +`RuntimeOptions.DumpMoveTruth` (now carrying a comment naming the soak +dependency), the GameWindow wiring, the RuntimeOptions tests, and the +gate-script allow-list entries. Its `docs/launch-options.md` row moved to +the Automation section with the dependency spelled out. Process lesson, +recorded on #435: "its issue is closed" is NOT sufficient to delete a +probe — grep `tools/` and the contract tests for consumers first (the +original #435 part 1 did this; part 2's dispatch omitted it). + +**Original report follows.** + +**Status (original):** OPEN +**Severity:** LOW (measurement-tooling gap, not a client defect — the gate +still runs and its OTHER checks still verify real behavior) +**Filed:** 2026-08-24 (found while closing #435 part 2) +**Component:** connected-gate tooling (`tools/run-connected-r6-soak.ps1`) + +**Symptom:** `run-connected-r6-soak.ps1` sets `ACDREAM_DUMP_MOVE_TRUTH=1` +before launch, then during each destination's checkpoint asserts the log +contains at least two fresh `move-truth OUT` lines +(`Wait-ForLogPattern $Client $stdoutLog 'move-truth OUT' ...`) and reports +a `$moveTruthDelta` count — this was one of several signals the script uses +to prove the client actually issued outbound movement during the run. +`ACDREAM_DUMP_MOVE_TRUTH` was retired in #435 part 2: +`MovementTruthDiagnosticController` is now a permanent no-op, so +`move-truth OUT` can never appear in the log again. The script still runs +(its forward/jump/combat input-dispatch checks are independent and still +real), but the movement-specific corroboration silently stops meaning +anything — a soak that broke outbound movement entirely would no longer be +caught by this particular check. + +**Why it wasn't fixed inline:** the fix requires choosing a NEW signal to +verify outbound movement happened (e.g. a different existing log line, a +wire-level assertion, or a purpose-built lightweight counter) — a design +decision outside the scope of a probe-deletion pass, not a mechanical +rename. `docs/ISSUES.md`/CLAUDE.md's "no workarounds without approval" rule +applies: inventing a replacement signal without checking it against what +the script's other checks already prove would risk a false sense of +coverage. + +**Corroborating detail:** `ConnectedWorldSoakRouteContractTests. +LaunchConfigurationIsDisclosedToTheArtifactDirectoryBeforeLaunch` +(`tests/AcDream.App.Tests/Diagnostics/ConnectedWorldSoakRouteContractTests.cs`) +asserts `$env:ACDREAM_DUMP_MOVE_TRUTH = '1'` appears in the ps1's source — +a text-content assertion, not a runtime one, so it still passes and gives +no signal that the mechanism died. + +**Fix shape:** decide on a replacement runtime signal for "the client +issued outbound movement" (candidates: an existing non-diagnostic log line +already emitted by the movement pipeline, or a small permanent counter +exposed through an existing owner class), wire it into the soak script's +per-destination checkpoint in place of the `move-truth OUT` grep, and +update the disclosure-contract test to match. + +--- + ## #434 — CLOSED: The DebugPanel/DebugVM developer surface is unreachable, and ~40 doc comments still advertise it as live **Status:** CLOSED 2026-08-24. Deleted `DebugPanel.cs` (340 lines), diff --git a/docs/launch-options.md b/docs/launch-options.md index d732c5fe..37487fa0 100644 --- a/docs/launch-options.md +++ b/docs/launch-options.md @@ -37,6 +37,15 @@ Assume a flag has a side effect until its row says otherwise. ## Conventions +- **Everything diagnostic is OFF by default.** Every probe, dump, capture, + and measurement flag in this document is inert until its variable is + explicitly set — an unset environment runs zero diagnostics. Exactly + four flags default ON, and none is a diagnostic: `ACDREAM_RETAIL_CHASE`, + `ACDREAM_CAMERA_COLLIDE`, `ACDREAM_CAMERA_ALIGN_SLOPE`, and + `ACDREAM_RETAIL_CLOSE_DEGRADES` are retail *behaviors* wearing an A/B + off-switch (`=0` disables the behavior for a comparison run). That + four-flag set is frozen by `LaunchOptionsDocumentationTests` — a new + default-on flag fails the build. - `=1` means the code tests for exactly the string `1`. Setting `true`, `yes`, or `0` does **not** enable such a flag (and `0` does not disable one whose test is "is the variable present"). @@ -225,8 +234,9 @@ $env:ACDREAM_FRAME_HISTORY = "$scratch\frames.csv" | `ACDREAM_UI_PROBE_DUMP` | `=1` | Enables the retail-UI automation probe's diagnostic dump path and feeds `RetailUiProbeBindings`/`RetailUiAutomationScriptRunner`. Also part of `RuntimeOptions.UiProbeEnabled` (`UiProbeDump \ | \ | UiProbeScript is set`). | `RuntimeOptions.UiProbeDump` → `LivePresentationComposition.cs:1465-1495`, `InteractionRetainedUiComposition.cs:1092-1100` | | `ACDREAM_UI_PROBE_SCRIPT` | `=` | Path to a script file the `RetailUiAutomationScriptRunner` executes against the retained UI (pointer/semantic-input command playback) for scripted UI regression testing. | Also flips `RuntimeOptions.UiProbeEnabled` true even without `ACDREAM_UI_PROBE_DUMP=1`. | `null` | `RuntimeOptions.UiProbeScript` → `InteractionRetainedUiComposition.cs:1094` | | `ACDREAM_VULKAN_FORCE_UNSUPPORTED` | `=` (case-insensitive property name, e.g. `MultiDrawIndirect`) | Test knob (Slice V5): clears one named required Vulkan feature from the capability record to synthetically fail the gate, so the `NotSupportedException` → exit-code-4 → report path can be exercised on hardware that actually supports everything. | Deliberately breaks Vulkan startup when set to a matched feature name — this is a "make it fail on purpose" gate-testing flag, never appropriate for a normal or measurement run. | `null` → real capabilities used unmodified | `RuntimeOptions.VulkanForcedUnsupportedFeature` → `VulkanCapabilityRecord.Without` (`VulkanCapabilityRecord.cs:113-119`), consumed at `VulkanGraphicsContext.cs:339` | -| `ACDREAM_VULKAN_PROBE` | `=1` | Runs the standalone Vulkan capability-probe/bring-up harness (opens its own window, runs the capability gate, presents synthetic V6c/V6d verification scenes, captures one screenshot) **instead of** the real client composition host, then exits. | Its class doc (`VulkanBringUpHost.cs:11`) is stale — claims it additionally requires `ACDREAM_RENDER_BACKEND=vulkan`, which is no longer read anywhere (see that flag's row); in the current code this flag ALONE gates entry (`GameWindow.cs:828: if (_options.VulkanCapabilityProbe)`). | `false` → normal composition host | `RuntimeOptions.VulkanCapabilityProbe` → `GameWindow.cs:828` → `VulkanBringUpHost` | +| `ACDREAM_VULKAN_PROBE` | `=1` | Runs the standalone Vulkan capability-probe/bring-up harness (opens its own window, runs the capability gate, presents synthetic V6c/V6d verification scenes, captures one screenshot) **instead of** the real client composition host, then exits. | This flag ALONE gates entry (`GameWindow.cs:828`); the former `ACDREAM_RENDER_BACKEND=vulkan` co-requisite died with the OpenGL backend (its class doc was corrected 2026-08-24). | `false` → normal composition host | `RuntimeOptions.VulkanCapabilityProbe` → `GameWindow.cs:828` → `VulkanBringUpHost` | | `ACDREAM_VULKAN_PROBE_FRAMES` | `=` (non-negative) | Bounds the bring-up probe harness to N presented frames so it can run unattended in CI, instead of presenting until a human closes the window. | The frame budget never cuts a pending screenshot capture short — the loop stays open until the screenshot has been attempted even past the budget, so an unattended run's whole product (a PNG) is guaranteed. Zero (unset/unparseable/explicit `0`) keeps the interactive wait-for-close behavior. | `0` → interactive (wait for window close) | `RuntimeOptions.VulkanCapabilityProbeFrames` → `VulkanBringUpHost.cs:141-249` | +| `ACDREAM_DUMP_MOVE_TRUTH` | `=1` | Emits one `move-truth OUT` line per outbound movement record (MoveToState / AutonomousPosition): local resolved position vs the wire position/cell, ground contact, velocity (`MovementTruthDiagnosticController`). | **Automation apparatus, NOT a spent probe** — the canonical nine-stop soak (`tools/run-connected-r6-soak.ps1`) hard-gates on ≥2 of these lines per destination as its proof that production input produced outbound movement traffic; deleting it fails the soak at every stop (#437, deleted-and-restored 2026-08-24). Print volume follows the outbound send cadence. | off | `RuntimeOptions.DumpMoveTruth` → `GameWindow.cs` → `MovementTruthDiagnosticController` | ## Permanent diagnostics @@ -253,6 +263,14 @@ $env:ACDREAM_FRAME_HISTORY = "$scratch\frames.csv" | `ACDREAM_PROBE_USEABILITY_FALLBACK` | `=1` | gates a per-call log of `IsUseableTarget` calls that take the null-useability fallback path (creature/door/lifestone passes) (measures a real ace-vs-retail data gap, not a bug investigation) | print-only; measures how often ACE ships entities without `_useability` set | off | `PhysicsDiagnostics.ProbeUseabilityFallbackEnabled` | | `ACDREAM_PROBE_VIS` | `=1` | emits `[vis]` line on root-cell CHANGE: visible cell ids, OutsideView poly/plane counts, per-cell plane counts, scissor-fallback count (phase u.2d repurposed the flag; its DebugPanel mirror is unreachable — #434) | print-only; ALSO implicitly enables the separate `ACDREAM_PROBE_ENVCELL` probe (its getter ORs with this flag — see Notes #3); startup-only in practice (its DebugPanel/DebugVM mirror is unreachable — #434)| false (off) | `RenderingDiagnostics.ProbeVisibilityEnabled` | | `ACDREAM_REMOTE_VEL_DIAG` | `=1` | prints per-UM/per-tick remote-velocity and animation-cycle diagnostic lines; `Runtime/Physics/RemoteMotion.cs` carries diagnostic-only fields (`PrevServerPos`, `PrevServerPosTime`, `MaxRootMotionSpeedSinceLastUP`, `LastOmegaDiagLogTime`) unconditionally on every remote — small fixed per-instance memory regardless of the flag, not gated (long-lived remote-velocity/animation diagnostic, commit a.1) | print-only, but the raw-site reads in `LiveEntityNetworkUpdateController.cs` fire on every UM/tick even when off (rule-5 violation, `Environment.GetEnvironmentVariable` call per event, 6+ call sites) | off | THREE readers: `AnimationPresentationDiagnostics.FromEnvironment()` (App owner record, cached at startup, consumed by `LiveEntityAnimationPresenter` for `[SEQSTATE]`/`[CURRNODE]`/other part-diagnostic lines, throttled to 1/sec/entity) + raw `Environment.GetEnvironmentVariable` reads scattered across `LiveEntityNetworkUpdateController.cs` (6+ sites: `[UM_RAW]`, `[FWD_WIRE]`, `[VEL_DIAG]`, `[UPCYCLE_SRC]`, `[UM_STALE]`) + `RemoteServerControlledVelocityCycle.cs:68` (`[UPCYCLE]`) | +| `ACDREAM_DUMP_CELLS` | `=` | one-shot JSON dump of any cached EnvCell whose id matches the list, to `ProbeDumpCellsPath` (issue #98 fixture capture) — Standing fixture-extraction tooling (A6.P3/#98 lineage) for the physics replay harness; roundtrip-tested. Not investigation-scoped. | file I/O once per matching cell id (no-op on repeat); fixture-generation tool, not a perf-neutral no-op when ids are listed | off/unset | `PhysicsDiagnostics.ProbeDumpCellIds` (`ParseHexIdList`) | +| `ACDREAM_DUMP_CELLS_DIR` | `=` | overrides the output directory for `ACDREAM_DUMP_CELLS` — Companion output-directory knob for ACDREAM_DUMP_CELLS. | print/file-path only; no effect unless `ACDREAM_DUMP_CELLS` is also set | off/unset | `PhysicsDiagnostics.ProbeDumpCellsPath` | +| `ACDREAM_DUMP_GFXOBJS` | `=` | one-shot JSON dump of any cached GfxObj's polygon table + BSP root metadata matching the list, to `ProbeDumpGfxObjsPath` (issue #98 fixture capture) — Standing fixture-extraction tooling (A6.P3/#98 lineage), pair of DUMP_CELLS. | file I/O once per matching id (no-op on repeat) | off/unset | `PhysicsDiagnostics.ProbeDumpGfxObjIds` (`ParseHexIdList`) | +| `ACDREAM_DUMP_GFXOBJS_DIR` | `=` | overrides the output directory for `ACDREAM_DUMP_GFXOBJS` — Companion output-directory knob for ACDREAM_DUMP_GFXOBJS. | print/file-path only; no effect unless `ACDREAM_DUMP_GFXOBJS` is also set | off/unset | `PhysicsDiagnostics.ProbeDumpGfxObjsPath` | +| `ACDREAM_DUMP_SKY` | `=1` | Print-only: dumps decoded `SkyDesc` raw values on region load (`SkyDescLoader.cs`) and per-GfxObj `Surface.Type`/translucency flags on first upload (`SkyRenderer.cs`), plus gates a `TimeSync` console diagnostic in `GameWindow`. Built to resolve specific open questions about retail sky units and GfxObjReplace timing (2026-04-23 research), now answered but the dumps remain wired. — Generic sky-keyframe isolation dump (introduced with the phase-1 tint revert); a tool, not a bug probe. | Three independent reads of the SAME env var, only one of which (`RuntimeOptions.DumpSky`) goes through the typed options object; the other two are raw scattered reads (see Notes). `SkyRenderer.cs:582`'s raw read is in the App layer and has no architectural excuse for bypassing `RuntimeOptions` — `_options.DumpSky` was already available to that composition. `print-only` in all three sites. | off/unset | `RuntimeOptions.DumpSky` (typed) → `GameWindow.cs:704` (`TimeSyncDiagnostic`); **also** two independent raw `Environment.GetEnvironmentVariable` reads at `SkyDescLoader.cs:392` (Core) and `SkyRenderer.cs:582` (App) | +| `ACDREAM_DUMP_STEEP_ROOF` | `=1` | gates `[steep-roof] KILL-VELOCITY-APPLIED` in `PhysicsEngine.ResolveWithTransition` when retail's `kill_velocity` zeroes body velocity on steep-slope impact, plus per-frame plane-normal traces in `TransitionTypes`/`PlayerMovementController` — KEEP: observes LIVE divergence-register row AD-56 (the plumb-fall freeze on steep-but-walkable polys, restored 2026-08-07). The only runtime lens on that active divergence; delete only with the AD-56 row itself. | print-only | off/unset | `PhysicsDiagnostics.DumpSteepRoofEnabled` | +| `ACDREAM_HIDE_PART` | `=` | Hides one mesh part by index on entities with ≥10 parts (humanoids) — a debugging aid for equipment/clothing part-visibility issues. — Generic model-part isolation tool (issue #37 lineage but general-purpose since); a tool, not a bug probe. | Real (visible) behavior change, not print-only, but scoped to a single diagnostic index and off by default. | off/unset | `RuntimeOptions.HidePartIndex` → `LivePresentationComposition.cs:608` → `LiveEntityAnimationPresenter.cs:21,38,243` | +| `ACDREAM_PROBE_CELL` | `=1` | gates one `[cell-transit]` line per `PlayerMovementController.CellId` change (old→new cell, position, reason tag) — Standing cell-transit tracer (L.2a slice 1), pair of the permanent ACDREAM_PROBE_RESOLVE; recurs in every membership investigation. | print-only; low volume (only on actual cell crossings) | off/unset | `PhysicsDiagnostics.ProbeCellEnabled` | ## Temporary probes @@ -261,39 +279,33 @@ the same commit as its investigation's fix** — if you find one here whose issue is closed, the strip was missed; delete both. > **Probe debt, measured 2026-08-24:** 64 temporary probes existed, citing 21 -> distinct issues with 14 already closed. [#435](ISSUES.md) stripped the 17 -> rows whose investigation had ended without the strip — see the Retired -> section below for their removal record — leaving 47. A further 14 rows -> (unchanged by this pass) name no owning issue at all, which is worse: -> nobody can tell when they are safe to remove. Every probe here still costs -> a branch on its hot path even when unset, and a handful re-read the -> environment per frame rather than caching (see their side-effects column). +> distinct issues with 14 already closed. [#435](ISSUES.md) part 1 stripped +> the 17 rows whose investigation had ended without the strip — see the +> Retired section below for their removal record — leaving 47. Part 2 +> traced each of the (then-)14 unattributed rows to its introducing commit +> and stripped the 7 that belonged to closed investigations +> (`ACDREAM_A8_DUMP_PV`/Phase A8, `ACDREAM_DUMP_CLOTHING`/#37, +> `ACDREAM_DUMP_EDGE_SLIDE`/#32, `ACDREAM_DUMP_LIVE_SPAWNS`/Phase A8, +> `ACDREAM_DUMP_STEPUP`/L.2.3d-f, `ACDREAM_DUMP_VENDOR`/the vendor +> campaign, `ACDREAM_DUMP_VITALS`/#5), leaving 40. An eighth, +> `ACDREAM_DUMP_MOVE_TRUTH`, was deleted and then RESTORED the same day: +> it turned out to be automation apparatus, not a probe — the canonical +> nine-stop soak hard-gates on its output (see its row under Automation; +> #437 is the record). The remaining rows attributed at part 2 were +> reclassified into Permanent diagnostics as standing tools rather than +> investigation probes. Every probe here still costs a branch on its hot +> path even when unset, and a handful re-read the environment per frame +> rather than caching (see their side-effects column). | Flag | Owning investigation | Value | What it does | Side effects | Read by | |---|---|---|---|---|---| -| `ACDREAM_A8_DUMP_PV` | (unattributed) | `=1` | Dumps local→NDC→clipped portal geometry (first 2 `Build` calls per distinct camera cell) | print-only (`Console.WriteLine`) | `PortalVisibilityBuilder.cs:270-271` (static field, not a diagnostics-owner class) | | `ACDREAM_CLIP_DEBUG` | #176 | `=1` | forces the EnvCell SHELL pass to map every instance to clip slot 0 (no-clip) instead of its cell's portal-slice region | ALTERS RENDERED OUTPUT: shells draw whole/unclipped instead of trimmed — a visual isolation mode, not a log-only probe; no DebugPanel mirror | `RenderingDiagnostics.ClipDebugNoShellTrim` | | `ACDREAM_DUMP_APPEARANCE` | #5 | `="1"` | Logs every `0xF625` ObjDescEvent + `0xF7DB` UpdateObject with body length, target guid, hex preview — used to debug remote-player appearance asymmetry | print-only (`Console.WriteLine`) | `WorldSession` static field `DumpAppearanceEnabled` (`WorldSession.cs:792-793`), raw scattered read, issue #5 diagnostic | -| `ACDREAM_DUMP_CELLS` | #98 | `=` | one-shot JSON dump of any cached EnvCell whose id matches the list, to `ProbeDumpCellsPath` (issue #98 fixture capture) | file I/O once per matching cell id (no-op on repeat); fixture-generation tool, not a perf-neutral no-op when ids are listed | `PhysicsDiagnostics.ProbeDumpCellIds` (`ParseHexIdList`) | -| `ACDREAM_DUMP_CELLS_DIR` | (unattributed) | `=` | overrides the output directory for `ACDREAM_DUMP_CELLS` | print/file-path only; no effect unless `ACDREAM_DUMP_CELLS` is also set | `PhysicsDiagnostics.ProbeDumpCellsPath` | -| `ACDREAM_DUMP_CLOTHING` | (unattributed) | `=1` | Print-only: dumps clothing/part-swap diagnostics for a spawned entity when its setup has ≥10 mesh parts (humanoids). Gated additionally on part count even when the flag is on. | `print-only` | `RuntimeOptions.DumpClothing` → `DatLiveEntityProjectionMaterializer.cs:251-258,1190` | -| `ACDREAM_DUMP_EDGE_SLIDE` | (unattributed) | `=1` | gates five `edge-slide:` trace lines (stepdown-failed, stepdown-branch-enter, phase2, branch, cliffslide) inside the L.4-diag edge-slide/cliff-slide code path | print-only; property re-reads `Environment.GetEnvironmentVariable` on EVERY call (not cached in a field) — repeated env lookups during edge-slide resolution when active; raw read outside any diagnostics-owner class (rule-5 candidate) | `Transition.DumpEdgeSlideEnabled` (private expression-bodied property in `TransitionTypes.cs`, raw read) | -| `ACDREAM_DUMP_GFXOBJS` | #98 | `=` | one-shot JSON dump of any cached GfxObj's polygon table + BSP root metadata matching the list, to `ProbeDumpGfxObjsPath` (issue #98 fixture capture) | file I/O once per matching id (no-op on repeat) | `PhysicsDiagnostics.ProbeDumpGfxObjIds` (`ParseHexIdList`) | -| `ACDREAM_DUMP_GFXOBJS_DIR` | (unattributed) | `=` | overrides the output directory for `ACDREAM_DUMP_GFXOBJS` | print/file-path only; no effect unless `ACDREAM_DUMP_GFXOBJS` is also set | `PhysicsDiagnostics.ProbeDumpGfxObjsPath` | -| `ACDREAM_DUMP_LIVE_SPAWNS` | (unattributed) | `=1` | Print-only: logs every live `CreateObject` spawn as it's processed, plus DROP lines when a setup dat id is missing. | `print-only` | `RuntimeOptions.DumpLiveSpawns` → `DatLiveEntityProjectionMaterializer.cs:161-226`, `SessionPlayerComposition.cs:566,712` | -| `ACDREAM_DUMP_MOVE_TRUTH` | (unattributed) | `=1` | Print-only: records the local player's last outbound movement wire truth (position, cell, contact byte, velocity) for comparing what was actually sent vs. local physics state. Early-returns with zero cost when disabled. | `print-only` | `RuntimeOptions.DumpMoveTruth` → `GameWindow.cs:795` → `MovementTruthDiagnosticController` | | `ACDREAM_DUMP_OPCODES` | #5 | `="1"` | Logs first occurrence of each genuinely-unhandled inbound opcode (deduped by opcode) | print-only. Must stay the LAST else-if in the dispatch chain per comment (else it would intercept handled opcodes) — currently correct. | `WorldSession` static field `DumpOpcodesEnabled` (`WorldSession.cs:788-789`, consumed `WorldSession.cs:2391-2398`), issue #5 diagnostic. Also mirrored (display-only, non-functional) via `DebugPanel.cs:241`/`DebugVM.cs:227`. | | `ACDREAM_DUMP_SCENERY_Z` | #48 | `=1` | Per-spawn Z-placement diagnostic for procedural scenery (trees/bushes/rocks), added for issue #48 (the "trees-in-sky" bug). | **NOT print-only** — this is a real behavior fork, not just added logging. `LandblockBuildFactory.cs:167-178`: when the flag is on, the streaming worker calls a **separate, duplicate scenery-building method** (`BuildSceneryEntitiesForStreaming`, a full parallel reimplementation of GfxObj/Setup mesh resolution + placement inline in this file) instead of production's `LandblockPhysicsContentBuilder.HydrateProceduralScenery`. Any visual/measurement run taken with this flag set is exercising a different scenery-placement code path than production, which can drift from it silently. | `RuntimeOptions.DumpSceneryZ` → `SessionPlayerComposition.cs:280` → `LandblockBuildFactory.cs:23,42,168,335` | -| `ACDREAM_DUMP_SKY` | (unattributed) | `=1` | Print-only: dumps decoded `SkyDesc` raw values on region load (`SkyDescLoader.cs`) and per-GfxObj `Surface.Type`/translucency flags on first upload (`SkyRenderer.cs`), plus gates a `TimeSync` console diagnostic in `GameWindow`. Built to resolve specific open questions about retail sky units and GfxObjReplace timing (2026-04-23 research), now answered but the dumps remain wired. | Three independent reads of the SAME env var, only one of which (`RuntimeOptions.DumpSky`) goes through the typed options object; the other two are raw scattered reads (see Notes). `SkyRenderer.cs:582`'s raw read is in the App layer and has no architectural excuse for bypassing `RuntimeOptions` — `_options.DumpSky` was already available to that composition. `print-only` in all three sites. | `RuntimeOptions.DumpSky` (typed) → `GameWindow.cs:704` (`TimeSyncDiagnostic`); **also** two independent raw `Environment.GetEnvironmentVariable` reads at `SkyDescLoader.cs:392` (Core) and `SkyRenderer.cs:582` (App) | -| `ACDREAM_DUMP_STEEP_ROOF` | (unattributed) | `=1` | gates `[steep-roof] KILL-VELOCITY-APPLIED` in `PhysicsEngine.ResolveWithTransition` when retail's `kill_velocity` zeroes body velocity on steep-slope impact, plus per-frame plane-normal traces in `TransitionTypes`/`PlayerMovementController` | print-only | `PhysicsDiagnostics.DumpSteepRoofEnabled` | -| `ACDREAM_DUMP_STEPUP` | (unattributed) | `=1` | prints `stepup: enter normal=… verdict=WALKABLE/STEEP …` on every step-up attempt | print-only; raw per-call `Environment.GetEnvironmentVariable` read outside a diagnostics-owner class (rule-5 violation); content is mirrored (not replaced) into the buffered `[transit-fail-stepup]` trace gated separately by `ACDREAM_DUMP_TRANSIT_FAIL` | raw read in `Transition.DoStepUp` (`TransitionTypes.cs:5991`, re-read every call — not cached) | | `ACDREAM_DUMP_TRANSIT_FAIL` | #345 | `=1` | buffers per-tick `[transit-fail-insert]`/`[transit-fail-stepup]`/`[transit-fail-walk]`/`[transit-fail-adjust]` trace lines into a `[ThreadStatic]` list and flushes them to console ONLY when a tick requested nonzero XY movement but delivered zero (self-selecting "stuck tick" predicate) | print-only, zero allocation when off (flag checked before touching any buffer per its own doc); buffer/list allocation only on ticks that are already stuck | `PhysicsDiagnostics.DumpTransitFailEnabled` | -| `ACDREAM_DUMP_VENDOR` | (unattributed) | `="1"` | `[vendor-diag]`-prefixed trace across ~25 call sites for two live-only vendor regressions: Chain A (far-click walk-to-use approach never opens the shop window) and Chain B (splittable vendor stack selection shows no quantity slider) | print-only (`Console.WriteLine`), verified true no-op when unset | `VendorDiagnostics.DumpVendorEnabled` (`VendorDiagnostics.cs:25-26`) — a proper diagnostics-owner class per Code Structure Rule 5, shared across App/Core.Net/Runtime | -| `ACDREAM_DUMP_VITALS` | (unattributed) | `="1"` | Logs every `PrivateUpdateVital(Current)` parse, every parsed `PlayerDescription` (vector flags/attr/spell counts), and `PlayerDescriptionParser` trailer/mid-walk `FormatException` failures with position | print-only at every site. `PlayerDescriptionParser.cs:458/473` re-read the env var raw inside `catch` blocks on every parse failure (rare, but scattered/uncached). | Read independently (not shared) at 4 sites: `WorldSession.cs:790-791` (`DumpVitalsEnabled`), `GameEventWiring.cs:1041` (local `dumpPd` at PlayerDescription registration), `PlayerDescriptionParser.cs:458` and `:473` (per-catch-block raw reads). Also mirrored (display-only, non-functional) via `DebugPanel.cs:240`/`DebugVM.cs:225`. | -| `ACDREAM_HIDE_PART` | (unattributed) | `=` | Hides one mesh part by index on entities with ≥10 parts (humanoids) — a debugging aid for equipment/clothing part-visibility issues. | Real (visible) behavior change, not print-only, but scoped to a single diagnostic index and off by default. | `RuntimeOptions.HidePartIndex` → `LivePresentationComposition.cs:608` → `LiveEntityAnimationPresenter.cs:21,38,243` | | `ACDREAM_LIGHT_DEBUG` | #176 | `=` (`int.TryParse`; unset/invalid → 0) | shader isolation mode uploaded as `uLightDebug` by `EnvCellRenderer` + `WbDrawDispatcher`: 0=off, 1=ambient-only vertex lighting, 2=kill dynamic point lights, 3=raw vLit visualization (texture ignored) | ALTERS RENDERED OUTPUT directly every draw pass (changes fragment-shader lighting/texturing) — not a log probe; no DebugPanel mirror | `RenderingDiagnostics.LightDebugMode` | | `ACDREAM_PROBE_BUILDING` | l.2d slice 1 | `=1` | gates the multi-line `[resolve-bldg]` BSP-shadow-hit trace in `TransitionTypes.FindObjCollisions`, one-time `[entity-source]` registration logs in `GameWindow`, `[door-cycle]` UM dispatch trail, and a one-shot `[setstate-hex]` wire dump of the first `SetState` (0xF74B) packet in `WorldSession` | print-only; also un-gates the `PhysicsDiagnostics.LastBspHitPoly` diagnostic side-channel (a static field write in `BSPQuery`/`FlatBspQuery`, read back by the `[resolve-bldg]` line) — no gameplay effect, but an extra static-field write per BSP hit while on; heavy output (one multi-line entry per BSP hit per physics tick) | `PhysicsDiagnostics.ProbeBuildingEnabled` | -| `ACDREAM_PROBE_CELL` | (unattributed) | `=1` | gates one `[cell-transit]` line per `PlayerMovementController.CellId` change (old→new cell, position, reason tag) | print-only; low volume (only on actual cell crossings) | `PhysicsDiagnostics.ProbeCellEnabled` | | `ACDREAM_PROBE_CELLSET` | a6.p5 | `=1` | gates `PhysicsDiagnostics.LogCellSetBuild`, one `[cellset-build]` line per `BuildCellSetAndPickContaining` call (seed cell, sphere XY, candidate list) from `CellTransit.cs:1468` | print-only; builds a `StringBuilder` of the candidate id list only when the flag is on | `PhysicsDiagnostics.ProbeCellSetEnabled` | | `ACDREAM_PROBE_CELL_CACHE` | indoor walking phase d | `=1` | gates one `[cell-cache]` line per EnvCell first-cached in `PhysicsDataCache.CacheCellStruct` (poly counts, BSP root structure) | print-only; fires at most once per EnvCell (cache is no-op after first population); no DebugPanel mirror | `PhysicsDiagnostics.ProbeCellCacheEnabled` | | `ACDREAM_PROBE_CHILD_CELL` | c4 route 7 | `=1` | gates one `[child-cell]` line per Runtime committed-child canonical-cell write in `RuntimeLiveEntitySessionController`, `RuntimeEntityObjectLifetime`, `RuntimeEntityDirectory` (parent/child guid, old/new cell, cause tag) | print-only | `PhysicsDiagnostics.ProbeChildCellEnabled` | @@ -359,3 +371,10 @@ from the "must still exist" check. | `ACDREAM_PROBE_WALK_MISS` | Issue #83 indoor walkable-plane miss trace. Investigation closed; stripped 2026-08-24 (#435). | none | | `ACDREAM_WIRE_MESH` | Issue #337 F2 overlay upgrade to real physics-BSP polygon edges. Investigation closed; stripped 2026-08-24 (#435) — F2 reverted to its proxy-cylinder overlay. | none | | `ACDREAM_WIRE_RADIUS` | Companion radius knob for `ACDREAM_WIRE_MESH`. Stripped alongside it 2026-08-24 (#435). | none | +| `ACDREAM_A8_DUMP_PV` | Phase A8.F portal-frame visual-gate triage dump (camera-cell portal census + EXIT-PROJ/EXIT-CLIP/EXIT trace in `PortalVisibilityBuilder.Build`). Phase A8 closed; stripped 2026-08-24 (#435 part 2). | none | +| `ACDREAM_DUMP_CLOTHING` | Issue #37 humanoid-coat clothing/part-swap trace. #37 closed 2026-05-11; stripped 2026-08-24 (#435 part 2). | none | +| `ACDREAM_DUMP_EDGE_SLIDE` | Issue #32 L.2c edge-slide/cliff-slide branch trace (five `edge-slide:` lines). #32 closed 2026-08-07; stripped 2026-08-24 (#435 part 2). | none | +| `ACDREAM_DUMP_LIVE_SPAWNS` | Phase A8 indoor-visibility batch live-spawn/DROP trace. Phase A8 closed; stripped 2026-08-24 (#435 part 2). | none | +| `ACDREAM_DUMP_STEPUP` | L.2.3d/e/f step-up `stepup: enter/SUCCESS/FAILED` trace. Investigation closed; stripped 2026-08-24 (#435 part 2) — its content is still covered by the separate `[transit-fail-stepup]` line under `ACDREAM_DUMP_TRANSIT_FAIL`. | `ACDREAM_DUMP_TRANSIT_FAIL` | +| `ACDREAM_DUMP_VENDOR` | `[vendor-diag]` trace (~25 call sites) for two vendor-approach/split-stack regressions. Vendor campaign closed 2026-08-08; stripped 2026-08-24 (#435 part 2) along with its owner class `VendorDiagnostics.cs`. | none | +| `ACDREAM_DUMP_VITALS` | Issue #5 `PrivateUpdateVital`/`PlayerDescription`/parse-failure trace across 4 sites. #5 closed 2026-04-25; stripped 2026-08-24 (#435 part 2). | none | diff --git a/src/AcDream.App/Composition/SessionPlayerComposition.cs b/src/AcDream.App/Composition/SessionPlayerComposition.cs index 879b9920..bbb7bb9d 100644 --- a/src/AcDream.App/Composition/SessionPlayerComposition.cs +++ b/src/AcDream.App/Composition/SessionPlayerComposition.cs @@ -562,8 +562,7 @@ internal sealed class SessionPlayerCompositionPhase d.EntityObjects, teardown, d.PlayerIdentity, - dormantLiveEntities, - d.Options.DumpLiveSpawns ? d.Log : null); + dormantLiveEntities); // 2026-08-08 vendor-approach root cause: the local player's // publication-chain host resolves moveto/sticky targets through // RuntimePhysicsState.ResolveObjectTableHost. Bind the graphical @@ -709,7 +708,6 @@ internal sealed class SessionPlayerCompositionPhase d.PlayerIdentity, deletion, dormantLiveEntities, - d.Options.DumpLiveSpawns ? d.Log : null, firstEntryDrive, acceptedPositionDrive); bindings.Adopt( diff --git a/src/AcDream.App/Interaction/PlayerInteractionMovementSink.cs b/src/AcDream.App/Interaction/PlayerInteractionMovementSink.cs index b2161cad..0b6dd15f 100644 --- a/src/AcDream.App/Interaction/PlayerInteractionMovementSink.cs +++ b/src/AcDream.App/Interaction/PlayerInteractionMovementSink.cs @@ -61,14 +61,6 @@ internal sealed class PlayerInteractionMovementSink( Height = approach.TargetHeight, }; - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] BeginApproach guid=0x{approach.Target.ServerGuid:X8} " - + $"movementType={movement.Type} distanceToObject={parameters.DistanceToObject} " - + $"canCharge={parameters.CanCharge} target=0x{movement.ObjectId:X8}"); - } - // PerformMovement cancels at its head. Do it explicitly before the // intent is armed so cancellation of the preceding move cannot clear // the new request; the internal second call is then a retail no-op. diff --git a/src/AcDream.App/Interaction/SelectionInteractionController.cs b/src/AcDream.App/Interaction/SelectionInteractionController.cs index 27ef7851..35f40b98 100644 --- a/src/AcDream.App/Interaction/SelectionInteractionController.cs +++ b/src/AcDream.App/Interaction/SelectionInteractionController.cs @@ -181,11 +181,6 @@ internal sealed class SelectionInteractionController // ships with it. if (useImmediately && !_query.IsWieldedByPlayer(guid)) { - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] route=dblclick-world PickAndStoreSelection guid=0x{guid:X8} enqueue=Activate"); - } EnqueueIdentityBound( RuntimeQueuedInteractionKind.Activate, guid, @@ -221,11 +216,6 @@ internal sealed class SelectionInteractionController _toast?.Invoke("Nothing selected"); return; } - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] route=keyboard-use UseCurrentSelection guid=0x{selected:X8} enqueue=Use"); - } EnqueueIdentityBound( RuntimeQueuedInteractionKind.Use, selected, @@ -292,22 +282,10 @@ internal sealed class SelectionInteractionController bool ownedByPlayer = _items.IsOwnedByPlayer(serverGuid); bool useable = ownedByPlayer || _query.IsUseable(serverGuid); - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] RequestUse entry guid=0x{serverGuid:X8} ownedByPlayer={ownedByPlayer} useable={useable}"); - } - if (useable && _query.TryGetApproach(serverGuid, out InteractionApproach approach) && !approach.IsCloseRange) { - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] RequestUse guid=0x{serverGuid:X8} branch=approach-armed useRadius={approach.UseRadius} isCloseRange={approach.IsCloseRange}"); - } - // Genuinely out of range (a real walk, not just a turn) — // mirror SendPickup's arrival-gated shape: arm the transaction // on the approach token BEFORE the movement starts (so a @@ -327,17 +305,7 @@ internal sealed class SelectionInteractionController token.ControllerLifetime, token.ApproachGeneration), out _); - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] TryArmPostArrivalUse guid=0x{serverGuid:X8} armed={armed} approachToken=({token.ControllerLifetime},{token.ApproachGeneration})"); - } }); - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] BeginApproach-result guid=0x{serverGuid:X8} started={started} armed={armed} stopDistance={approach.UseRadius} target=0x{approach.Target.ServerGuid:X8}"); - } if (!started || !armed) { // Release whatever got captured (or the caller's own @@ -360,11 +328,6 @@ internal sealed class SelectionInteractionController // — keep retail's immediate send; ACE's own "already within use // distance" branch (Player_Move.cs:65-87) calls back synchronously, // so there is no arrival gap to race here. - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] RequestUse guid=0x{serverGuid:X8} branch=immediate-dispatch ownedByPlayer={ownedByPlayer} useable={useable}"); - } RuntimeInteractionDispatchResult result = _transactions.TryDispatchUse( serverGuid, @@ -373,11 +336,6 @@ internal sealed class SelectionInteractionController reservation, _transport, out uint sequence); - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] RequestUse guid=0x{serverGuid:X8} TryDispatchUse verdict={result} seq={sequence}"); - } if (result == RuntimeInteractionDispatchResult.NotInWorld) _toast?.Invoke("Not in world"); if (result == RuntimeInteractionDispatchResult.Dispatched) @@ -609,22 +567,6 @@ internal sealed class SelectionInteractionController RuntimePendingUse pending, bool accepted) { - if (VendorDiagnostics.DumpVendorEnabled) - { - // Diagnostic-only re-query — TryGetApproach is a pure read with - // no side effects, so an extra call here (gated off in - // production) cannot change RequestUse's own dispatch outcome. - string distanceText = "n/a"; - if (_query.TryGetApproach(pending.ServerGuid, out InteractionApproach diagApproach)) - { - float dx = diagApproach.Target.Entity.Position.X - diagApproach.Player.Position.X; - float dy = diagApproach.Target.Entity.Position.Y - diagApproach.Player.Position.Y; - distanceText = MathF.Sqrt(dx * dx + dy * dy).ToString("F2"); - } - Console.WriteLine( - $"[vendor-diag] HandleUseApproachCompletion guid=0x{pending.ServerGuid:X8} accepted={accepted} playerToTargetDist={distanceText}"); - } - if (!accepted) { pending.Reservation?.CancelBeforeDispatch(); diff --git a/src/AcDream.App/Interaction/WorldSelectionQuery.cs b/src/AcDream.App/Interaction/WorldSelectionQuery.cs index 9acbd662..883504bd 100644 --- a/src/AcDream.App/Interaction/WorldSelectionQuery.cs +++ b/src/AcDream.App/Interaction/WorldSelectionQuery.cs @@ -571,12 +571,6 @@ internal sealed class WorldSelectionQuery bool haveSpawn = _liveEntities.TryGetSnapshot(serverGuid, out var spawn); bool fromWire = haveSpawn && spawn.UseRadius is > 0f; float radius = fromWire ? spawn.UseRadius!.Value : DefaultUseRadius; - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] GetUseRadius guid=0x{serverGuid:X8} radius={radius} " - + $"source={(fromWire ? "wire" : "fallback-0.6")}"); - } return radius; } } diff --git a/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs b/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs index 665d757c..d3d618fb 100644 --- a/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs +++ b/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs @@ -58,17 +58,6 @@ internal sealed class DatLiveEntityProjectionMaterializer /// private readonly RuntimeWorldTransitState _transit; - private int _received; - private int _hydrated; - private int _noPosition; - private int _noSetup; - private int _missingSetup; - private int _noMesh; - private int _noCycle; - private int _zeroFramerate; - private int _singleFrame; - private int _missingPartFrames; - public DatLiveEntityProjectionMaterializer( RuntimeOptions options, IDatReaderWriter dats, @@ -116,16 +105,6 @@ internal sealed class DatLiveEntityProjectionMaterializer public void ResetSessionState() { - _received = 0; - _hydrated = 0; - _noPosition = 0; - _noSetup = 0; - _missingSetup = 0; - _noMesh = 0; - _noCycle = 0; - _zeroFramerate = 0; - _singleFrame = 0; - _missingPartFrames = 0; } public bool TryMaterialize( @@ -157,18 +136,10 @@ internal sealed class DatLiveEntityProjectionMaterializer if (appearanceUpdate is not null && expectedRecord is null) return false; - _received++; - bool dumpLiveSpawns = _options.DumpLiveSpawns; - DumpSpawn(canonicalSpawn, dumpLiveSpawns); - if (!_origin.IsKnown) return false; if (canonicalSpawn.Position is null || canonicalSpawn.SetupTableId is null) { - if (canonicalSpawn.Position is null) - _noPosition++; - else - _noSetup++; return false; } @@ -216,13 +187,6 @@ internal sealed class DatLiveEntityProjectionMaterializer _collisionAssets.CacheSetup(canonicalSpawn.SetupTableId.Value, setup); if (setup is null) { - _missingSetup++; - if (dumpLiveSpawns) - { - Console.WriteLine( - $"live: DROP setup dat 0x{canonicalSpawn.SetupTableId.Value:X8} missing " - + $"(guid=0x{canonicalSpawn.Guid:X8})"); - } return false; } @@ -248,14 +212,6 @@ internal sealed class DatLiveEntityProjectionMaterializer List flattened = [.. SetupMesh.Flatten(setup, idleFrame)]; IReadOnlyList animPartChanges = canonicalSpawn.AnimPartChanges ?? Array.Empty(); - bool dumpClothing = _options.DumpClothing && setup.Parts.Count >= 10; - DumpClothingHeader( - canonicalSpawn, - setup, - flattened, - idleFrame, - animPartChanges, - dumpClothing); foreach (CreateObject.AnimPartChange change in animPartChanges) { @@ -273,17 +229,14 @@ internal sealed class DatLiveEntityProjectionMaterializer baseId => ResolveCollisionPart(baseId)); if (_options.RetailCloseDegrades && IsIssue47HumanoidSetup(setup)) - ApplyRetailCloseDegrades(flattened, dumpClothing); + ApplyRetailCloseDegrades(flattened); IReadOnlyList textureChanges = canonicalSpawn.TextureChanges ?? Array.Empty(); Dictionary>? surfaceOverrides = ResolveSurfaceOverrides( - canonicalSpawn, flattened, - textureChanges, - dumpClothing, - dumpLiveSpawns); + textureChanges); float scale = canonicalSpawn.ObjScale ?? 1f; Matrix4x4 scaleMatrix = Matrix4x4.CreateScale(scale); @@ -294,7 +247,6 @@ internal sealed class DatLiveEntityProjectionMaterializer var indexedPartAvailable = new bool[flattened.Count]; var animatedPartTemplate = new LiveAnimationPartTemplate[flattened.Count]; var bounds = new LocalBoundsAccumulator(); - int clothingTriangles = 0; for (int partIndex = 0; partIndex < flattened.Count; partIndex++) { @@ -318,23 +270,10 @@ internal sealed class DatLiveEntityProjectionMaterializer drawable); if (gfx is null) { - if (dumpClothing) - Console.WriteLine($" EMIT part={partIndex:D2} gfx=0x{part.GfxObjId:X8} GFXOBJ_DAT_MISSING -> 0 tris"); continue; } _collisionAssets.CacheGfxObj(part.GfxObjId, gfx); - if (dumpClothing) - { - var subMeshes = GfxObjMesh.Build(gfx, _dats); - int triangles = 0; - foreach (var subMesh in subMeshes) - triangles += subMesh.Indices.Length / 3; - clothingTriangles += triangles; - Console.WriteLine( - $" EMIT part={partIndex:D2} gfx=0x{part.GfxObjId:X8} " - + $"subMeshes={subMeshes.Count} tris={triangles}"); - } if (GfxObjBounds.Get(gfx) is { } partBounds) bounds.Add(transform, partBounds); @@ -346,21 +285,8 @@ internal sealed class DatLiveEntityProjectionMaterializer if (meshRefs.Count == 0) { - _noMesh++; - if (dumpLiveSpawns) - { - Console.WriteLine( - $"live: DROP no mesh refs from setup 0x{canonicalSpawn.SetupTableId.Value:X8} " - + $"(guid=0x{canonicalSpawn.Guid:X8})"); - } return false; } - if (dumpClothing) - { - Console.WriteLine( - $" TOTAL tris={clothingTriangles} meshRefs={meshRefs.Count} " - + $"(parts.Count={flattened.Count})"); - } PaletteOverride? paletteOverride = CreatePaletteOverride(canonicalSpawn); PartOverride[] partOverrides = CreatePartOverrides(animPartChanges); @@ -456,7 +382,6 @@ internal sealed class DatLiveEntityProjectionMaterializer indexedPartAvailable, animatedPartTemplate, bounds, - dumpLiveSpawns, expectedCreateIntegrationVersion, synchronizeAnimation: supersessionRecovery); } @@ -477,9 +402,7 @@ internal sealed class DatLiveEntityProjectionMaterializer return slotZeroId; } - private void ApplyRetailCloseDegrades( - List parts, - bool dumpClothing) + private void ApplyRetailCloseDegrades(List parts) { for (int partIndex = 0; partIndex < parts.Count; partIndex++) { @@ -495,33 +418,13 @@ internal sealed class DatLiveEntityProjectionMaterializer } parts[partIndex] = new MeshRef(resolvedId, part.PartTransform); - if (dumpClothing) - { - Console.WriteLine( - $" DEGRADE part={partIndex:D2} gfx=0x{part.GfxObjId:X8} " - + $"-> close=0x{resolvedId:X8}"); - } } } private Dictionary>? ResolveSurfaceOverrides( - WorldSession.EntitySpawn spawn, IReadOnlyList parts, - IReadOnlyList textureChanges, - bool dumpClothing, - bool dumpLiveSpawns) + IReadOnlyList textureChanges) { - if (dumpClothing) - { - Console.WriteLine($" TextureChanges count={textureChanges.Count}"); - foreach (CreateObject.TextureChange change in textureChanges) - { - Console.WriteLine( - $" TC part={change.PartIndex:D2} oldTex=0x{change.OldTexture:X8} " - + $"-> newTex=0x{change.NewTexture:X8}"); - } - } - if (textureChanges.Count == 0) return null; @@ -536,8 +439,6 @@ internal sealed class DatLiveEntityProjectionMaterializer oldToNew[change.OldTexture] = change.NewTexture; } - bool statueDiagnostic = dumpLiveSpawns - && spawn.Name?.Contains("Statue", StringComparison.OrdinalIgnoreCase) == true; var result = new Dictionary>(); for (int partIndex = 0; partIndex < parts.Count; partIndex++) { @@ -547,12 +448,6 @@ internal sealed class DatLiveEntityProjectionMaterializer GfxObj? gfx = _dats.Get(parts[partIndex].GfxObjId); if (gfx is null) { - if (statueDiagnostic) - { - Console.WriteLine( - $"live: [STATUE] resolve part={partIndex} " - + $"GfxObj 0x{parts[partIndex].GfxObjId:X8} missing"); - } continue; } _collisionAssets.CacheGfxObj(parts[partIndex].GfxObjId, gfx); @@ -729,7 +624,6 @@ internal sealed class DatLiveEntityProjectionMaterializer IReadOnlyList indexedPartAvailable, IReadOnlyList animatedPartTemplate, LocalBoundsAccumulator bounds, - bool dumpLiveSpawns, ulong expectedCreateIntegrationVersion, bool synchronizeAnimation) { @@ -835,7 +729,6 @@ internal sealed class DatLiveEntityProjectionMaterializer _worldEvents.UpsertCurrent(snapshot); if (_runtime.TryMarkWorldSpawnPublished(spawn.Guid)) _worldEvents.FireEntitySpawned(snapshot); - _hydrated++; if (!_runtime.IsCurrentCreateIntegration( expectedRecord, @@ -921,18 +814,6 @@ internal sealed class DatLiveEntityProjectionMaterializer return false; } - if (dumpLiveSpawns && _received % 20 == 0) - { - Console.WriteLine( - $"live: animated={_runtime.AnimationRuntimeCount} " - + $"animReject: noCycle={_noCycle} fr0={_zeroFramerate} " - + $"1frame={_singleFrame} partFrames={_missingPartFrames}"); - Console.WriteLine( - $"live: summary recv={_received} hydrated={_hydrated} " - + $"drops: noPos={_noPosition} noSetup={_noSetup} " - + $"setupMissing={_missingSetup} noMesh={_noMesh}"); - } - return _runtime.IsCurrentCreateIntegration( expectedRecord, expectedCreateIntegrationVersion); @@ -961,18 +842,6 @@ internal sealed class DatLiveEntityProjectionMaterializer spawn, idleCycle); } - if (!retainedAnimation) - { - if (idleCycle is null) - _noCycle++; - else if (idleCycle.Framerate == 0f) - _zeroFramerate++; - else if (idleCycle.HighFrame <= idleCycle.LowFrame) - _singleFrame++; - else if (idleCycle.Animation.PartFrames.Count <= 1) - _missingPartFrames++; - } - if (!retainedAnimation && idleCycle is not null && idleCycle.Framerate != 0f @@ -1159,56 +1028,6 @@ internal sealed class DatLiveEntityProjectionMaterializer : null; } - private void DumpSpawn(WorldSession.EntitySpawn spawn, bool enabled) - { - if (!enabled) - return; - - string position = spawn.Position is { } p - ? $"({p.PositionX:F1},{p.PositionY:F1},{p.PositionZ:F1})@0x{p.LandblockId:X8}" - : "no-pos"; - string setup = spawn.SetupTableId is { } setupId - ? $"0x{setupId:X8}" - : "no-setup"; - string physicsTable = spawn.Physics?.PhysicsScriptTableId is { } tableId - ? $"0x{tableId:X8}" - : "no-petable"; - string name = spawn.Name is { Length: > 0 } foundName - ? $"\"{foundName}\"" - : "no-name"; - string itemType = spawn.ItemType is { } foundItemType - ? $"0x{foundItemType:X8}" - : "no-itemtype"; - Console.WriteLine( - $"live: spawn guid=0x{spawn.Guid:X8} name={name} setup={setup} pos={position} " - + $"petable={physicsTable} itemType={itemType} " - + $"animParts={spawn.AnimPartChanges?.Count ?? 0} " - + $"texChanges={spawn.TextureChanges?.Count ?? 0} " - + $"subPalettes={spawn.SubPalettes?.Count ?? 0}"); - } - - private void DumpClothingHeader( - WorldSession.EntitySpawn spawn, - Setup setup, - IReadOnlyList flattened, - AnimationFrame? idleFrame, - IReadOnlyList changes, - bool enabled) - { - if (!enabled) - return; - - Console.WriteLine( - $"\n=== DUMP_CLOTHING: guid=0x{spawn.Guid:X8} name='{spawn.Name}' " - + $"setup=0x{setup.Id:X8} setup.Parts.Count={setup.Parts.Count} " - + $"flatten.Count={flattened.Count} APC={changes.Count} ==="); - foreach (CreateObject.AnimPartChange change in changes) - Console.WriteLine($" APC part={change.PartIndex:D2} -> gfx=0x{change.NewModelId:X8}"); - Console.WriteLine( - $" basePalette=0x{spawn.BasePaletteId ?? 0:X8} " - + $"subPalettes={spawn.SubPalettes?.Count ?? 0}"); - } - private static bool IsIssue47HumanoidSetup(Setup setup) { if (setup.Parts.Count != 34) diff --git a/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs b/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs index 2edaaf2a..a9164297 100644 --- a/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs +++ b/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs @@ -265,12 +265,6 @@ public static class PortalVisibilityBuilder // stands near a doorway plane). private const float SeedInPlaneEpsilon = 0.0002f; - // TEMP diagnostic (Phase A8.F visual-gate triage; strip after): ACDREAM_A8_DUMP_PV=1 dumps the - // local→NDC→clipped portal geometry for the first 2 Build calls per distinct camera cell. - private static readonly bool s_pvDump = - Environment.GetEnvironmentVariable("ACDREAM_A8_DUMP_PV") == "1"; - private static readonly Dictionary s_pvDumpCount = new(); - /// /// #120 observable: total convergence-tripwire firings across both the /// interior and the exterior look-in propagation. @@ -386,36 +380,6 @@ public static class PortalVisibilityBuilder int churnReenqueues = 0; var churnReciprocal = churnProbe ? new System.Text.StringBuilder(256) : null; - bool pvDump = false; - if (s_pvDump) - { - lock (s_pvDumpCount) - { - s_pvDumpCount.TryGetValue(cameraCell.CellId, out int dc); - if (dc < 2) { s_pvDumpCount[cameraCell.CellId] = dc + 1; pvDump = true; } - } - if (pvDump) - { - Console.WriteLine($"[pv-dump] camCell=0x{cameraCell.CellId:X8} portals={cameraCell.Portals.Count} polyLists={cameraCell.PortalPolygons.Count} vp[M11={viewProj.M11:F3} M22={viewProj.M22:F3} M33={viewProj.M33:F3} M34={viewProj.M34:F3} M43={viewProj.M43:F3} M44={viewProj.M44:F3}]"); - // Camera-cell portal census (A8.F triage 2026-05-29): report, for EVERY - // portal, the exact inputs the BFS guards read — BEFORE the guards run, so - // a portal the loop silently `continue`s past is still visible here. An - // empty OUTSIDEVIEW can then be traced to the precise gate: polyLen<3 (empty - // polygon from EnvCellLandblockBuildBuilder), interiorSide=false (camera back-facing the - // portal — a legitimately-empty result, not a bug), or (if both OK) a - // downstream projection/clip failure shown by the EXIT-PROJ/EXIT-CLIP lines. - for (int ci = 0; ci < cameraCell.Portals.Count; ci++) - { - int plen = ci < cameraCell.PortalPolygons.Count - ? (cameraCell.PortalPolygons[ci]?.Length ?? -1) : -2; - bool hasPlane = ci < cameraCell.ClipPlanes.Count; - bool interiorSide = !hasPlane || CameraOnInteriorSide(cameraCell, ci, cameraPos); - var n = hasPlane ? cameraCell.ClipPlanes[ci].Normal : Vector3.Zero; - Console.WriteLine($"[pv-dump] CAMPORTAL[{ci}] other=0x{cameraCell.Portals[ci].OtherCellId:X4} polyLen={plen} hasPlane={hasPlane} interiorSide={interiorSide} planeN=({n.X:F3},{n.Y:F3},{n.Z:F3})"); - } - } - } - // T2 (BR-4): retail's growth propagation is IN PLACE, never by re-enqueue // — PView::AddViewToPortals (Ghidra 0x005a52d0, pc:433446): first // discovery enqueues via InsCellTodoList; growth into a cell whose @@ -481,8 +445,6 @@ public static class PortalVisibilityBuilder continue; } - bool dx = pvDump && cell.Portals[i].OtherCellId == 0xFFFF; - // (R-A2b Phase 1 pin, throwaway) Log the side-test inputs for EVERY portal so a back-portal // traversal (cell=0x..0173 p->0x0171) can be attributed to the side test. // Strip with the rest of the [pv-trace] apparatus. @@ -505,7 +467,6 @@ public static class PortalVisibilityBuilder && !CameraOnInteriorSide(cell, i, cameraPos)) { trace?.Add($"portal cell=0x{cell.CellId:X8} p{i}->0x{portal.OtherCellId:X4} skip=side"); - if (dx) Console.WriteLine($"[pv-dump] EXIT-CULLED(side) cell=0x{cell.CellId:X8} p{i} localN={poly.Length} hasClipPlane={(i < cell.ClipPlanes.Count)}"); continue; } @@ -526,8 +487,6 @@ public static class PortalVisibilityBuilder endCount - processedCount, clippedRegion, out int clipVerts); - if (dx) Console.WriteLine($"[pv-dump] EXIT-PROJ cell=0x{cell.CellId:X8} p{i} localN={poly.Length} clipN={clipVerts} local0=({poly[0].X:F2},{poly[0].Y:F2},{poly[0].Z:F2})"); - if (dx) Console.WriteLine($"[pv-dump] EXIT-CLIP cell=0x{cell.CellId:X8} p{i} currentViewPolys={currentView.Polygons.Count} clipResult={clippedRegion.Count}"); // Empty clip = no flood through this portal, period — retail's empty-GetClip rule // (polyClipFinish <3 survivors → reject; ClipPortals adds no view). The @@ -544,13 +503,6 @@ public static class PortalVisibilityBuilder if (portal.OtherCellId == 0xFFFF) { - if (pvDump) - { - Console.WriteLine($"[pv-dump] EXIT cell=0x{cell.CellId:X8} p{i} localN={poly.Length} clipVerts={clipVerts} clipPolys={clippedRegion.Count}"); - Console.WriteLine($"[pv-dump] local=[{string.Join(" ", System.Array.ConvertAll(poly, v => $"({v.X:F2},{v.Y:F2},{v.Z:F2})"))}]"); - foreach (var cp in clippedRegion) - Console.WriteLine($"[pv-dump] clipped({cp.Vertices.Length})=[{string.Join(" ", System.Array.ConvertAll((Vector2[])cp.Vertices, v => $"({v.X:F3},{v.Y:F3})"))}]"); - } // Exit portal -> outdoors visible through this (clipped) opening. // OutsideView gates DRAWN color (terrain/sky/scissor), and the // shell that rasterizes this aperture draws +drawLiftZ above @@ -688,9 +640,6 @@ public static class PortalVisibilityBuilder ProcessCellPortals(cell, 0); } - if (pvDump) - Console.WriteLine($"[pv-dump] OUTSIDEVIEW polys={frame.OutsideView.Polygons.Count} bfsCellViews={frame.CellViews.Count} crossBldg={frame.CrossBuildingViews.Count}"); - // Phase U.4c flap probe (ACDREAM_PROBE_FLAP) — read-only per-frame snapshot of the // root cell's per-portal side-test + projection + the frame's exit/visible counts. if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeFlapEnabled) diff --git a/src/AcDream.App/RuntimeOptions.cs b/src/AcDream.App/RuntimeOptions.cs index d4c0e500..22e6679a 100644 --- a/src/AcDream.App/RuntimeOptions.cs +++ b/src/AcDream.App/RuntimeOptions.cs @@ -42,14 +42,16 @@ public sealed record RuntimeOptions( string? LivePass, bool DevTools, bool UncappedRendering, + /// #435 kept deliberately: NOT a spent probe. The canonical + /// nine-stop soak (`tools/run-connected-r6-soak.ps1`) hard-gates on the + /// `move-truth OUT` records this emits — it is the automated proof that + /// production input produced outbound movement traffic. bool DumpMoveTruth, bool DumpSky, bool NoAudio, int HidePartIndex, bool RetailCloseDegrades, bool DumpSceneryZ, - bool DumpLiveSpawns, - bool DumpClothing, int? LegacyStreamRadius, bool RetailUi, /// Campaign CC slice CC4: interim env/test-only seam that opens @@ -157,8 +159,6 @@ public sealed record RuntimeOptions( // only for before/after diagnostic comparisons. RetailCloseDegrades: !string.Equals(env("ACDREAM_RETAIL_CLOSE_DEGRADES"), "0", StringComparison.Ordinal), DumpSceneryZ: IsExactlyOne(env("ACDREAM_DUMP_SCENERY_Z")), - DumpLiveSpawns: IsExactlyOne(env("ACDREAM_DUMP_LIVE_SPAWNS")), - DumpClothing: IsExactlyOne(env("ACDREAM_DUMP_CLOTHING")), // Legacy override for ACDREAM_STREAM_RADIUS. Caller applies it on // top of the quality preset's radii. Null when unset or invalid. LegacyStreamRadius: TryParseNonNegativeInt(env("ACDREAM_STREAM_RADIUS")), diff --git a/src/AcDream.App/UI/ItemInteractionController.cs b/src/AcDream.App/UI/ItemInteractionController.cs index 5a327efb..a0515a53 100644 --- a/src/AcDream.App/UI/ItemInteractionController.cs +++ b/src/AcDream.App/UI/ItemInteractionController.cs @@ -525,11 +525,6 @@ public sealed class ItemInteractionController : IDisposable switch (mode) { case InteractionModeKind.Use: - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] route=target-mode-click OfferPrimaryClick(Use) guid=0x{targetGuid:X8}"); - } ClearTargetMode(); accepted = ActivateItem(targetGuid); break; @@ -562,11 +557,6 @@ public sealed class ItemInteractionController : IDisposable /// public bool UseSelectedOrEnterMode(uint selectedObjectId) { - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] route=toolbar-or-queued-use UseSelectedOrEnterMode guid=0x{selectedObjectId:X8}"); - } if (selectedObjectId != 0) return ActivateItem(selectedObjectId); return _interactionState.EnterUse(); @@ -666,11 +656,6 @@ public sealed class ItemInteractionController : IDisposable public bool ActivateItem(uint itemGuid) { - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] entry:ActivateItem guid=0x{itemGuid:X8} isTargetModeActive={IsTargetModeActive}"); - } if (itemGuid == 0) return false; if (IsTargetModeActive) @@ -997,11 +982,6 @@ public sealed class ItemInteractionController : IDisposable /// public bool ExecuteConfirmedUse(uint objectId) { - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] route=confirmed-use ExecuteConfirmedUse guid=0x{objectId:X8}"); - } if (objectId == 0u || (_requestUse is null && _sendUse is null)) return false; if (!EnsureInventoryRequestReady()) diff --git a/src/AcDream.App/UI/Layout/SelectedObjectController.cs b/src/AcDream.App/UI/Layout/SelectedObjectController.cs index 7076e794..1f4ba9c7 100644 --- a/src/AcDream.App/UI/Layout/SelectedObjectController.cs +++ b/src/AcDream.App/UI/Layout/SelectedObjectController.cs @@ -377,13 +377,6 @@ public sealed class SelectedObjectController : IRetainedPanelController ? $"{stackSize} {objectName}" : objectName; - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] ApplySelection guid=0x{g:X8} stackSizeOperand={stackSize} " - + $"objectName={objectName ?? "null"} builtLabel={_currentName ?? "null"}"); - } - // ── 3. Selection overlay: brief flash (retail container ObjectSelected // = Pause(0.25s)→Normal). "StackedItemSelected" for stacks. ────────────── SetOverlayState(stackSize > 1u @@ -410,18 +403,6 @@ public sealed class SelectedObjectController : IRetainedPanelController _splitQuantity.Reset(stackSize, initialValue: seed); if (_stackSizeEntry is not null) _stackSizeEntry.Visible = true; if (_stackSizeSlider is not null) _stackSizeSlider.Visible = true; - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] ApplySelection guid=0x{g:X8} sliderVisible=true " - + $"isVendorSplitExempt={vendorSplitExempt} maxSplitSize={stackSize} seed={seed}"); - } - } - else if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] ApplySelection guid=0x{g:X8} sliderVisible=false " - + $"failingPredicate=stackSize<=1u stackSize={stackSize}"); } // ── 4. Health: query, and show the meter only if real health is already known. diff --git a/src/AcDream.App/World/LiveEntityDeletionController.cs b/src/AcDream.App/World/LiveEntityDeletionController.cs index bebeecfd..8bc67bf0 100644 --- a/src/AcDream.App/World/LiveEntityDeletionController.cs +++ b/src/AcDream.App/World/LiveEntityDeletionController.cs @@ -21,15 +21,13 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink private readonly ILiveEntityTeardownCoordinator _teardown; private readonly ILocalPlayerIdentitySource _identity; private readonly DormantLiveEntityStore _dormant; - private readonly Action? _diagnostic; public LiveEntityDeletionController( LiveEntityRuntime runtime, RuntimeEntityObjectLifetime entityObjects, ILiveEntityTeardownCoordinator teardown, ILocalPlayerIdentitySource identity, - DormantLiveEntityStore? dormant = null, - Action? diagnostic = null) + DormantLiveEntityStore? dormant = null) { _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); _entityObjects = entityObjects @@ -37,7 +35,6 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink _teardown = teardown ?? throw new ArgumentNullException(nameof(teardown)); _identity = identity ?? throw new ArgumentNullException(nameof(identity)); _dormant = dormant ?? new DormantLiveEntityStore(); - _diagnostic = diagnostic; } /// @@ -64,13 +61,6 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink if (removed) { _dormant.RemoveExact(delete); - _diagnostic?.Invoke( - $"live: delete guid=0x{delete.Guid:X8} instSeq={delete.InstanceSequence}"); - } - else if (removedDormant) - { - _diagnostic?.Invoke( - $"live: delete dormant guid=0x{delete.Guid:X8} instSeq={delete.InstanceSequence}"); } return removed || removedDormant; } @@ -95,8 +85,6 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink return false; _dormant.Retain(snapshot); - _diagnostic?.Invoke( - $"live: dormant guid=0x{candidate.ServerGuid:X8} instSeq={candidate.Generation}"); return true; } } diff --git a/src/AcDream.App/World/LiveEntityHydrationController.cs b/src/AcDream.App/World/LiveEntityHydrationController.cs index c23c6e45..a21404a0 100644 --- a/src/AcDream.App/World/LiveEntityHydrationController.cs +++ b/src/AcDream.App/World/LiveEntityHydrationController.cs @@ -180,7 +180,6 @@ internal sealed class LiveEntityHydrationController : ILiveEntityLandblockLoaded private readonly ILocalPlayerIdentitySource _identity; private readonly LiveEntityDeletionController _deletion; private readonly DormantLiveEntityStore _dormant; - private readonly Action? _diagnostic; /// /// C3c: the graphical first-entry drive pump — pumped at the end of each /// Create transaction so a fresh residence drives its conductor @@ -219,7 +218,6 @@ internal sealed class LiveEntityHydrationController : ILiveEntityLandblockLoaded ILocalPlayerIdentitySource identity, LiveEntityDeletionController deletion, DormantLiveEntityStore? dormant = null, - Action? diagnostic = null, RuntimeFirstEntryDriveController? firstEntry = null, RuntimeAcceptedPositionDriveController? acceptedPositionDrive = null) { @@ -236,7 +234,6 @@ internal sealed class LiveEntityHydrationController : ILiveEntityLandblockLoaded _identity = identity ?? throw new ArgumentNullException(nameof(identity)); _deletion = deletion ?? throw new ArgumentNullException(nameof(deletion)); _dormant = dormant ?? new DormantLiveEntityStore(); - _diagnostic = diagnostic; _firstEntry = firstEntry; _acceptedPositionDrive = acceptedPositionDrive; } @@ -465,11 +462,6 @@ AppearanceSynchronization: ChildUnparentDisposition disposition = _relationships.OnChildBecameUnparented(update.Guid); bool accepted = disposition is not ChildUnparentDisposition.Superseded; - if (accepted) - { - _diagnostic?.Invoke( - $"live: pickup guid=0x{update.Guid:X8} instSeq={update.InstanceSequence} posSeq={update.PositionSequence}"); - } return accepted; } @@ -601,7 +593,6 @@ AppearanceSynchronization: if (candidates.Count == 0) return; - int projected = 0; lock (_datLock) { foreach (RuntimeEntityRecord candidate in candidates) @@ -623,26 +614,21 @@ AppearanceSynchronization: candidate.Snapshot, LiveProjectionPurpose.CreateSupersessionRecovery)) { - projected++; projectedCanonicalAppearance = true; } } else if (record?.WorldEntity is not null && record.InitialHydrationCompleted) { - if (_runtime.RebucketLiveEntity( - record.ServerGuid, - record.ProjectionCellId)) - { - projected++; - } + _runtime.RebucketLiveEntity( + record.ServerGuid, + record.ProjectionCellId); } else if (ProjectExact( candidate, candidate.Snapshot, LiveProjectionPurpose.SpatialRecovery)) { - projected++; projectedCanonicalAppearance = true; } @@ -658,21 +644,14 @@ AppearanceSynchronization: if (record is not null && _runtime.IsCurrentRecord(record) - && record.AppearanceProjectionSynchronizationPending - && SynchronizeAppearance( - record, - record.ObjDescAuthorityVersion)) + && record.AppearanceProjectionSynchronizationPending) { - projected++; + SynchronizeAppearance( + record, + record.ObjDescAuthorityVersion); } } } - - if (projected > 0) - { - _diagnostic?.Invoke( - $"live: re-projected {projected} server object(s) into landblock 0x{loadedLandblockId:X8}"); - } } public bool EnsureWorldOrigin( diff --git a/src/AcDream.Core.Net/GameEventWiring.cs b/src/AcDream.Core.Net/GameEventWiring.cs index 81d3160f..554008fc 100644 --- a/src/AcDream.Core.Net/GameEventWiring.cs +++ b/src/AcDream.Core.Net/GameEventWiring.cs @@ -822,13 +822,6 @@ public static class GameEventWiring registrar.Register(GameEventType.ApproachVendor, e => { var p = VendorApproach.TryParse(e.Payload.Span); - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] ApproachVendor(0x0062) inbound parsed={p is not null} " - + $"vendorGuid={(p is null ? "n/a" : $"0x{p.Value.VendorGuid:X8}")} " - + $"itemCount={(p is null ? "n/a" : p.Value.Items.Count.ToString())}"); - } if (p is null) return; var profile = new VendorShopProfile( @@ -846,14 +839,6 @@ public static class GameEventWiring for (int i = 0; i < shopItems.Length; i++) { VendorApproach.ItemProfile item = p.Value.Items[i]; - if (VendorDiagnostics.DumpVendorEnabled && i < 5) - { - Console.WriteLine( - $"[vendor-diag] ApproachVendor wire-item[{i}] guid=0x{item.ItemGuid:X8} " - + $"name={item.Desc.Name ?? "null"} " - + $"descStackSize={(item.Desc.StackSize is { } ds ? ds.ToString() : "null")} " - + $"stackSizeMax={(item.Desc.StackSizeMax is { } sm ? sm.ToString() : "null")}"); - } shopItems[i] = new VendorShopItem( item.ItemGuid, item.StackSize, @@ -950,12 +935,6 @@ public static class GameEventWiring registrar.Register(GameEventType.UseDone, e => { uint? err = GameEvents.ParseUseDone(e.Payload.Span); - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] UseDone(0x01C7) inbound parsed={err is not null} " - + $"err={(err is null ? "n/a" : $"0x{err.Value:X4}")}"); - } if (err is null) return; // Already the diagnostics-only log line SHOULD-FIX 4 asks for — // it fires unconditionally, so an unmapped code below stays @@ -1038,12 +1017,9 @@ public static class GameEventWiring // player this is the authoritative source (the per-item // SpellBook flag in IdentifyObjectResponse is for caster // items / scrolls only). - bool dumpPd = Environment.GetEnvironmentVariable("ACDREAM_DUMP_VITALS") == "1"; registrar.Register(GameEventType.PlayerDescription, e => { var p = PlayerDescriptionParser.TryParse(e.Payload.Span); - if (dumpPd) - Console.WriteLine($"vitals: PlayerDescription body.len={e.Payload.Length} parsed={(p is null ? "NULL" : $"vec={p.Value.VectorFlags} attrs={p.Value.Attributes.Count} spells={p.Value.Spells.Count}")}"); if (p is null) return; // R3: a trailer-truncated parse carries zero placeholder option @@ -1106,8 +1082,6 @@ public static class GameEventWiring if (attr.Current is uint cur) { // Vital entry (id 7/8/9) — has absolute current. - if (dumpPd) - Console.WriteLine($"vitals: PD-vital id={attr.AtType} ranks={attr.Ranks} start={attr.Start} cur={cur}"); localPlayer.OnVitalUpdate( vitalId: attr.AtType, ranks: attr.Ranks, @@ -1120,8 +1094,6 @@ public static class GameEventWiring // Primary attribute (id 1..6) — Endurance+Self feed // the vital max formula (Endurance/2 for Health, // Endurance for Stamina, Self for Mana). - if (dumpPd) - Console.WriteLine($"vitals: PD-attr id={attr.AtType} ranks={attr.Ranks} start={attr.Start}"); localPlayer.OnAttributeUpdate( atType: attr.AtType, ranks: attr.Ranks, @@ -1174,10 +1146,6 @@ public static class GameEventWiring int total = (int)(formulaBonus + s.Init + s.Ranks); if (s.SkillId == 24u) runSkill = total; else if (s.SkillId == 22u) jumpSkill = total; - - if (dumpPd) - Console.WriteLine( - $"vitals: PD-skill id={s.SkillId} init={s.Init} ranks={s.Ranks} formulaBonus={formulaBonus} total={total}"); } if (runSkill >= 0 || jumpSkill >= 0) onSkillsUpdated?.Invoke(runSkill, jumpSkill); diff --git a/src/AcDream.Core.Net/Messages/PlayerDescriptionParser.cs b/src/AcDream.Core.Net/Messages/PlayerDescriptionParser.cs index 765b97fe..8fdb6fdf 100644 --- a/src/AcDream.Core.Net/Messages/PlayerDescriptionParser.cs +++ b/src/AcDream.Core.Net/Messages/PlayerDescriptionParser.cs @@ -447,7 +447,7 @@ public static class PlayerDescriptionParser } } } - catch (FormatException ex) + catch (FormatException) { // Trailer corrupted — keep what we have and flag it. Once // Tasks 3-9 add list reads inside this try block, partial @@ -455,8 +455,6 @@ public static class PlayerDescriptionParser // them so they can ignore the trailer if they need all-or- // nothing semantics. trailerTruncated = true; - if (System.Environment.GetEnvironmentVariable("ACDREAM_DUMP_VITALS") == "1") - System.Console.WriteLine($"PlayerDescriptionParser: trailer FormatException at pos={pos}/{payload.Length}: {ex.Message}"); } return new Parsed( @@ -466,12 +464,9 @@ public static class PlayerDescriptionParser shortcuts, hotbarSpells, desiredComps, spellbookFilters, gameplayOptions, inventory, equipped, trailerTruncated); } - catch (FormatException ex) + catch (FormatException) { // Truncation mid-walk — return null so caller knows parse failed. - // Diagnostic when ACDREAM_DUMP_VITALS=1 surfaces the failure point. - if (System.Environment.GetEnvironmentVariable("ACDREAM_DUMP_VITALS") == "1") - System.Console.WriteLine($"PlayerDescriptionParser: FormatException at pos={pos}/{payload.Length}: {ex.Message}"); return null; } } diff --git a/src/AcDream.Core.Net/WorldSession.cs b/src/AcDream.Core.Net/WorldSession.cs index c513e699..2b83fc50 100644 --- a/src/AcDream.Core.Net/WorldSession.cs +++ b/src/AcDream.Core.Net/WorldSession.cs @@ -780,15 +780,12 @@ public sealed class WorldSession : IDisposable // Issue #5 diagnostics (env-var-gated): // ACDREAM_DUMP_OPCODES=1 → log first occurrence of each unhandled opcode - // ACDREAM_DUMP_VITALS=1 → log every PrivateUpdateVital(Current) parse // ACDREAM_DUMP_APPEARANCE=1 → log every 0xF625 ObjDescEvent + 0xF7DB UpdateObject // with body len, target guid, hex preview. Used to // debug remote-player appearance asymmetry (retail // observer in acdream renders wrong skin/hair). private static readonly bool DumpOpcodesEnabled = Environment.GetEnvironmentVariable("ACDREAM_DUMP_OPCODES") == "1"; - private static readonly bool DumpVitalsEnabled = - Environment.GetEnvironmentVariable("ACDREAM_DUMP_VITALS") == "1"; private static readonly bool DumpAppearanceEnabled = Environment.GetEnvironmentVariable("ACDREAM_DUMP_APPEARANCE") == "1"; private readonly System.Collections.Generic.HashSet _seenUnhandledOpcodes = new(); @@ -2249,8 +2246,6 @@ public sealed class WorldSession : IDisposable // format per holtburger UpdateVital — see // PrivateUpdateVital.TryParseFull. var parsed = PrivateUpdateVital.TryParseFull(body); - if (DumpVitalsEnabled) - Console.WriteLine($"vitals: 0x02E7 PrivateUpdateVital body.len={body.Length} parsed={(parsed is null ? "null" : $"v{parsed.Value.VitalId} ranks={parsed.Value.Ranks} start={parsed.Value.Start} cur={parsed.Value.Current}")}"); if (parsed is not null) VitalUpdated?.Invoke(parsed.Value); } @@ -2259,8 +2254,6 @@ public sealed class WorldSession : IDisposable // Issue #5: current-only delta (regen ticks / drains). // Wire format per holtburger UpdateVitalCurrent. var parsed = PrivateUpdateVital.TryParseCurrent(body); - if (DumpVitalsEnabled) - Console.WriteLine($"vitals: 0x02E9 PrivateUpdateVitalCurrent body.len={body.Length} parsed={(parsed is null ? "null" : $"v{parsed.Value.VitalId} cur={parsed.Value.Current}")}"); if (parsed is not null) VitalCurrentUpdated?.Invoke(parsed.Value); } diff --git a/src/AcDream.Core/Items/VendorDiagnostics.cs b/src/AcDream.Core/Items/VendorDiagnostics.cs deleted file mode 100644 index 9e8d058e..00000000 --- a/src/AcDream.Core/Items/VendorDiagnostics.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace AcDream.Core.Items; - -/// -/// TEMPORARY diagnostic-probe owner for two live-only regressions that have -/// each survived two green-tested fixes: Chain A (a far-click walk-to-use -/// approaches a vendor, the vendor plays its cosmetic greeting, but the shop -/// window never opens) and Chain B (selecting a splittable vendor stack -/// shows the bare item name with no quantity slider). Every probe line is -/// prefixed [vendor-diag] and gated on -/// so the family is a true no-op when the flag is unset. -/// -/// -/// Read once from ACDREAM_DUMP_VENDOR=1 at process start, per Code -/// Structure Rule 5 (one static diagnostic-owner class per subsystem, no -/// per-call-site reads). -/// Lives in AcDream.Core — the one project every call site (App, -/// Core.Net, Runtime) already references — so a single flag instance is -/// shared across the whole probe family regardless of which layer observes -/// it first. -/// -public static class VendorDiagnostics -{ - public static bool DumpVendorEnabled { get; } = - Environment.GetEnvironmentVariable("ACDREAM_DUMP_VENDOR") == "1"; -} diff --git a/src/AcDream.Core/Physics/PhysicsDiagnostics.cs b/src/AcDream.Core/Physics/PhysicsDiagnostics.cs index 0f3f0102..cc8a9231 100644 --- a/src/AcDream.Core/Physics/PhysicsDiagnostics.cs +++ b/src/AcDream.Core/Physics/PhysicsDiagnostics.cs @@ -1637,7 +1637,7 @@ public static class PhysicsDiagnostics // channel), naming which phase halted the attempt and, on Collided, // the colliding polygon's normal and which of the three channels // wrote it. - // [transit-fail-stepup] — DoStepUp entry/exit, mirroring the existing + // [transit-fail-stepup] — DoStepUp entry/exit, mirroring the retired // ACDREAM_DUMP_STEPUP probe's own input-normal-and-verdict content // so a stuck-tick capture carries the step chain without running a // second flag. @@ -1753,7 +1753,7 @@ public static class PhysicsDiagnostics /// /// One line per Transition.DoStepUp entry or exit, mirroring the - /// content of the existing ACDREAM_DUMP_STEPUP probe (same input + /// content of the retired ACDREAM_DUMP_STEPUP probe (same input /// normal / walkable verdict / landing-plane fields) so a stuck-tick /// capture carries the step-up chain without a second flag. /// is "enter" or "exit"; diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index 7c60c224..c6a80164 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -1349,9 +1349,6 @@ public sealed class Transition CollisionInfo.CopyFrom(source.CollisionInfo); } - private static bool DumpEdgeSlideEnabled => - Environment.GetEnvironmentVariable("ACDREAM_DUMP_EDGE_SLIDE") == "1"; - // ----------------------------------------------------------------------- // A6.P7 (2026-05-25) — retail-binary shape dispatch rule // ----------------------------------------------------------------------- @@ -2227,7 +2224,6 @@ public sealed class Transition if (ci.ContactPlaneValid) return TransitionState.OK; - DumpStepDownBranchGate(contactInvalid: true); if (oi.Contact && !sp.StepDown && sp.CheckCellId != 0 && oi.StepDown) { // L.2.3i (2026-04-29): retail uses FloorZ when OnWalkable, @@ -2280,7 +2276,6 @@ public sealed class Transition // intentionally narrow: it tells the next L.2c slice whether // we are missing precipice context, a steep contact plane, or // merely the EdgeSlide flag. - DumpEdgeSlideStepDownFailed(stepDownHeight, zVal); bool stop = EdgeSlideAfterStepDownFailed( engine, @@ -2469,7 +2464,6 @@ public sealed class Transition TransitionCellCollisionPhase.Objects, cellId, FindObjCollisionsInCell(engine, cellId)); - DumpPhase2(innerAttempt, environment, objects); PhysicsDiagnostics.TraceTransitInsertAttempt( ObjectInfo.SelfEntityId, innerAttempt, "objects", environment, building, objects, objects, @@ -2501,7 +2495,6 @@ public sealed class Transition // No steep-plane exception precedes this gate (0050b3d8-0050b3e7). if (!oi.OnWalkable || !oi.EdgeSlide) { - DumpEdgeSlideBranch("branch1/!onwalkable-or-!edgeslide", zVal); sp.ClearWalkable(); sp.RestoreCheckPos(); ci.ContactPlaneValid = false; @@ -2513,7 +2506,6 @@ public sealed class Transition if (ci.ContactPlaneValid && ci.ContactPlane.Normal.Z < zVal) { var cliffPlane = ci.ContactPlane; - DumpEdgeSlideBranch("branch2/steep-cliffslide", zVal); sp.ClearWalkable(); sp.RestoreCheckPos(); ci.ContactPlaneValid = false; @@ -2536,7 +2528,6 @@ public sealed class Transition // rapidly down the stairs. Do not restore stale history here. if (sp.HasWalkablePolygon) { - DumpEdgeSlideBranch("branch3/precipice-slide", zVal); sp.RestoreCheckPos(); ci.ContactPlaneValid = false; ci.ContactPlaneIsWater = false; @@ -2546,7 +2537,6 @@ public sealed class Transition if (ci.ContactPlaneValid) { - DumpEdgeSlideBranch("branch4/contact-no-walkable", zVal); sp.ClearWalkable(); sp.RestoreCheckPos(); ci.ContactPlaneValid = false; @@ -2624,8 +2614,6 @@ public sealed class Transition Vector3 collideNormal = new(-contactNormal.Y, contactNormal.X, 0f); if (collideNormal.LengthSquared() < PhysicsGlobals.EpsilonSq) { - DumpCliffSlide("degenerate-cross/last-known", contactPlane, - new Plane(referenceNormal, 0f), contactNormal, 0f, false); return TransitionState.OK; } @@ -2633,8 +2621,6 @@ public sealed class Transition Vector3 offset = sp.GlobalSphere[0].Origin - sp.GlobalCurrCenter[0].Origin; float angle = Vector3.Dot(collideNormal, offset); - DumpCliffSlide("ok/last-known", contactPlane, - new Plane(referenceNormal, 0f), collideNormal, angle, true); if (angle <= 0f) { @@ -2650,89 +2636,6 @@ public sealed class Transition return TransitionState.Adjusted; } - private void DumpEdgeSlideStepDownFailed(float stepDownHeight, float zVal) - { - if (!DumpEdgeSlideEnabled) return; - - var sp = SpherePath; - var ci = CollisionInfo; - var oi = ObjectInfo; - - Console.WriteLine( - System.FormattableString.Invariant( - $"edge-slide: stepdown-failed cur={Fmt(sp.CurPos)} check={Fmt(sp.CheckPos)} cell=0x{sp.CheckCellId:X8} edgeFlag={oi.EdgeSlide} contactFlag={oi.Contact} onWalkable={oi.OnWalkable} contactPlane={ci.ContactPlaneValid} lastPlane={ci.LastKnownContactPlaneValid} walkableValid={sp.WalkableValid} walkablePoly={sp.HasWalkablePolygon} lastWalkablePoly={sp.HasLastWalkablePolygon} stepDown={stepDownHeight:F3} zVal={zVal:F3}")); - } - - /// - /// L.4-diag: log step-down branch gate decision. Whether we entered or - /// skipped the contact-recovery branch matters for whether CliffSlide - /// has any chance of firing. - /// - private void DumpStepDownBranchGate(bool contactInvalid) - { - if (!DumpEdgeSlideEnabled) return; - - var sp = SpherePath; - var ci = CollisionInfo; - var oi = ObjectInfo; - - bool wouldEnter = contactInvalid && oi.Contact && !sp.StepDown - && sp.CheckCellId != 0 && oi.StepDown; - - if (!wouldEnter) return; // only log when entering, to keep noise low - - Console.WriteLine( - System.FormattableString.Invariant( - $"edge-slide: stepdown-branch-enter cur={Fmt(sp.CurPos)} contactValid={ci.ContactPlaneValid} contactN.Z={(ci.ContactPlaneValid ? ci.ContactPlane.Normal.Z : 0f):F3} onWalk={oi.OnWalkable} contact={oi.Contact}")); - } - - /// - /// L.4-diag: log Phase 2 outcome per inner attempt. Tells us whether - /// we're churning in Slid retries or escaping to step-down branch. - /// - private void DumpPhase2(int attempt, TransitionState envState, TransitionState objState) - { - if (!DumpEdgeSlideEnabled) return; - if (objState == TransitionState.OK) return; // skip clean attempts - - Console.WriteLine( - System.FormattableString.Invariant( - $"edge-slide: phase2 attempt={attempt} env={envState} obj={objState}")); - } - - /// - /// L.4-diag: log which branch of EdgeSlideAfterStepDownFailed fired. - /// Tells us whether CliffSlide gets called or whether we hit a - /// stop-at-edge branch. - /// - private void DumpEdgeSlideBranch(string branch, float zVal) - { - if (!DumpEdgeSlideEnabled) return; - var sp = SpherePath; - var ci = CollisionInfo; - var oi = ObjectInfo; - Console.WriteLine( - System.FormattableString.Invariant( - $"edge-slide: branch={branch} contactValid={ci.ContactPlaneValid} contactN.Z={(ci.ContactPlaneValid ? ci.ContactPlane.Normal.Z : 0f):F3} lastValid={ci.LastKnownContactPlaneValid} lastN.Z={(ci.LastKnownContactPlaneValid ? ci.LastKnownContactPlane.Normal.Z : 0f):F3} walkPolyValid={sp.HasWalkablePolygon} walkPolyN.Z={(sp.HasWalkablePolygon ? sp.WalkablePlane.Normal.Z : 0f):F3} lastWalkPolyN.Z={(sp.HasLastWalkablePolygon ? sp.LastWalkablePlane.Normal.Z : 0f):F3} onWalk={oi.OnWalkable} edgeFlag={oi.EdgeSlide} zVal={zVal:F3}")); - } - - /// - /// L.4-diag: log CliffSlide invocation. Tells us whether the - /// cross-product is degenerate (no slide) or producing a real - /// deflection. - /// - private void DumpCliffSlide(string outcome, Plane current, Plane lastKnown, - Vector3 collideNormal, float angle, bool willApply) - { - if (!DumpEdgeSlideEnabled) return; - Console.WriteLine( - System.FormattableString.Invariant( - $"edge-slide: cliffslide outcome={outcome} curN={Fmt(current.Normal)} lastN={Fmt(lastKnown.Normal)} collideN={Fmt(collideNormal)} angle={angle:F4} apply={willApply}")); - } - - private static string Fmt(Vector3 value) => - System.FormattableString.Invariant($"({value.X:F3},{value.Y:F3},{value.Z:F3})"); - // ----------------------------------------------------------------------- // Environment collision — outdoor terrain // ----------------------------------------------------------------------- @@ -5739,29 +5642,9 @@ public sealed class Transition var ci = CollisionInfo; var oi = ObjectInfo; - // L.2.3f (2026-04-29): diagnostic for steep-roof bug. Logs the - // input polygon normal that triggered step-up. The verdict tells - // whether THIS polygon would pass FloorZ (≈ 0.66) — but actual - // step-up acceptance depends on the polygon found by step_sphere_down - // INSIDE the recursive TransitionalInsert, which may be different. - // The post-step "result=" line below logs that outcome. - bool diag = Environment.GetEnvironmentVariable("ACDREAM_DUMP_STEPUP") == "1"; - if (diag) - { - float floor = PhysicsGlobals.FloorZ; - string verdict = collisionNormal.Z >= floor ? "WALKABLE" : "STEEP"; - Console.WriteLine( - $"stepup: enter normal=({collisionNormal.X:F3},{collisionNormal.Y:F3},{collisionNormal.Z:F3}) " + - $"|Z|={collisionNormal.Z:F3} vs FloorZ={floor:F3} → {verdict}, " + - $"OnWalkable={(oi.State & ObjectInfoState.OnWalkable) != 0}, " + - $"StepUpHeight={oi.StepUpHeight:F3}, " + - $"CurPos=({sp.CurPos.X:F2},{sp.CurPos.Y:F2},{sp.CurPos.Z:F2})"); - } - - // #345 probe (2026-08-08): mirrors the ACDREAM_DUMP_STEPUP content - // above into the buffered stuck-tick trace (self-guards internally, - // zero cost when ACDREAM_DUMP_TRANSIT_FAIL is unset) so a stuck-tick - // capture carries the step-up chain without a second flag. + // #345 probe (2026-08-08): buffers this step-up attempt into the + // stuck-tick trace (self-guards internally, zero cost when + // ACDREAM_DUMP_TRANSIT_FAIL is unset). PhysicsDiagnostics.TraceTransitStepUp( oi.SelfEntityId, "enter", collisionNormal, onWalkable: (oi.State & ObjectInfoState.OnWalkable) != 0, @@ -5807,28 +5690,6 @@ public sealed class Transition sp.StepUp = false; sp.ClearWalkable(); - // L.2.3f: log the result + landing plane if step-up succeeded. - // This is the actual surface the player ended up on, which may - // differ from the input collision normal (e.g. step-up scanned - // past a steep slope and landed on a flatter polygon higher up). - if (diag) - { - if (stepDown && ci.ContactPlaneValid) - { - float floor = PhysicsGlobals.FloorZ; - string verdict = ci.ContactPlane.Normal.Z >= floor ? "WALKABLE" : "STEEP"; - Console.WriteLine( - $"stepup: SUCCESS — landed on plane normal=" + - $"({ci.ContactPlane.Normal.X:F3},{ci.ContactPlane.Normal.Y:F3},{ci.ContactPlane.Normal.Z:F3}) " + - $"|Z|={ci.ContactPlane.Normal.Z:F3} vs FloorZ={floor:F3} → {verdict}, " + - $"new CheckPos=({sp.CheckPos.X:F2},{sp.CheckPos.Y:F2},{sp.CheckPos.Z:F2})"); - } - else - { - Console.WriteLine($"stepup: FAILED — sliding back along normal"); - } - } - // #345 probe (2026-08-08): the matching exit line for the "enter" // trace above. PhysicsDiagnostics.TraceTransitStepUp( diff --git a/src/AcDream.Runtime/Gameplay/RuntimeInteractionTransactionState.cs b/src/AcDream.Runtime/Gameplay/RuntimeInteractionTransactionState.cs index 503e3c33..37b11903 100644 --- a/src/AcDream.Runtime/Gameplay/RuntimeInteractionTransactionState.cs +++ b/src/AcDream.Runtime/Gameplay/RuntimeInteractionTransactionState.cs @@ -224,11 +224,6 @@ public sealed class RuntimeInteractionTransactionState : IDisposable verdict = RuntimeInteractionDispatchResult.Dispatched; } - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] TryDispatchUse guid=0x{serverGuid:X8} ownedByPlayer={ownedByPlayer} useable={useable} verdict={verdict} seq={sequence}"); - } return verdict; } @@ -633,11 +628,6 @@ public sealed class RuntimeInteractionTransactionState : IDisposable if (_pendingUse is not { } current || current.ApproachToken != approachToken) { - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] TryResolveUseApproachCompletion no-match approachToken=({approachToken.ControllerLifetime},{approachToken.ApproachGeneration}) natural={natural} hasPendingUse={_pendingUse is not null}"); - } pending = default; return false; } @@ -645,11 +635,6 @@ public sealed class RuntimeInteractionTransactionState : IDisposable _pendingUse = null; pending = current; IncrementRevision(); - if (VendorDiagnostics.DumpVendorEnabled) - { - Console.WriteLine( - $"[vendor-diag] TryResolveUseApproachCompletion guid=0x{pending.ServerGuid:X8} natural={natural} accepted={natural}"); - } return natural; } diff --git a/src/AcDream.Runtime/Gameplay/VendorShopItemMaterializer.cs b/src/AcDream.Runtime/Gameplay/VendorShopItemMaterializer.cs index 82568a44..1e62454b 100644 --- a/src/AcDream.Runtime/Gameplay/VendorShopItemMaterializer.cs +++ b/src/AcDream.Runtime/Gameplay/VendorShopItemMaterializer.cs @@ -191,7 +191,6 @@ public sealed class VendorShopItemMaterializer : IDisposable } } - int diagIndex = 0; foreach (VendorShopItem item in currentItems) { bool ownedAlready = _ownedGuids.ContainsKey(item.ItemGuid); @@ -208,16 +207,6 @@ public sealed class VendorShopItemMaterializer : IDisposable } WeenieData materialized = ToWeenieData(item, transition.VendorId); - if (VendorDiagnostics.DumpVendorEnabled && diagIndex < 5) - { - Console.WriteLine( - $"[vendor-diag] materialize[{diagIndex}] guid=0x{item.ItemGuid:X8} " - + $"descStackSize={(item.DescStackSize is { } ds ? ds.ToString() : "null")} " - + $"maxStackSize={(item.MaxStackSize is { } ms ? ms.ToString() : "null")} " - + $"resolvedStackSize={(materialized.StackSize is { } rs ? rs.ToString() : "null")} " - + $"resolvedStackSizeMax={(materialized.StackSizeMax is { } rm ? rm.ToString() : "null")}"); - diagIndex++; - } _objects.Ingest(materialized); nextOwned[item.ItemGuid] = transition.VendorId; diff --git a/tests/AcDream.App.Tests/Diagnostics/LaunchOptionsDocumentationTests.cs b/tests/AcDream.App.Tests/Diagnostics/LaunchOptionsDocumentationTests.cs index 6297df33..6652db65 100644 --- a/tests/AcDream.App.Tests/Diagnostics/LaunchOptionsDocumentationTests.cs +++ b/tests/AcDream.App.Tests/Diagnostics/LaunchOptionsDocumentationTests.cs @@ -47,17 +47,13 @@ public sealed class LaunchOptionsDocumentationTests ["src/AcDream.App/Physics/RemoteServerControlledVelocityCycle.cs"] = 1, ["src/AcDream.App/Platform/GraphicalWindowBackendSelection.cs"] = 1, ["src/AcDream.App/Rendering/GameWindow.cs"] = 1, - ["src/AcDream.App/Rendering/PortalVisibilityBuilder.cs"] = 1, ["src/AcDream.App/Rendering/Sky/SkyRenderer.cs"] = 1, ["src/AcDream.App/Rendering/TextureCache.cs"] = 1, ["src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs"] = 2, - ["src/AcDream.Core/Physics/TransitionTypes.cs"] = 2, ["src/AcDream.Core/Vfx/PhysicsScriptRunner.cs"] = 1, ["src/AcDream.Core/World/SkyDescLoader.cs"] = 2, - ["src/AcDream.Core.Net/GameEventWiring.cs"] = 1, - ["src/AcDream.Core.Net/Messages/PlayerDescriptionParser.cs"] = 1, ["src/AcDream.Core.Net/Messages/UpdateMotion.cs"] = 1, - ["src/AcDream.Core.Net/WorldSession.cs"] = 3, + ["src/AcDream.Core.Net/WorldSession.cs"] = 2, ["src/AcDream.Platform/ApplicationPathSet.cs"] = 3, ["src/AcDream.Platform/BakePublicationGuardPaths.cs"] = 1, ["src/AcDream.Runtime/Physics/RuntimeRemotePhysicsUpdater.cs"] = 1, @@ -110,6 +106,65 @@ public sealed class LaunchOptionsDocumentationTests + "removal commit) in the same commit that deletes the read site."); } + /// + /// The four flags that default ON. All are retail behaviors wearing an + /// A/B off-switch (=0 disables) — none is a diagnostic. FROZEN: + /// a diagnostic that activates without its env var set taxes every run + /// and every measurement silently, so growing this set fails. + /// + private static readonly IReadOnlySet DefaultOnBehaviorFlags = + new HashSet(StringComparer.Ordinal) + { + "ACDREAM_RETAIL_CHASE", + "ACDREAM_CAMERA_COLLIDE", + "ACDREAM_CAMERA_ALIGN_SLOPE", + "ACDREAM_RETAIL_CLOSE_DEGRADES", + }; + + /// + /// A read shaped "anything but the literal 0 enables" — the two + /// default-on idioms in this codebase: + /// GetEnvironmentVariable("X") != "0" and + /// !string.Equals(env("X"), "0", ...). + /// + private static readonly Regex DefaultOnRead = new( + @"(?:GetEnvironmentVariable\(\s*""(ACDREAM_[A-Z0-9_]+)""\s*\)\s*!=\s*""0""" + + @"|!string\.Equals\(\s*env\(\s*""(ACDREAM_[A-Z0-9_]+)""\s*\)\s*,\s*""0"")", + RegexOptions.Compiled); + + [Fact] + public void OnlyTheFourRetailBehaviorFlagsDefaultOn() + { + var defaultOn = new HashSet(StringComparer.Ordinal); + foreach ((string path, _) in SourceFiles()) + { + foreach (Match match in DefaultOnRead.Matches(File.ReadAllText(path))) + { + defaultOn.Add(match.Groups[1].Success + ? match.Groups[1].Value + : match.Groups[2].Value); + } + } + + List added = defaultOn.Except(DefaultOnBehaviorFlags) + .Order(StringComparer.Ordinal).ToList(); + Assert.True( + added.Count == 0, + "New default-ON flag(s): " + string.Join(", ", added) + + ". Every diagnostic must be OFF until its variable is " + + "explicitly set; only a retail behavior with an A/B off-switch " + + "may default on, and adding one means updating this frozen set " + + "AND the Conventions section of docs/launch-options.md."); + + List gone = DefaultOnBehaviorFlags.Except(defaultOn) + .Order(StringComparer.Ordinal).ToList(); + Assert.True( + gone.Count == 0, + "Frozen default-ON flag(s) no longer read that way: " + + string.Join(", ", gone) + + ". Update this set and the docs in the same commit."); + } + [Fact] public void DirectEnvironmentReadsOutsideOwnerClassesDoNotGrow() { diff --git a/tests/AcDream.App.Tests/RuntimeOptionsTests.cs b/tests/AcDream.App.Tests/RuntimeOptionsTests.cs index 3f5739db..10902a19 100644 --- a/tests/AcDream.App.Tests/RuntimeOptionsTests.cs +++ b/tests/AcDream.App.Tests/RuntimeOptionsTests.cs @@ -160,7 +160,6 @@ public sealed class RuntimeOptionsTests // Default-on: RetailCloseDegrades is true unless explicitly disabled. Assert.True(opts.RetailCloseDegrades); Assert.False(opts.DumpSceneryZ); - Assert.False(opts.DumpClothing); Assert.Null(opts.LegacyStreamRadius); Assert.False(opts.UiProbeDump); Assert.Null(opts.UiProbeScript); @@ -508,7 +507,6 @@ public sealed class RuntimeOptionsTests ["ACDREAM_DUMP_SKY"] = "1", ["ACDREAM_NO_AUDIO"] = "1", ["ACDREAM_DUMP_SCENERY_Z"] = "1", - ["ACDREAM_DUMP_CLOTHING"] = "1", })); Assert.True(allOn.DevTools); Assert.True(allOn.UncappedRendering); @@ -516,25 +514,22 @@ public sealed class RuntimeOptionsTests Assert.True(allOn.DumpSky); Assert.True(allOn.NoAudio); Assert.True(allOn.DumpSceneryZ); - Assert.True(allOn.DumpClothing); // Any non-"1" value leaves them off, matching the // string.Equals(env, "1", StringComparison.Ordinal) check. var anyOther = RuntimeOptions.Parse(AnyDatDir, Env(new() { + ["ACDREAM_DUMP_MOVE_TRUTH"] = "true", ["ACDREAM_DEVTOOLS"] = "true", ["ACDREAM_UNCAPPED_RENDER"] = "true", - ["ACDREAM_DUMP_MOVE_TRUTH"] = "yes", ["ACDREAM_NO_AUDIO"] = "2", ["ACDREAM_DUMP_SCENERY_Z"] = " 1", - ["ACDREAM_DUMP_CLOTHING"] = "true", })); Assert.False(anyOther.DevTools); Assert.False(anyOther.UncappedRendering); Assert.False(anyOther.DumpMoveTruth); Assert.False(anyOther.NoAudio); Assert.False(anyOther.DumpSceneryZ); - Assert.False(anyOther.DumpClothing); } [Fact] diff --git a/tests/AcDream.Core.Tests/Physics/CellarLipWedgeTests.cs b/tests/AcDream.Core.Tests/Physics/CellarLipWedgeTests.cs index 3d69a53c..b80fbffd 100644 --- a/tests/AcDream.Core.Tests/Physics/CellarLipWedgeTests.cs +++ b/tests/AcDream.Core.Tests/Physics/CellarLipWedgeTests.cs @@ -320,7 +320,6 @@ public class CellarLipWedgeTests var sw = new StringWriter(); PhysicsDiagnostics.ProbeIndoorBspEnabled = true; PhysicsDiagnostics.ProbeStepWalkEnabled = true; - Environment.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", "1"); Console.SetOut(sw); try { @@ -334,7 +333,6 @@ public class CellarLipWedgeTests finally { Console.SetOut(saved); - Environment.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", null); PhysicsDiagnostics.ProbeIndoorBspEnabled = false; PhysicsDiagnostics.ProbeStepWalkEnabled = false; } diff --git a/tests/AcDream.Core.Tests/Physics/DoorCollisionApparatusTests.cs b/tests/AcDream.Core.Tests/Physics/DoorCollisionApparatusTests.cs index e1c524b6..db4e83d9 100644 --- a/tests/AcDream.Core.Tests/Physics/DoorCollisionApparatusTests.cs +++ b/tests/AcDream.Core.Tests/Physics/DoorCollisionApparatusTests.cs @@ -267,7 +267,6 @@ public class DoorCollisionApparatusTests PhysicsDiagnostics.ProbeResolveEnabled = true; PhysicsDiagnostics.ProbeBuildingEnabled = true; - Env.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", "1"); // Synthetic floor plane at Z = 0 so the grounded sphere has a // walkable plane to rest on. Sphere foot center starts at Z=radius @@ -339,7 +338,6 @@ public class DoorCollisionApparatusTests // (Y < 12.0) — the same shape as the Path-6 apparatus tests above. PhysicsDiagnostics.ProbeResolveEnabled = false; PhysicsDiagnostics.ProbeBuildingEnabled = false; - Env.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", null); Assert.True(blocked, $"Door must block the grounded off-center approach (#99 closed). " +