fix #435 (part 2, closes it): attribute the unowned probes — delete 7, reclassify 8, restore 1

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 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-24 12:32:42 +02:00
parent 0c5057c9ff
commit c1e6e3da44
26 changed files with 256 additions and 693 deletions

View file

@ -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 what it does, its exact value shape, and **what else it changes about the
run** — is documented in run** — is documented in
[`docs/launch-options.md`](docs/launch-options.md). That file is the single [`docs/launch-options.md`](docs/launch-options.md). That file is the single
source of truth and is enforced by `LaunchOptionsDocumentationTests`: a source of truth for every probe we have and how to turn one on, and it is
flag without a documented row fails the build, and so does a documented row enforced by `LaunchOptionsDocumentationTests`: a flag without a documented
whose read site was deleted. 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 - **Read the side-effects column before any measurement.** Flags that look
inert are not: `ACDREAM_AUTOMATION_ARTIFACT_DIR` also builds a per-frame inert are not: `ACDREAM_AUTOMATION_ARTIFACT_DIR` also builds a per-frame
diagnostics referee (#432), and `ACDREAM_STREAM_RADIUS` measures a diagnostics referee (#432), and `ACDREAM_STREAM_RADIUS` measures a

View file

@ -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. - 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. - 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 **Status:** The 17 orphaned probes from part 1 are DELETED (2026-08-24) —
removed, flag count 161 → 144, temporary probes 64 → 47. Build clean; full 3,493 lines removed, flag count 161 → 144, temporary probes 64 → 47. Build
hermetic suite 15,321 passed / 0 failed (baseline 15,333 minus the 12 tests clean; full hermetic suite 15,321 passed / 0 failed (baseline 15,333 minus
whose only subject was a deleted probe). Four files went entirely: the 12 tests whose only subject was a deleted probe). Four files went
`WalkMissDiagnostic.cs`, `CollisionMeshWireframe.cs` and two probe-only test entirely: `WalkMissDiagnostic.cs`, `CollisionMeshWireframe.cs` and two
files. `LaunchOptionsDocumentationTests` did its job during the cleanup — probe-only test files. `LaunchOptionsDocumentationTests` did its job during
it refused the deletion until the doc's rows moved to Retired and the frozen the cleanup — it refused the deletion until the doc's rows moved to Retired
direct-read counts were lowered (`PhysicsEngine.cs` to zero, and the frozen direct-read counts were lowered (`PhysicsEngine.cs` to zero,
`TransitionTypes.cs` 3 → 2). `TransitionTypes.cs` 3 → 2).
Notable: `TransitionTypes.SetContactPlane` shed its `CallerMemberName` / 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 survives and reverts to the proxy-cylinder form, as intended when
`ACDREAM_WIRE_MESH` went. `ACDREAM_WIRE_MESH` went.
**STILL OPEN — the 14 unattributed probes.** They name no owning issue, so **Part 2 (2026-08-24):** traced each of the 14 then-unattributed rows to
nothing records when they are safe to remove. Deleting them on a guess is its introducing commit and confirmed 8 belonged to closed investigations —
how a future investigation loses apparatus it needed. The right next step is `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 attribution, not deletion: for each, find the commit that introduced it
(`git log -S ACDREAM_PROBE_X`), record the issue in its (`git log -S ACDREAM_PROBE_X`), record the issue in its
`docs/launch-options.md` row, and only then decide. Deliberately deferred. `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 ## #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), **Status:** CLOSED 2026-08-24. Deleted `DebugPanel.cs` (340 lines),

View file

@ -37,6 +37,15 @@ Assume a flag has a side effect until its row says otherwise.
## Conventions ## 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`, - `=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 `yes`, or `0` does **not** enable such a flag (and `0` does not disable
one whose test is "is the variable present"). 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_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>` | 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_UI_PROBE_SCRIPT` | `=<path>` | 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` | `=<feature-name>` (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_FORCE_UNSUPPORTED` | `=<feature-name>` (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` | `=<int>` (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_VULKAN_PROBE_FRAMES` | `=<int>` (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 ## 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_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_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_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` | `=<comma list of hex cell ids>` | 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` | `=<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` | `=<comma list of hex GfxObj ids>` | 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` | `=<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` | `=<int>` | 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 ## 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. issue is closed, the strip was missed; delete both.
> **Probe debt, measured 2026-08-24:** 64 temporary probes existed, citing 21 > **Probe debt, measured 2026-08-24:** 64 temporary probes existed, citing 21
> distinct issues with 14 already closed. [#435](ISSUES.md) stripped the 17 > distinct issues with 14 already closed. [#435](ISSUES.md) part 1 stripped
> rows whose investigation had ended without the strip — see the Retired > the 17 rows whose investigation had ended without the strip — see the
> section below for their removal record — leaving 47. A further 14 rows > Retired section below for their removal record — leaving 47. Part 2
> (unchanged by this pass) name no owning issue at all, which is worse: > traced each of the (then-)14 unattributed rows to its introducing commit
> nobody can tell when they are safe to remove. Every probe here still costs > and stripped the 7 that belonged to closed investigations
> a branch on its hot path even when unset, and a handful re-read the > (`ACDREAM_A8_DUMP_PV`/Phase A8, `ACDREAM_DUMP_CLOTHING`/#37,
> environment per frame rather than caching (see their side-effects column). > `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 | | 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_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_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 | `=<comma list of hex cell ids>` | 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) | `=<dir>` | 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 | `=<comma list of hex GfxObj ids>` | 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) | `=<dir>` | 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_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_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_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) | `=<int>` | 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>` (`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_LIGHT_DEBUG` | #176 | `=<int>` (`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_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_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_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` | | `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_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_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_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 |

View file

@ -562,8 +562,7 @@ internal sealed class SessionPlayerCompositionPhase
d.EntityObjects, d.EntityObjects,
teardown, teardown,
d.PlayerIdentity, d.PlayerIdentity,
dormantLiveEntities, dormantLiveEntities);
d.Options.DumpLiveSpawns ? d.Log : null);
// 2026-08-08 vendor-approach root cause: the local player's // 2026-08-08 vendor-approach root cause: the local player's
// publication-chain host resolves moveto/sticky targets through // publication-chain host resolves moveto/sticky targets through
// RuntimePhysicsState.ResolveObjectTableHost. Bind the graphical // RuntimePhysicsState.ResolveObjectTableHost. Bind the graphical
@ -709,7 +708,6 @@ internal sealed class SessionPlayerCompositionPhase
d.PlayerIdentity, d.PlayerIdentity,
deletion, deletion,
dormantLiveEntities, dormantLiveEntities,
d.Options.DumpLiveSpawns ? d.Log : null,
firstEntryDrive, firstEntryDrive,
acceptedPositionDrive); acceptedPositionDrive);
bindings.Adopt( bindings.Adopt(

View file

@ -61,14 +61,6 @@ internal sealed class PlayerInteractionMovementSink(
Height = approach.TargetHeight, 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 // PerformMovement cancels at its head. Do it explicitly before the
// intent is armed so cancellation of the preceding move cannot clear // intent is armed so cancellation of the preceding move cannot clear
// the new request; the internal second call is then a retail no-op. // the new request; the internal second call is then a retail no-op.

View file

@ -181,11 +181,6 @@ internal sealed class SelectionInteractionController
// ships with it. // ships with it.
if (useImmediately && !_query.IsWieldedByPlayer(guid)) if (useImmediately && !_query.IsWieldedByPlayer(guid))
{ {
if (VendorDiagnostics.DumpVendorEnabled)
{
Console.WriteLine(
$"[vendor-diag] route=dblclick-world PickAndStoreSelection guid=0x{guid:X8} enqueue=Activate");
}
EnqueueIdentityBound( EnqueueIdentityBound(
RuntimeQueuedInteractionKind.Activate, RuntimeQueuedInteractionKind.Activate,
guid, guid,
@ -221,11 +216,6 @@ internal sealed class SelectionInteractionController
_toast?.Invoke("Nothing selected"); _toast?.Invoke("Nothing selected");
return; return;
} }
if (VendorDiagnostics.DumpVendorEnabled)
{
Console.WriteLine(
$"[vendor-diag] route=keyboard-use UseCurrentSelection guid=0x{selected:X8} enqueue=Use");
}
EnqueueIdentityBound( EnqueueIdentityBound(
RuntimeQueuedInteractionKind.Use, RuntimeQueuedInteractionKind.Use,
selected, selected,
@ -292,22 +282,10 @@ internal sealed class SelectionInteractionController
bool ownedByPlayer = _items.IsOwnedByPlayer(serverGuid); bool ownedByPlayer = _items.IsOwnedByPlayer(serverGuid);
bool useable = ownedByPlayer || _query.IsUseable(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 if (useable
&& _query.TryGetApproach(serverGuid, out InteractionApproach approach) && _query.TryGetApproach(serverGuid, out InteractionApproach approach)
&& !approach.IsCloseRange) && !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) — // Genuinely out of range (a real walk, not just a turn) —
// mirror SendPickup's arrival-gated shape: arm the transaction // mirror SendPickup's arrival-gated shape: arm the transaction
// on the approach token BEFORE the movement starts (so a // on the approach token BEFORE the movement starts (so a
@ -327,17 +305,7 @@ internal sealed class SelectionInteractionController
token.ControllerLifetime, token.ControllerLifetime,
token.ApproachGeneration), token.ApproachGeneration),
out _); 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) if (!started || !armed)
{ {
// Release whatever got captured (or the caller's own // 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 // — keep retail's immediate send; ACE's own "already within use
// distance" branch (Player_Move.cs:65-87) calls back synchronously, // distance" branch (Player_Move.cs:65-87) calls back synchronously,
// so there is no arrival gap to race here. // 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 = RuntimeInteractionDispatchResult result =
_transactions.TryDispatchUse( _transactions.TryDispatchUse(
serverGuid, serverGuid,
@ -373,11 +336,6 @@ internal sealed class SelectionInteractionController
reservation, reservation,
_transport, _transport,
out uint sequence); out uint sequence);
if (VendorDiagnostics.DumpVendorEnabled)
{
Console.WriteLine(
$"[vendor-diag] RequestUse guid=0x{serverGuid:X8} TryDispatchUse verdict={result} seq={sequence}");
}
if (result == RuntimeInteractionDispatchResult.NotInWorld) if (result == RuntimeInteractionDispatchResult.NotInWorld)
_toast?.Invoke("Not in world"); _toast?.Invoke("Not in world");
if (result == RuntimeInteractionDispatchResult.Dispatched) if (result == RuntimeInteractionDispatchResult.Dispatched)
@ -609,22 +567,6 @@ internal sealed class SelectionInteractionController
RuntimePendingUse pending, RuntimePendingUse pending,
bool accepted) 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) if (!accepted)
{ {
pending.Reservation?.CancelBeforeDispatch(); pending.Reservation?.CancelBeforeDispatch();

View file

@ -571,12 +571,6 @@ internal sealed class WorldSelectionQuery
bool haveSpawn = _liveEntities.TryGetSnapshot(serverGuid, out var spawn); bool haveSpawn = _liveEntities.TryGetSnapshot(serverGuid, out var spawn);
bool fromWire = haveSpawn && spawn.UseRadius is > 0f; bool fromWire = haveSpawn && spawn.UseRadius is > 0f;
float radius = fromWire ? spawn.UseRadius!.Value : DefaultUseRadius; 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; return radius;
} }
} }

View file

@ -58,17 +58,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
/// </summary> /// </summary>
private readonly RuntimeWorldTransitState _transit; 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( public DatLiveEntityProjectionMaterializer(
RuntimeOptions options, RuntimeOptions options,
IDatReaderWriter dats, IDatReaderWriter dats,
@ -116,16 +105,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
public void ResetSessionState() 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( public bool TryMaterialize(
@ -157,18 +136,10 @@ internal sealed class DatLiveEntityProjectionMaterializer
if (appearanceUpdate is not null && expectedRecord is null) if (appearanceUpdate is not null && expectedRecord is null)
return false; return false;
_received++;
bool dumpLiveSpawns = _options.DumpLiveSpawns;
DumpSpawn(canonicalSpawn, dumpLiveSpawns);
if (!_origin.IsKnown) if (!_origin.IsKnown)
return false; return false;
if (canonicalSpawn.Position is null || canonicalSpawn.SetupTableId is null) if (canonicalSpawn.Position is null || canonicalSpawn.SetupTableId is null)
{ {
if (canonicalSpawn.Position is null)
_noPosition++;
else
_noSetup++;
return false; return false;
} }
@ -216,13 +187,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
_collisionAssets.CacheSetup(canonicalSpawn.SetupTableId.Value, setup); _collisionAssets.CacheSetup(canonicalSpawn.SetupTableId.Value, setup);
if (setup is null) 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; return false;
} }
@ -248,14 +212,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
List<MeshRef> flattened = [.. SetupMesh.Flatten(setup, idleFrame)]; List<MeshRef> flattened = [.. SetupMesh.Flatten(setup, idleFrame)];
IReadOnlyList<CreateObject.AnimPartChange> animPartChanges = IReadOnlyList<CreateObject.AnimPartChange> animPartChanges =
canonicalSpawn.AnimPartChanges ?? Array.Empty<CreateObject.AnimPartChange>(); canonicalSpawn.AnimPartChanges ?? Array.Empty<CreateObject.AnimPartChange>();
bool dumpClothing = _options.DumpClothing && setup.Parts.Count >= 10;
DumpClothingHeader(
canonicalSpawn,
setup,
flattened,
idleFrame,
animPartChanges,
dumpClothing);
foreach (CreateObject.AnimPartChange change in animPartChanges) foreach (CreateObject.AnimPartChange change in animPartChanges)
{ {
@ -273,17 +229,14 @@ internal sealed class DatLiveEntityProjectionMaterializer
baseId => ResolveCollisionPart(baseId)); baseId => ResolveCollisionPart(baseId));
if (_options.RetailCloseDegrades && IsIssue47HumanoidSetup(setup)) if (_options.RetailCloseDegrades && IsIssue47HumanoidSetup(setup))
ApplyRetailCloseDegrades(flattened, dumpClothing); ApplyRetailCloseDegrades(flattened);
IReadOnlyList<CreateObject.TextureChange> textureChanges = IReadOnlyList<CreateObject.TextureChange> textureChanges =
canonicalSpawn.TextureChanges ?? Array.Empty<CreateObject.TextureChange>(); canonicalSpawn.TextureChanges ?? Array.Empty<CreateObject.TextureChange>();
Dictionary<int, Dictionary<uint, uint>>? surfaceOverrides = Dictionary<int, Dictionary<uint, uint>>? surfaceOverrides =
ResolveSurfaceOverrides( ResolveSurfaceOverrides(
canonicalSpawn,
flattened, flattened,
textureChanges, textureChanges);
dumpClothing,
dumpLiveSpawns);
float scale = canonicalSpawn.ObjScale ?? 1f; float scale = canonicalSpawn.ObjScale ?? 1f;
Matrix4x4 scaleMatrix = Matrix4x4.CreateScale(scale); Matrix4x4 scaleMatrix = Matrix4x4.CreateScale(scale);
@ -294,7 +247,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
var indexedPartAvailable = new bool[flattened.Count]; var indexedPartAvailable = new bool[flattened.Count];
var animatedPartTemplate = new LiveAnimationPartTemplate[flattened.Count]; var animatedPartTemplate = new LiveAnimationPartTemplate[flattened.Count];
var bounds = new LocalBoundsAccumulator(); var bounds = new LocalBoundsAccumulator();
int clothingTriangles = 0;
for (int partIndex = 0; partIndex < flattened.Count; partIndex++) for (int partIndex = 0; partIndex < flattened.Count; partIndex++)
{ {
@ -318,23 +270,10 @@ internal sealed class DatLiveEntityProjectionMaterializer
drawable); drawable);
if (gfx is null) if (gfx is null)
{ {
if (dumpClothing)
Console.WriteLine($" EMIT part={partIndex:D2} gfx=0x{part.GfxObjId:X8} GFXOBJ_DAT_MISSING -> 0 tris");
continue; continue;
} }
_collisionAssets.CacheGfxObj(part.GfxObjId, gfx); _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) if (GfxObjBounds.Get(gfx) is { } partBounds)
bounds.Add(transform, partBounds); bounds.Add(transform, partBounds);
@ -346,21 +285,8 @@ internal sealed class DatLiveEntityProjectionMaterializer
if (meshRefs.Count == 0) 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; return false;
} }
if (dumpClothing)
{
Console.WriteLine(
$" TOTAL tris={clothingTriangles} meshRefs={meshRefs.Count} "
+ $"(parts.Count={flattened.Count})");
}
PaletteOverride? paletteOverride = CreatePaletteOverride(canonicalSpawn); PaletteOverride? paletteOverride = CreatePaletteOverride(canonicalSpawn);
PartOverride[] partOverrides = CreatePartOverrides(animPartChanges); PartOverride[] partOverrides = CreatePartOverrides(animPartChanges);
@ -456,7 +382,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
indexedPartAvailable, indexedPartAvailable,
animatedPartTemplate, animatedPartTemplate,
bounds, bounds,
dumpLiveSpawns,
expectedCreateIntegrationVersion, expectedCreateIntegrationVersion,
synchronizeAnimation: supersessionRecovery); synchronizeAnimation: supersessionRecovery);
} }
@ -477,9 +402,7 @@ internal sealed class DatLiveEntityProjectionMaterializer
return slotZeroId; return slotZeroId;
} }
private void ApplyRetailCloseDegrades( private void ApplyRetailCloseDegrades(List<MeshRef> parts)
List<MeshRef> parts,
bool dumpClothing)
{ {
for (int partIndex = 0; partIndex < parts.Count; partIndex++) for (int partIndex = 0; partIndex < parts.Count; partIndex++)
{ {
@ -495,33 +418,13 @@ internal sealed class DatLiveEntityProjectionMaterializer
} }
parts[partIndex] = new MeshRef(resolvedId, part.PartTransform); 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<int, Dictionary<uint, uint>>? ResolveSurfaceOverrides( private Dictionary<int, Dictionary<uint, uint>>? ResolveSurfaceOverrides(
WorldSession.EntitySpawn spawn,
IReadOnlyList<MeshRef> parts, IReadOnlyList<MeshRef> parts,
IReadOnlyList<CreateObject.TextureChange> textureChanges, IReadOnlyList<CreateObject.TextureChange> textureChanges)
bool dumpClothing,
bool dumpLiveSpawns)
{ {
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) if (textureChanges.Count == 0)
return null; return null;
@ -536,8 +439,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
oldToNew[change.OldTexture] = change.NewTexture; oldToNew[change.OldTexture] = change.NewTexture;
} }
bool statueDiagnostic = dumpLiveSpawns
&& spawn.Name?.Contains("Statue", StringComparison.OrdinalIgnoreCase) == true;
var result = new Dictionary<int, Dictionary<uint, uint>>(); var result = new Dictionary<int, Dictionary<uint, uint>>();
for (int partIndex = 0; partIndex < parts.Count; partIndex++) for (int partIndex = 0; partIndex < parts.Count; partIndex++)
{ {
@ -547,12 +448,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
GfxObj? gfx = _dats.Get<GfxObj>(parts[partIndex].GfxObjId); GfxObj? gfx = _dats.Get<GfxObj>(parts[partIndex].GfxObjId);
if (gfx is null) if (gfx is null)
{ {
if (statueDiagnostic)
{
Console.WriteLine(
$"live: [STATUE] resolve part={partIndex} "
+ $"GfxObj 0x{parts[partIndex].GfxObjId:X8} missing");
}
continue; continue;
} }
_collisionAssets.CacheGfxObj(parts[partIndex].GfxObjId, gfx); _collisionAssets.CacheGfxObj(parts[partIndex].GfxObjId, gfx);
@ -729,7 +624,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
IReadOnlyList<bool> indexedPartAvailable, IReadOnlyList<bool> indexedPartAvailable,
IReadOnlyList<LiveAnimationPartTemplate> animatedPartTemplate, IReadOnlyList<LiveAnimationPartTemplate> animatedPartTemplate,
LocalBoundsAccumulator bounds, LocalBoundsAccumulator bounds,
bool dumpLiveSpawns,
ulong expectedCreateIntegrationVersion, ulong expectedCreateIntegrationVersion,
bool synchronizeAnimation) bool synchronizeAnimation)
{ {
@ -835,7 +729,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
_worldEvents.UpsertCurrent(snapshot); _worldEvents.UpsertCurrent(snapshot);
if (_runtime.TryMarkWorldSpawnPublished(spawn.Guid)) if (_runtime.TryMarkWorldSpawnPublished(spawn.Guid))
_worldEvents.FireEntitySpawned(snapshot); _worldEvents.FireEntitySpawned(snapshot);
_hydrated++;
if (!_runtime.IsCurrentCreateIntegration( if (!_runtime.IsCurrentCreateIntegration(
expectedRecord, expectedRecord,
@ -921,18 +814,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
return false; 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( return _runtime.IsCurrentCreateIntegration(
expectedRecord, expectedRecord,
expectedCreateIntegrationVersion); expectedCreateIntegrationVersion);
@ -961,18 +842,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
spawn, spawn,
idleCycle); 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 if (!retainedAnimation
&& idleCycle is not null && idleCycle is not null
&& idleCycle.Framerate != 0f && idleCycle.Framerate != 0f
@ -1159,56 +1028,6 @@ internal sealed class DatLiveEntityProjectionMaterializer
: null; : 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<MeshRef> flattened,
AnimationFrame? idleFrame,
IReadOnlyList<CreateObject.AnimPartChange> 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) private static bool IsIssue47HumanoidSetup(Setup setup)
{ {
if (setup.Parts.Count != 34) if (setup.Parts.Count != 34)

View file

@ -265,12 +265,6 @@ public static class PortalVisibilityBuilder
// stands near a doorway plane). // stands near a doorway plane).
private const float SeedInPlaneEpsilon = 0.0002f; 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<uint, int> s_pvDumpCount = new();
/// <summary> /// <summary>
/// #120 observable: total convergence-tripwire firings across both the /// #120 observable: total convergence-tripwire firings across both the
/// interior <see cref="Build"/> and the exterior look-in propagation. /// interior <see cref="Build"/> and the exterior look-in propagation.
@ -386,36 +380,6 @@ public static class PortalVisibilityBuilder
int churnReenqueues = 0; int churnReenqueues = 0;
var churnReciprocal = churnProbe ? new System.Text.StringBuilder(256) : null; 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 // T2 (BR-4): retail's growth propagation is IN PLACE, never by re-enqueue
// — PView::AddViewToPortals (Ghidra 0x005a52d0, pc:433446): first // — PView::AddViewToPortals (Ghidra 0x005a52d0, pc:433446): first
// discovery enqueues via InsCellTodoList; growth into a cell whose // discovery enqueues via InsCellTodoList; growth into a cell whose
@ -481,8 +445,6 @@ public static class PortalVisibilityBuilder
continue; 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 // (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. // traversal (cell=0x..0173 p->0x0171) can be attributed to the side test.
// Strip with the rest of the [pv-trace] apparatus. // Strip with the rest of the [pv-trace] apparatus.
@ -505,7 +467,6 @@ public static class PortalVisibilityBuilder
&& !CameraOnInteriorSide(cell, i, cameraPos)) && !CameraOnInteriorSide(cell, i, cameraPos))
{ {
trace?.Add($"portal cell=0x{cell.CellId:X8} p{i}->0x{portal.OtherCellId:X4} skip=side"); 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; continue;
} }
@ -526,8 +487,6 @@ public static class PortalVisibilityBuilder
endCount - processedCount, endCount - processedCount,
clippedRegion, clippedRegion,
out int clipVerts); 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 // Empty clip = no flood through this portal, period — retail's empty-GetClip rule
// (polyClipFinish <3 survivors → reject; ClipPortals adds no view). The // (polyClipFinish <3 survivors → reject; ClipPortals adds no view). The
@ -544,13 +503,6 @@ public static class PortalVisibilityBuilder
if (portal.OtherCellId == 0xFFFF) 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. // Exit portal -> outdoors visible through this (clipped) opening.
// OutsideView gates DRAWN color (terrain/sky/scissor), and the // OutsideView gates DRAWN color (terrain/sky/scissor), and the
// shell that rasterizes this aperture draws +drawLiftZ above // shell that rasterizes this aperture draws +drawLiftZ above
@ -688,9 +640,6 @@ public static class PortalVisibilityBuilder
ProcessCellPortals(cell, 0); 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 // 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. // root cell's per-portal side-test + projection + the frame's exit/visible counts.
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeFlapEnabled) if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeFlapEnabled)

View file

@ -42,14 +42,16 @@ public sealed record RuntimeOptions(
string? LivePass, string? LivePass,
bool DevTools, bool DevTools,
bool UncappedRendering, bool UncappedRendering,
/// <summary>#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.</summary>
bool DumpMoveTruth, bool DumpMoveTruth,
bool DumpSky, bool DumpSky,
bool NoAudio, bool NoAudio,
int HidePartIndex, int HidePartIndex,
bool RetailCloseDegrades, bool RetailCloseDegrades,
bool DumpSceneryZ, bool DumpSceneryZ,
bool DumpLiveSpawns,
bool DumpClothing,
int? LegacyStreamRadius, int? LegacyStreamRadius,
bool RetailUi, bool RetailUi,
/// <summary>Campaign CC slice CC4: interim env/test-only seam that opens /// <summary>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. // only for before/after diagnostic comparisons.
RetailCloseDegrades: !string.Equals(env("ACDREAM_RETAIL_CLOSE_DEGRADES"), "0", StringComparison.Ordinal), RetailCloseDegrades: !string.Equals(env("ACDREAM_RETAIL_CLOSE_DEGRADES"), "0", StringComparison.Ordinal),
DumpSceneryZ: IsExactlyOne(env("ACDREAM_DUMP_SCENERY_Z")), 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 // Legacy override for ACDREAM_STREAM_RADIUS. Caller applies it on
// top of the quality preset's radii. Null when unset or invalid. // top of the quality preset's radii. Null when unset or invalid.
LegacyStreamRadius: TryParseNonNegativeInt(env("ACDREAM_STREAM_RADIUS")), LegacyStreamRadius: TryParseNonNegativeInt(env("ACDREAM_STREAM_RADIUS")),

View file

@ -525,11 +525,6 @@ public sealed class ItemInteractionController : IDisposable
switch (mode) switch (mode)
{ {
case InteractionModeKind.Use: case InteractionModeKind.Use:
if (VendorDiagnostics.DumpVendorEnabled)
{
Console.WriteLine(
$"[vendor-diag] route=target-mode-click OfferPrimaryClick(Use) guid=0x{targetGuid:X8}");
}
ClearTargetMode(); ClearTargetMode();
accepted = ActivateItem(targetGuid); accepted = ActivateItem(targetGuid);
break; break;
@ -562,11 +557,6 @@ public sealed class ItemInteractionController : IDisposable
/// </summary> /// </summary>
public bool UseSelectedOrEnterMode(uint selectedObjectId) 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) if (selectedObjectId != 0)
return ActivateItem(selectedObjectId); return ActivateItem(selectedObjectId);
return _interactionState.EnterUse(); return _interactionState.EnterUse();
@ -666,11 +656,6 @@ public sealed class ItemInteractionController : IDisposable
public bool ActivateItem(uint itemGuid) 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 (itemGuid == 0) return false;
if (IsTargetModeActive) if (IsTargetModeActive)
@ -997,11 +982,6 @@ public sealed class ItemInteractionController : IDisposable
/// </summary> /// </summary>
public bool ExecuteConfirmedUse(uint objectId) 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)) if (objectId == 0u || (_requestUse is null && _sendUse is null))
return false; return false;
if (!EnsureInventoryRequestReady()) if (!EnsureInventoryRequestReady())

View file

@ -377,13 +377,6 @@ public sealed class SelectedObjectController : IRetainedPanelController
? $"{stackSize} {objectName}" ? $"{stackSize} {objectName}"
: 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 // ── 3. Selection overlay: brief flash (retail container ObjectSelected
// = Pause(0.25s)→Normal). "StackedItemSelected" for stacks. ────────────── // = Pause(0.25s)→Normal). "StackedItemSelected" for stacks. ──────────────
SetOverlayState(stackSize > 1u SetOverlayState(stackSize > 1u
@ -410,18 +403,6 @@ public sealed class SelectedObjectController : IRetainedPanelController
_splitQuantity.Reset(stackSize, initialValue: seed); _splitQuantity.Reset(stackSize, initialValue: seed);
if (_stackSizeEntry is not null) _stackSizeEntry.Visible = true; if (_stackSizeEntry is not null) _stackSizeEntry.Visible = true;
if (_stackSizeSlider is not null) _stackSizeSlider.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. // ── 4. Health: query, and show the meter only if real health is already known.

View file

@ -21,15 +21,13 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink
private readonly ILiveEntityTeardownCoordinator _teardown; private readonly ILiveEntityTeardownCoordinator _teardown;
private readonly ILocalPlayerIdentitySource _identity; private readonly ILocalPlayerIdentitySource _identity;
private readonly DormantLiveEntityStore _dormant; private readonly DormantLiveEntityStore _dormant;
private readonly Action<string>? _diagnostic;
public LiveEntityDeletionController( public LiveEntityDeletionController(
LiveEntityRuntime runtime, LiveEntityRuntime runtime,
RuntimeEntityObjectLifetime entityObjects, RuntimeEntityObjectLifetime entityObjects,
ILiveEntityTeardownCoordinator teardown, ILiveEntityTeardownCoordinator teardown,
ILocalPlayerIdentitySource identity, ILocalPlayerIdentitySource identity,
DormantLiveEntityStore? dormant = null, DormantLiveEntityStore? dormant = null)
Action<string>? diagnostic = null)
{ {
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
_entityObjects = entityObjects _entityObjects = entityObjects
@ -37,7 +35,6 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink
_teardown = teardown ?? throw new ArgumentNullException(nameof(teardown)); _teardown = teardown ?? throw new ArgumentNullException(nameof(teardown));
_identity = identity ?? throw new ArgumentNullException(nameof(identity)); _identity = identity ?? throw new ArgumentNullException(nameof(identity));
_dormant = dormant ?? new DormantLiveEntityStore(); _dormant = dormant ?? new DormantLiveEntityStore();
_diagnostic = diagnostic;
} }
/// <summary> /// <summary>
@ -64,13 +61,6 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink
if (removed) if (removed)
{ {
_dormant.RemoveExact(delete); _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; return removed || removedDormant;
} }
@ -95,8 +85,6 @@ internal sealed class LiveEntityDeletionController : ILiveEntityPruneSink
return false; return false;
_dormant.Retain(snapshot); _dormant.Retain(snapshot);
_diagnostic?.Invoke(
$"live: dormant guid=0x{candidate.ServerGuid:X8} instSeq={candidate.Generation}");
return true; return true;
} }
} }

View file

@ -180,7 +180,6 @@ internal sealed class LiveEntityHydrationController : ILiveEntityLandblockLoaded
private readonly ILocalPlayerIdentitySource _identity; private readonly ILocalPlayerIdentitySource _identity;
private readonly LiveEntityDeletionController _deletion; private readonly LiveEntityDeletionController _deletion;
private readonly DormantLiveEntityStore _dormant; private readonly DormantLiveEntityStore _dormant;
private readonly Action<string>? _diagnostic;
/// <summary> /// <summary>
/// C3c: the graphical first-entry drive pump — pumped at the end of each /// C3c: the graphical first-entry drive pump — pumped at the end of each
/// Create transaction so a fresh residence drives its conductor /// Create transaction so a fresh residence drives its conductor
@ -219,7 +218,6 @@ internal sealed class LiveEntityHydrationController : ILiveEntityLandblockLoaded
ILocalPlayerIdentitySource identity, ILocalPlayerIdentitySource identity,
LiveEntityDeletionController deletion, LiveEntityDeletionController deletion,
DormantLiveEntityStore? dormant = null, DormantLiveEntityStore? dormant = null,
Action<string>? diagnostic = null,
RuntimeFirstEntryDriveController? firstEntry = null, RuntimeFirstEntryDriveController? firstEntry = null,
RuntimeAcceptedPositionDriveController? acceptedPositionDrive = null) RuntimeAcceptedPositionDriveController? acceptedPositionDrive = null)
{ {
@ -236,7 +234,6 @@ internal sealed class LiveEntityHydrationController : ILiveEntityLandblockLoaded
_identity = identity ?? throw new ArgumentNullException(nameof(identity)); _identity = identity ?? throw new ArgumentNullException(nameof(identity));
_deletion = deletion ?? throw new ArgumentNullException(nameof(deletion)); _deletion = deletion ?? throw new ArgumentNullException(nameof(deletion));
_dormant = dormant ?? new DormantLiveEntityStore(); _dormant = dormant ?? new DormantLiveEntityStore();
_diagnostic = diagnostic;
_firstEntry = firstEntry; _firstEntry = firstEntry;
_acceptedPositionDrive = acceptedPositionDrive; _acceptedPositionDrive = acceptedPositionDrive;
} }
@ -465,11 +462,6 @@ AppearanceSynchronization:
ChildUnparentDisposition disposition = ChildUnparentDisposition disposition =
_relationships.OnChildBecameUnparented(update.Guid); _relationships.OnChildBecameUnparented(update.Guid);
bool accepted = disposition is not ChildUnparentDisposition.Superseded; 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; return accepted;
} }
@ -601,7 +593,6 @@ AppearanceSynchronization:
if (candidates.Count == 0) if (candidates.Count == 0)
return; return;
int projected = 0;
lock (_datLock) lock (_datLock)
{ {
foreach (RuntimeEntityRecord candidate in candidates) foreach (RuntimeEntityRecord candidate in candidates)
@ -623,26 +614,21 @@ AppearanceSynchronization:
candidate.Snapshot, candidate.Snapshot,
LiveProjectionPurpose.CreateSupersessionRecovery)) LiveProjectionPurpose.CreateSupersessionRecovery))
{ {
projected++;
projectedCanonicalAppearance = true; projectedCanonicalAppearance = true;
} }
} }
else if (record?.WorldEntity is not null else if (record?.WorldEntity is not null
&& record.InitialHydrationCompleted) && record.InitialHydrationCompleted)
{ {
if (_runtime.RebucketLiveEntity( _runtime.RebucketLiveEntity(
record.ServerGuid, record.ServerGuid,
record.ProjectionCellId)) record.ProjectionCellId);
{
projected++;
}
} }
else if (ProjectExact( else if (ProjectExact(
candidate, candidate,
candidate.Snapshot, candidate.Snapshot,
LiveProjectionPurpose.SpatialRecovery)) LiveProjectionPurpose.SpatialRecovery))
{ {
projected++;
projectedCanonicalAppearance = true; projectedCanonicalAppearance = true;
} }
@ -658,21 +644,14 @@ AppearanceSynchronization:
if (record is not null if (record is not null
&& _runtime.IsCurrentRecord(record) && _runtime.IsCurrentRecord(record)
&& record.AppearanceProjectionSynchronizationPending && record.AppearanceProjectionSynchronizationPending)
&& SynchronizeAppearance(
record,
record.ObjDescAuthorityVersion))
{ {
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( public bool EnsureWorldOrigin(

View file

@ -822,13 +822,6 @@ public static class GameEventWiring
registrar.Register(GameEventType.ApproachVendor, e => registrar.Register(GameEventType.ApproachVendor, e =>
{ {
var p = VendorApproach.TryParse(e.Payload.Span); 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; if (p is null) return;
var profile = new VendorShopProfile( var profile = new VendorShopProfile(
@ -846,14 +839,6 @@ public static class GameEventWiring
for (int i = 0; i < shopItems.Length; i++) for (int i = 0; i < shopItems.Length; i++)
{ {
VendorApproach.ItemProfile item = p.Value.Items[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( shopItems[i] = new VendorShopItem(
item.ItemGuid, item.ItemGuid,
item.StackSize, item.StackSize,
@ -950,12 +935,6 @@ public static class GameEventWiring
registrar.Register(GameEventType.UseDone, e => registrar.Register(GameEventType.UseDone, e =>
{ {
uint? err = GameEvents.ParseUseDone(e.Payload.Span); 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; if (err is null) return;
// Already the diagnostics-only log line SHOULD-FIX 4 asks for — // Already the diagnostics-only log line SHOULD-FIX 4 asks for —
// it fires unconditionally, so an unmapped code below stays // 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 // player this is the authoritative source (the per-item
// SpellBook flag in IdentifyObjectResponse is for caster // SpellBook flag in IdentifyObjectResponse is for caster
// items / scrolls only). // items / scrolls only).
bool dumpPd = Environment.GetEnvironmentVariable("ACDREAM_DUMP_VITALS") == "1";
registrar.Register(GameEventType.PlayerDescription, e => registrar.Register(GameEventType.PlayerDescription, e =>
{ {
var p = PlayerDescriptionParser.TryParse(e.Payload.Span); 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; if (p is null) return;
// R3: a trailer-truncated parse carries zero placeholder option // R3: a trailer-truncated parse carries zero placeholder option
@ -1106,8 +1082,6 @@ public static class GameEventWiring
if (attr.Current is uint cur) if (attr.Current is uint cur)
{ {
// Vital entry (id 7/8/9) — has absolute current. // 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( localPlayer.OnVitalUpdate(
vitalId: attr.AtType, vitalId: attr.AtType,
ranks: attr.Ranks, ranks: attr.Ranks,
@ -1120,8 +1094,6 @@ public static class GameEventWiring
// Primary attribute (id 1..6) — Endurance+Self feed // Primary attribute (id 1..6) — Endurance+Self feed
// the vital max formula (Endurance/2 for Health, // the vital max formula (Endurance/2 for Health,
// Endurance for Stamina, Self for Mana). // Endurance for Stamina, Self for Mana).
if (dumpPd)
Console.WriteLine($"vitals: PD-attr id={attr.AtType} ranks={attr.Ranks} start={attr.Start}");
localPlayer.OnAttributeUpdate( localPlayer.OnAttributeUpdate(
atType: attr.AtType, atType: attr.AtType,
ranks: attr.Ranks, ranks: attr.Ranks,
@ -1174,10 +1146,6 @@ public static class GameEventWiring
int total = (int)(formulaBonus + s.Init + s.Ranks); int total = (int)(formulaBonus + s.Init + s.Ranks);
if (s.SkillId == 24u) runSkill = total; if (s.SkillId == 24u) runSkill = total;
else if (s.SkillId == 22u) jumpSkill = 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) if (runSkill >= 0 || jumpSkill >= 0)
onSkillsUpdated?.Invoke(runSkill, jumpSkill); onSkillsUpdated?.Invoke(runSkill, jumpSkill);

View file

@ -447,7 +447,7 @@ public static class PlayerDescriptionParser
} }
} }
} }
catch (FormatException ex) catch (FormatException)
{ {
// Trailer corrupted — keep what we have and flag it. Once // Trailer corrupted — keep what we have and flag it. Once
// Tasks 3-9 add list reads inside this try block, partial // 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- // them so they can ignore the trailer if they need all-or-
// nothing semantics. // nothing semantics.
trailerTruncated = true; 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( return new Parsed(
@ -466,12 +464,9 @@ public static class PlayerDescriptionParser
shortcuts, hotbarSpells, desiredComps, spellbookFilters, shortcuts, hotbarSpells, desiredComps, spellbookFilters,
gameplayOptions, inventory, equipped, trailerTruncated); gameplayOptions, inventory, equipped, trailerTruncated);
} }
catch (FormatException ex) catch (FormatException)
{ {
// Truncation mid-walk — return null so caller knows parse failed. // 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; return null;
} }
} }

View file

@ -780,15 +780,12 @@ public sealed class WorldSession : IDisposable
// Issue #5 diagnostics (env-var-gated): // Issue #5 diagnostics (env-var-gated):
// ACDREAM_DUMP_OPCODES=1 → log first occurrence of each unhandled opcode // 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 // ACDREAM_DUMP_APPEARANCE=1 → log every 0xF625 ObjDescEvent + 0xF7DB UpdateObject
// with body len, target guid, hex preview. Used to // with body len, target guid, hex preview. Used to
// debug remote-player appearance asymmetry (retail // debug remote-player appearance asymmetry (retail
// observer in acdream renders wrong skin/hair). // observer in acdream renders wrong skin/hair).
private static readonly bool DumpOpcodesEnabled = private static readonly bool DumpOpcodesEnabled =
Environment.GetEnvironmentVariable("ACDREAM_DUMP_OPCODES") == "1"; Environment.GetEnvironmentVariable("ACDREAM_DUMP_OPCODES") == "1";
private static readonly bool DumpVitalsEnabled =
Environment.GetEnvironmentVariable("ACDREAM_DUMP_VITALS") == "1";
private static readonly bool DumpAppearanceEnabled = private static readonly bool DumpAppearanceEnabled =
Environment.GetEnvironmentVariable("ACDREAM_DUMP_APPEARANCE") == "1"; Environment.GetEnvironmentVariable("ACDREAM_DUMP_APPEARANCE") == "1";
private readonly System.Collections.Generic.HashSet<uint> _seenUnhandledOpcodes = new(); private readonly System.Collections.Generic.HashSet<uint> _seenUnhandledOpcodes = new();
@ -2249,8 +2246,6 @@ public sealed class WorldSession : IDisposable
// format per holtburger UpdateVital<false> — see // format per holtburger UpdateVital<false> — see
// PrivateUpdateVital.TryParseFull. // PrivateUpdateVital.TryParseFull.
var parsed = PrivateUpdateVital.TryParseFull(body); 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) if (parsed is not null)
VitalUpdated?.Invoke(parsed.Value); VitalUpdated?.Invoke(parsed.Value);
} }
@ -2259,8 +2254,6 @@ public sealed class WorldSession : IDisposable
// Issue #5: current-only delta (regen ticks / drains). // Issue #5: current-only delta (regen ticks / drains).
// Wire format per holtburger UpdateVitalCurrent<false>. // Wire format per holtburger UpdateVitalCurrent<false>.
var parsed = PrivateUpdateVital.TryParseCurrent(body); 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) if (parsed is not null)
VitalCurrentUpdated?.Invoke(parsed.Value); VitalCurrentUpdated?.Invoke(parsed.Value);
} }

View file

@ -1,27 +0,0 @@
using System;
namespace AcDream.Core.Items;
/// <summary>
/// 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 <c>[vendor-diag]</c> and gated on <see cref="DumpVendorEnabled"/>
/// so the family is a true no-op when the flag is unset.
/// </summary>
/// <remarks>
/// Read once from <c>ACDREAM_DUMP_VENDOR=1</c> at process start, per Code
/// Structure Rule 5 (one static diagnostic-owner class per subsystem, no
/// per-call-site <see cref="Environment.GetEnvironmentVariable"/> reads).
/// Lives in <c>AcDream.Core</c> — 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.
/// </remarks>
public static class VendorDiagnostics
{
public static bool DumpVendorEnabled { get; } =
Environment.GetEnvironmentVariable("ACDREAM_DUMP_VENDOR") == "1";
}

View file

@ -1637,7 +1637,7 @@ public static class PhysicsDiagnostics
// channel), naming which phase halted the attempt and, on Collided, // channel), naming which phase halted the attempt and, on Collided,
// the colliding polygon's normal and which of the three channels // the colliding polygon's normal and which of the three channels
// wrote it. // 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 // ACDREAM_DUMP_STEPUP probe's own input-normal-and-verdict content
// so a stuck-tick capture carries the step chain without running a // so a stuck-tick capture carries the step chain without running a
// second flag. // second flag.
@ -1753,7 +1753,7 @@ public static class PhysicsDiagnostics
/// <summary> /// <summary>
/// One line per <c>Transition.DoStepUp</c> entry or exit, mirroring the /// One line per <c>Transition.DoStepUp</c> entry or exit, mirroring the
/// content of the existing <c>ACDREAM_DUMP_STEPUP</c> probe (same input /// content of the retired <c>ACDREAM_DUMP_STEPUP</c> probe (same input
/// normal / walkable verdict / landing-plane fields) so a stuck-tick /// normal / walkable verdict / landing-plane fields) so a stuck-tick
/// capture carries the step-up chain without a second flag. /// capture carries the step-up chain without a second flag.
/// <paramref name="edge"/> is <c>"enter"</c> or <c>"exit"</c>; /// <paramref name="edge"/> is <c>"enter"</c> or <c>"exit"</c>;

View file

@ -1349,9 +1349,6 @@ public sealed class Transition
CollisionInfo.CopyFrom(source.CollisionInfo); 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 // A6.P7 (2026-05-25) — retail-binary shape dispatch rule
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -2227,7 +2224,6 @@ public sealed class Transition
if (ci.ContactPlaneValid) if (ci.ContactPlaneValid)
return TransitionState.OK; return TransitionState.OK;
DumpStepDownBranchGate(contactInvalid: true);
if (oi.Contact && !sp.StepDown && sp.CheckCellId != 0 && oi.StepDown) if (oi.Contact && !sp.StepDown && sp.CheckCellId != 0 && oi.StepDown)
{ {
// L.2.3i (2026-04-29): retail uses FloorZ when OnWalkable, // 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 // intentionally narrow: it tells the next L.2c slice whether
// we are missing precipice context, a steep contact plane, or // we are missing precipice context, a steep contact plane, or
// merely the EdgeSlide flag. // merely the EdgeSlide flag.
DumpEdgeSlideStepDownFailed(stepDownHeight, zVal);
bool stop = EdgeSlideAfterStepDownFailed( bool stop = EdgeSlideAfterStepDownFailed(
engine, engine,
@ -2469,7 +2464,6 @@ public sealed class Transition
TransitionCellCollisionPhase.Objects, TransitionCellCollisionPhase.Objects,
cellId, cellId,
FindObjCollisionsInCell(engine, cellId)); FindObjCollisionsInCell(engine, cellId));
DumpPhase2(innerAttempt, environment, objects);
PhysicsDiagnostics.TraceTransitInsertAttempt( PhysicsDiagnostics.TraceTransitInsertAttempt(
ObjectInfo.SelfEntityId, innerAttempt, "objects", ObjectInfo.SelfEntityId, innerAttempt, "objects",
environment, building, objects, objects, environment, building, objects, objects,
@ -2501,7 +2495,6 @@ public sealed class Transition
// No steep-plane exception precedes this gate (0050b3d8-0050b3e7). // No steep-plane exception precedes this gate (0050b3d8-0050b3e7).
if (!oi.OnWalkable || !oi.EdgeSlide) if (!oi.OnWalkable || !oi.EdgeSlide)
{ {
DumpEdgeSlideBranch("branch1/!onwalkable-or-!edgeslide", zVal);
sp.ClearWalkable(); sp.ClearWalkable();
sp.RestoreCheckPos(); sp.RestoreCheckPos();
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
@ -2513,7 +2506,6 @@ public sealed class Transition
if (ci.ContactPlaneValid && ci.ContactPlane.Normal.Z < zVal) if (ci.ContactPlaneValid && ci.ContactPlane.Normal.Z < zVal)
{ {
var cliffPlane = ci.ContactPlane; var cliffPlane = ci.ContactPlane;
DumpEdgeSlideBranch("branch2/steep-cliffslide", zVal);
sp.ClearWalkable(); sp.ClearWalkable();
sp.RestoreCheckPos(); sp.RestoreCheckPos();
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
@ -2536,7 +2528,6 @@ public sealed class Transition
// rapidly down the stairs. Do not restore stale history here. // rapidly down the stairs. Do not restore stale history here.
if (sp.HasWalkablePolygon) if (sp.HasWalkablePolygon)
{ {
DumpEdgeSlideBranch("branch3/precipice-slide", zVal);
sp.RestoreCheckPos(); sp.RestoreCheckPos();
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false; ci.ContactPlaneIsWater = false;
@ -2546,7 +2537,6 @@ public sealed class Transition
if (ci.ContactPlaneValid) if (ci.ContactPlaneValid)
{ {
DumpEdgeSlideBranch("branch4/contact-no-walkable", zVal);
sp.ClearWalkable(); sp.ClearWalkable();
sp.RestoreCheckPos(); sp.RestoreCheckPos();
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
@ -2624,8 +2614,6 @@ public sealed class Transition
Vector3 collideNormal = new(-contactNormal.Y, contactNormal.X, 0f); Vector3 collideNormal = new(-contactNormal.Y, contactNormal.X, 0f);
if (collideNormal.LengthSquared() < PhysicsGlobals.EpsilonSq) if (collideNormal.LengthSquared() < PhysicsGlobals.EpsilonSq)
{ {
DumpCliffSlide("degenerate-cross/last-known", contactPlane,
new Plane(referenceNormal, 0f), contactNormal, 0f, false);
return TransitionState.OK; return TransitionState.OK;
} }
@ -2633,8 +2621,6 @@ public sealed class Transition
Vector3 offset = sp.GlobalSphere[0].Origin - sp.GlobalCurrCenter[0].Origin; Vector3 offset = sp.GlobalSphere[0].Origin - sp.GlobalCurrCenter[0].Origin;
float angle = Vector3.Dot(collideNormal, offset); float angle = Vector3.Dot(collideNormal, offset);
DumpCliffSlide("ok/last-known", contactPlane,
new Plane(referenceNormal, 0f), collideNormal, angle, true);
if (angle <= 0f) if (angle <= 0f)
{ {
@ -2650,89 +2636,6 @@ public sealed class Transition
return TransitionState.Adjusted; 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}"));
}
/// <summary>
/// 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.
/// </summary>
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}"));
}
/// <summary>
/// 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.
/// </summary>
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}"));
}
/// <summary>
/// L.4-diag: log which branch of EdgeSlideAfterStepDownFailed fired.
/// Tells us whether CliffSlide gets called or whether we hit a
/// stop-at-edge branch.
/// </summary>
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}"));
}
/// <summary>
/// L.4-diag: log CliffSlide invocation. Tells us whether the
/// cross-product is degenerate (no slide) or producing a real
/// deflection.
/// </summary>
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 // Environment collision — outdoor terrain
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -5739,29 +5642,9 @@ public sealed class Transition
var ci = CollisionInfo; var ci = CollisionInfo;
var oi = ObjectInfo; var oi = ObjectInfo;
// L.2.3f (2026-04-29): diagnostic for steep-roof bug. Logs the // #345 probe (2026-08-08): buffers this step-up attempt into the
// input polygon normal that triggered step-up. The verdict tells // stuck-tick trace (self-guards internally, zero cost when
// whether THIS polygon would pass FloorZ (≈ 0.66) — but actual // ACDREAM_DUMP_TRANSIT_FAIL is unset).
// 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.
PhysicsDiagnostics.TraceTransitStepUp( PhysicsDiagnostics.TraceTransitStepUp(
oi.SelfEntityId, "enter", collisionNormal, oi.SelfEntityId, "enter", collisionNormal,
onWalkable: (oi.State & ObjectInfoState.OnWalkable) != 0, onWalkable: (oi.State & ObjectInfoState.OnWalkable) != 0,
@ -5807,28 +5690,6 @@ public sealed class Transition
sp.StepUp = false; sp.StepUp = false;
sp.ClearWalkable(); 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" // #345 probe (2026-08-08): the matching exit line for the "enter"
// trace above. // trace above.
PhysicsDiagnostics.TraceTransitStepUp( PhysicsDiagnostics.TraceTransitStepUp(

View file

@ -224,11 +224,6 @@ public sealed class RuntimeInteractionTransactionState : IDisposable
verdict = RuntimeInteractionDispatchResult.Dispatched; 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; return verdict;
} }
@ -633,11 +628,6 @@ public sealed class RuntimeInteractionTransactionState : IDisposable
if (_pendingUse is not { } current if (_pendingUse is not { } current
|| current.ApproachToken != approachToken) || 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; pending = default;
return false; return false;
} }
@ -645,11 +635,6 @@ public sealed class RuntimeInteractionTransactionState : IDisposable
_pendingUse = null; _pendingUse = null;
pending = current; pending = current;
IncrementRevision(); IncrementRevision();
if (VendorDiagnostics.DumpVendorEnabled)
{
Console.WriteLine(
$"[vendor-diag] TryResolveUseApproachCompletion guid=0x{pending.ServerGuid:X8} natural={natural} accepted={natural}");
}
return natural; return natural;
} }

View file

@ -191,7 +191,6 @@ public sealed class VendorShopItemMaterializer : IDisposable
} }
} }
int diagIndex = 0;
foreach (VendorShopItem item in currentItems) foreach (VendorShopItem item in currentItems)
{ {
bool ownedAlready = _ownedGuids.ContainsKey(item.ItemGuid); bool ownedAlready = _ownedGuids.ContainsKey(item.ItemGuid);
@ -208,16 +207,6 @@ public sealed class VendorShopItemMaterializer : IDisposable
} }
WeenieData materialized = ToWeenieData(item, transition.VendorId); 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); _objects.Ingest(materialized);
nextOwned[item.ItemGuid] = transition.VendorId; nextOwned[item.ItemGuid] = transition.VendorId;

View file

@ -47,17 +47,13 @@ public sealed class LaunchOptionsDocumentationTests
["src/AcDream.App/Physics/RemoteServerControlledVelocityCycle.cs"] = 1, ["src/AcDream.App/Physics/RemoteServerControlledVelocityCycle.cs"] = 1,
["src/AcDream.App/Platform/GraphicalWindowBackendSelection.cs"] = 1, ["src/AcDream.App/Platform/GraphicalWindowBackendSelection.cs"] = 1,
["src/AcDream.App/Rendering/GameWindow.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/Sky/SkyRenderer.cs"] = 1,
["src/AcDream.App/Rendering/TextureCache.cs"] = 1, ["src/AcDream.App/Rendering/TextureCache.cs"] = 1,
["src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs"] = 2, ["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/Vfx/PhysicsScriptRunner.cs"] = 1,
["src/AcDream.Core/World/SkyDescLoader.cs"] = 2, ["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/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/ApplicationPathSet.cs"] = 3,
["src/AcDream.Platform/BakePublicationGuardPaths.cs"] = 1, ["src/AcDream.Platform/BakePublicationGuardPaths.cs"] = 1,
["src/AcDream.Runtime/Physics/RuntimeRemotePhysicsUpdater.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."); + "removal commit) in the same commit that deletes the read site.");
} }
/// <summary>
/// The four flags that default ON. All are retail behaviors wearing an
/// A/B off-switch (<c>=0</c> 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.
/// </summary>
private static readonly IReadOnlySet<string> DefaultOnBehaviorFlags =
new HashSet<string>(StringComparer.Ordinal)
{
"ACDREAM_RETAIL_CHASE",
"ACDREAM_CAMERA_COLLIDE",
"ACDREAM_CAMERA_ALIGN_SLOPE",
"ACDREAM_RETAIL_CLOSE_DEGRADES",
};
/// <summary>
/// A read shaped "anything but the literal 0 enables" — the two
/// default-on idioms in this codebase:
/// <c>GetEnvironmentVariable("X") != "0"</c> and
/// <c>!string.Equals(env("X"), "0", ...)</c>.
/// </summary>
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<string>(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<string> 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<string> 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] [Fact]
public void DirectEnvironmentReadsOutsideOwnerClassesDoNotGrow() public void DirectEnvironmentReadsOutsideOwnerClassesDoNotGrow()
{ {

View file

@ -160,7 +160,6 @@ public sealed class RuntimeOptionsTests
// Default-on: RetailCloseDegrades is true unless explicitly disabled. // Default-on: RetailCloseDegrades is true unless explicitly disabled.
Assert.True(opts.RetailCloseDegrades); Assert.True(opts.RetailCloseDegrades);
Assert.False(opts.DumpSceneryZ); Assert.False(opts.DumpSceneryZ);
Assert.False(opts.DumpClothing);
Assert.Null(opts.LegacyStreamRadius); Assert.Null(opts.LegacyStreamRadius);
Assert.False(opts.UiProbeDump); Assert.False(opts.UiProbeDump);
Assert.Null(opts.UiProbeScript); Assert.Null(opts.UiProbeScript);
@ -508,7 +507,6 @@ public sealed class RuntimeOptionsTests
["ACDREAM_DUMP_SKY"] = "1", ["ACDREAM_DUMP_SKY"] = "1",
["ACDREAM_NO_AUDIO"] = "1", ["ACDREAM_NO_AUDIO"] = "1",
["ACDREAM_DUMP_SCENERY_Z"] = "1", ["ACDREAM_DUMP_SCENERY_Z"] = "1",
["ACDREAM_DUMP_CLOTHING"] = "1",
})); }));
Assert.True(allOn.DevTools); Assert.True(allOn.DevTools);
Assert.True(allOn.UncappedRendering); Assert.True(allOn.UncappedRendering);
@ -516,25 +514,22 @@ public sealed class RuntimeOptionsTests
Assert.True(allOn.DumpSky); Assert.True(allOn.DumpSky);
Assert.True(allOn.NoAudio); Assert.True(allOn.NoAudio);
Assert.True(allOn.DumpSceneryZ); Assert.True(allOn.DumpSceneryZ);
Assert.True(allOn.DumpClothing);
// Any non-"1" value leaves them off, matching the // Any non-"1" value leaves them off, matching the
// string.Equals(env, "1", StringComparison.Ordinal) check. // string.Equals(env, "1", StringComparison.Ordinal) check.
var anyOther = RuntimeOptions.Parse(AnyDatDir, Env(new() var anyOther = RuntimeOptions.Parse(AnyDatDir, Env(new()
{ {
["ACDREAM_DUMP_MOVE_TRUTH"] = "true",
["ACDREAM_DEVTOOLS"] = "true", ["ACDREAM_DEVTOOLS"] = "true",
["ACDREAM_UNCAPPED_RENDER"] = "true", ["ACDREAM_UNCAPPED_RENDER"] = "true",
["ACDREAM_DUMP_MOVE_TRUTH"] = "yes",
["ACDREAM_NO_AUDIO"] = "2", ["ACDREAM_NO_AUDIO"] = "2",
["ACDREAM_DUMP_SCENERY_Z"] = " 1", ["ACDREAM_DUMP_SCENERY_Z"] = " 1",
["ACDREAM_DUMP_CLOTHING"] = "true",
})); }));
Assert.False(anyOther.DevTools); Assert.False(anyOther.DevTools);
Assert.False(anyOther.UncappedRendering); Assert.False(anyOther.UncappedRendering);
Assert.False(anyOther.DumpMoveTruth); Assert.False(anyOther.DumpMoveTruth);
Assert.False(anyOther.NoAudio); Assert.False(anyOther.NoAudio);
Assert.False(anyOther.DumpSceneryZ); Assert.False(anyOther.DumpSceneryZ);
Assert.False(anyOther.DumpClothing);
} }
[Fact] [Fact]

View file

@ -320,7 +320,6 @@ public class CellarLipWedgeTests
var sw = new StringWriter(); var sw = new StringWriter();
PhysicsDiagnostics.ProbeIndoorBspEnabled = true; PhysicsDiagnostics.ProbeIndoorBspEnabled = true;
PhysicsDiagnostics.ProbeStepWalkEnabled = true; PhysicsDiagnostics.ProbeStepWalkEnabled = true;
Environment.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", "1");
Console.SetOut(sw); Console.SetOut(sw);
try try
{ {
@ -334,7 +333,6 @@ public class CellarLipWedgeTests
finally finally
{ {
Console.SetOut(saved); Console.SetOut(saved);
Environment.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", null);
PhysicsDiagnostics.ProbeIndoorBspEnabled = false; PhysicsDiagnostics.ProbeIndoorBspEnabled = false;
PhysicsDiagnostics.ProbeStepWalkEnabled = false; PhysicsDiagnostics.ProbeStepWalkEnabled = false;
} }

View file

@ -267,7 +267,6 @@ public class DoorCollisionApparatusTests
PhysicsDiagnostics.ProbeResolveEnabled = true; PhysicsDiagnostics.ProbeResolveEnabled = true;
PhysicsDiagnostics.ProbeBuildingEnabled = true; PhysicsDiagnostics.ProbeBuildingEnabled = true;
Env.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", "1");
// Synthetic floor plane at Z = 0 so the grounded sphere has a // Synthetic floor plane at Z = 0 so the grounded sphere has a
// walkable plane to rest on. Sphere foot center starts at Z=radius // 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. // (Y < 12.0) — the same shape as the Path-6 apparatus tests above.
PhysicsDiagnostics.ProbeResolveEnabled = false; PhysicsDiagnostics.ProbeResolveEnabled = false;
PhysicsDiagnostics.ProbeBuildingEnabled = false; PhysicsDiagnostics.ProbeBuildingEnabled = false;
Env.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", null);
Assert.True(blocked, Assert.True(blocked,
$"Door must block the grounded off-center approach (#99 closed). " + $"Door must block the grounded off-center approach (#99 closed). " +