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