merge: Campaign P Slice P2 response-layer (TS-1 resolved, AP-7 ported, TS-4 stopped at escape valve)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> # Conflicts: # docs/architecture/retail-divergence-register.md
This commit is contained in:
commit
26e0334af3
9 changed files with 746 additions and 63 deletions
|
|
@ -4273,12 +4273,48 @@ contact chain diverges). Retiring those three rows IS this issue; do them
|
|||
together against a retail cdb capture of a downhill jump (velocity +
|
||||
contact-plane trace at landing).
|
||||
|
||||
**Where:** `PlayerMovementController.cs:874` (AD-25 suppression),
|
||||
`PhysicsBody.cs:307` (AP-7), `BSPQuery.cs:2001` (TS-4).
|
||||
**Reattribution (2026-07-30, Campaign P Slice P2 research pass —
|
||||
`docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §3,
|
||||
§6 Step 6):** confirmed as the AD-25 + AP-7 + TS-4 composite above, with
|
||||
two corrections to the original framing. First, **AD-25's LOCAL-PLAYER
|
||||
half was already ported** in the #182 verbatim `UpdateObjectInternal`
|
||||
rebuild (2026-07-07) — what remains open for AD-25 is remote/NPC-only and
|
||||
is explicitly Campaign P P3 scope, not this issue. Second, and more
|
||||
important: **no client-side `PhysicsState.Sledding` auto-toggle exists in
|
||||
retail, and this issue should not wait on inventing one.** A cross-
|
||||
reference of the named-retail decomp (zero hits for "sled" anywhere in the
|
||||
1.4M-line pseudo-C, string or hex-constant search) against ACE's complete
|
||||
`PhysicsObj.cs` (the same shared `CPhysicsObj` class that produced
|
||||
`calc_friction`) found the ONLY write site for `PhysicsState.Sledding`
|
||||
anywhere in any reference repo is a per-weenie game-data property
|
||||
(`WorldObject_Properties.cs:1105-1109`, server/database-set, same pattern
|
||||
as `Ethereal`/`Static`) — not a physics-engine landing response. Ordinary
|
||||
downhill-jump glide-and-bounce in retail is therefore NOT the literal
|
||||
Sledding state for an ordinary player; Sledding is most likely reserved
|
||||
for specific data-authored content (e.g. an actual sled-ride mechanic),
|
||||
outside this issue's scope. AP-7 landed this same session (`calc_friction`
|
||||
now ports retail's confirmed 0.25f threshold); TS-4's removal was attempted
|
||||
per its own fixture-first requirement and reproduced the historical
|
||||
2026-04-30 wedge, so **TS-4 stays deferred** (see its register row and the
|
||||
research doc §7 item 6 for the precise mechanism and the concrete next
|
||||
step). Closure of #166 therefore pends: (a) TS-4 actually landing, and (b)
|
||||
re-checking Campaign P's final visual matrix item 5 ("Downhill jump
|
||||
landing: sled glide + bounce") against a fresh capture — if the glide/
|
||||
bounce still visibly mismatches retail after AP-7 alone (with TS-4 still
|
||||
deferred), that capture, not a guess, is what should drive any further
|
||||
work here, and it should go through cdb against live retail before any
|
||||
client-side Sledding-state mechanism is written.
|
||||
|
||||
**Where:** `PlayerMovementController.cs:874` (AD-25 suppression, remote
|
||||
half only — local half already ported via #182),
|
||||
`src/AcDream.Core/Physics/PhysicsBody.cs` (`calc_friction`, AP-7 — DONE
|
||||
2026-07-30), `src/AcDream.Core/Physics/BSPQuery.cs` (Path 6 steep branches,
|
||||
TS-4 — deferred, shortcut still in place).
|
||||
|
||||
**Acceptance:** side-by-side downhill jump: acdream glides/bounces like
|
||||
retail; flat-ground landings unchanged; no micro-bounce death spiral
|
||||
(the reason AD-25 exists) reintroduced.
|
||||
(the reason AD-25 exists) reintroduced. Blocked on TS-4's eventual landing
|
||||
per the note above.
|
||||
|
||||
## #164 — UM action-replay dispatches drop the per-action Autonomous bit
|
||||
|
||||
|
|
|
|||
|
|
@ -62,10 +62,13 @@ accepted-divergence entries (#96, #49, #50).
|
|||
|
||||
---
|
||||
|
||||
## 2. Adaptation (AD) — 44 rows (AD-52 filed 2026-07-29 at Campaign N slice N6 — the fragment-assembler 60 s partial TTL + completed-sequence ring; AD-51 filed 2026-07-29 at Campaign N slice N4 — the reclaimed-word pool for ACE's fresh-sequence cleartext RejectRetransmit; AD-50 filed 2026-07-29 at Campaign N slice N2 — the inbound-watermark ACE init; AD-49 stays reserved for Campaign N §5's blob-layer ordering deferral, filed when its slice lands; AD-47 and AD-48 filed 2026-07-29 at Campaign V slice V11 — the MSAA sample-position and present-pacing rows the campaign's risk register scheduled for the GL deletion; AD-11 retired 2026-07-23 — exact low-bit ItemUses predicate; AD-31 retired 2026-07-15 — the DAT-authored portal-space viewport replaces the black transit cover)
|
||||
## 2. Adaptation (AD) — 47 rows (AD-55 filed 2026-07-30 at Campaign P Slice P2, split out of the retired AP-7 row — the open cos(10°)-vs-0.99999536f Sledding slope-flatness constant; AD-53/AD-54 filed the same slice, split out of the retired TS-1 row — CliffSlide's three-source reference-normal fallback chain and the walkable-steepness reroute to CliffSlide; AD-52 filed 2026-07-29 at Campaign N slice N6 — the fragment-assembler 60 s partial TTL + completed-sequence ring; AD-51 filed 2026-07-29 at Campaign N slice N4 — the reclaimed-word pool for ACE's fresh-sequence cleartext RejectRetransmit; AD-50 filed 2026-07-29 at Campaign N slice N2 — the inbound-watermark ACE init; AD-49 stays reserved for Campaign N §5's blob-layer ordering deferral, filed when its slice lands; AD-47 and AD-48 filed 2026-07-29 at Campaign V slice V11 — the MSAA sample-position and present-pacing rows the campaign's risk register scheduled for the GL deletion; AD-11 retired 2026-07-23 — exact low-bit ItemUses predicate; AD-31 retired 2026-07-15 — the DAT-authored portal-space viewport replaces the black transit cover)
|
||||
|
||||
| # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle |
|
||||
|---|---|---|---|---|---|
|
||||
| AD-53 | `Transition.CliffSlide`'s reference-normal cross-product operand tries THREE sources in priority order (`LastWalkablePlane` if `Normal.Z >= FloorZ`, then `LastKnownContactPlane` at the same threshold, then world-up `UnitZ`); retail's `CTransition::cliff_slide` uses `this->collision_info.last_known_contact_plane.N` directly, with no fallback chain | `src/AcDream.Core/Physics/TransitionTypes.cs` (`CliffSlide`, the `referenceNormal`/`refSource` selection above the cross-product) | Filed 2026-07-30 splitting TS-1's retirement (Campaign P Slice P2). A fresh read of `last_known_contact_plane`'s own maintenance (pc:272659-272668) confirms retail overwrites it unconditionally from `contact_plane` every `validate_transition` pass — including with a steep plane — so retail keeps NO separately-preserved flat-ground history there either; this fallback chain is a genuine acdream invention, not a retail-matching read. Kept because it compensates for AP-4's incomplete `OnWalkable` bookkeeping (L.4-cliffslide-fallback, 2026-04-30): without it, `cross(currentSteep, lastKnownSteep)` degenerates to zero after >1 frame on a continuous steep slope, and CliffSlide returns `OK` (no deflection) instead of downhill drift — the "stay on the roof" wedge the L.4 session fought | If AP-4's `OnWalkable` reordering is ever completed/removed, `last_known_contact_plane` should carry the same information retail's does and this fallback chain becomes unneeded ballast (or, worse, silently picks a stale `LastWalkablePlane` over the now-correct current one) — re-audit together with AP-4 | `CTransition::cliff_slide` pc:272397 (0050a6d0); `last_known_contact_plane` maintenance pc:272659-272668 (~0050ad07); `docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §2 gap #2 |
|
||||
| AD-54 | `Transition.EdgeSlideAfterStepDownFailed` reroutes to `CliffSlide` instead of `PrecipiceSlide` when the stored walkable polygon itself is steeper than `FloorZ` (`sp.WalkablePlane.Normal.Z < PhysicsGlobals.FloorZ`); retail's raw `SPHEREPATH::edge_slide` has no steepness branch here — `if (walkable != null) { ... precipice_slide(...) }` unconditionally | `src/AcDream.Core/Physics/TransitionTypes.cs` (`EdgeSlideAfterStepDownFailed`, the `L.4-walkable-steep` block) | Filed 2026-07-30 splitting TS-1's retirement (Campaign P Slice P2). The permissive `LandingZ` walkable-acceptance threshold that lets a steep roof become "walkable" in the first place IS confirmed retail-faithful (`BSPTREE::find_collisions` unconditional `walkable_allowance = LandingZ`, pc:323740-323783, TS-4's own citation) — so a steep-roof walkable polygon is a real state retail also reaches. What is NOT independently verified is whether retail's outer `transitional_insert` caller absorbs a same-polygon-standing `COLLIDED_TS` from `precipice_slide` (its raw `find_crossed_edge` returning false while standing on, not crossing, the polygon) some other way that avoids the acdream "stuck in a Collided revert loop" this reroute prevents | If retail's outer retry loop turns out to already handle the no-crossed-edge-while-standing-on-a-steep-poly case without a reroute, this branch is an unnecessary compensating layer that could route a genuinely PrecipiceSlide-bound case (a shallow polygon edge that happens to sit at exactly `FloorZ`) into CliffSlide instead | `SPHEREPATH::edge_slide` pc:273001-273090 (0050b3d0, direct walkable branch quoted at pc:364-370 in the P2 research doc); `BSPTREE::find_collisions` pc:323740-323783 (0053a730, unconditional `LandingZ`); `docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §2 gap #3 |
|
||||
| AD-55 | `calc_friction`'s Sledding slope-flatness test compares `GroundNormal.Z > 0.99999536f` (≈0.175° from flat); the raw retail decomp literally computes `__fcos(0.17453292519943295)` (= cos(10°) ≈ 0.984808) and compares that against `contact_plane.N.z` — physically very different tests (0.175° accepts only essentially-perfectly-flat ground; 10° accepts any modest slope) | `src/AcDream.Core/Physics/PhysicsBody.cs` (`calc_friction`, the Sledding near-flat branch) | Filed 2026-07-30 splitting AP-7's retirement (Campaign P Slice P2). Two hypotheses, neither confirmed this pass: (a) BN misdecompiled a raw float-constant load as an `__fcos()` call (a known BN artifact class), or (b) ACE's own port made an independent error and cos(10°) is correct. `0.99999536f` is kept provisionally — least churn, since it is what acdream's own prior (structurally unreachable) dead code already had — pending a live Ghidra decompile of `0050ee70` checking whether the FCOS opcode is real or a raw `FLD` of one of these two constants | Currently harmless in production: nothing sets `PhysicsState.Sledding` client-side (see #166 research), so this branch is unreachable either way. The moment a data-authored Sledding toggle exists, the wrong constant changes which slopes get the light 0.2f sled-friction override vs. the heavier default | `CPhysicsObj::calc_friction` pc:276694-276822 (0050ee70), the `__fcos(0.17453292519943295)` slope-flatness comparison; ACE `PhysicsObj.calc_friction` PhysicsObj.cs:2120-2141 (`0.99999536f`); `docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §1, §7 item 3 |
|
||||
| AD-46 | **LIVE. Reframed at Campaign V slice V11 (2026-07-29), when GL was deleted and the comparison that discovered this row ceased to exist.** Dense alpha-blended distant scenery (the treeline) may read slightly denser than retail's, because the anisotropic TAP PATTERN is implementation-defined and acdream's Vulkan driver does not tap identically to retail's D3D9 one. Both request the same sampler state — trilinear, clamp-and-repeat, the device's maximum anisotropy. **What changed at V11 is only the left-hand side of the comparison**: this was measured GL-vs-Vulkan (~15% of the pixels in the band), and it is now a Vulkan-vs-retail question against the D3D oracle in the last column. The measurement below is retained as the evidence that the residual is a tap pattern and not a bug, even though one of its two arms no longer exists. | `src/AcDream.App/Rendering/Wb/WorldTextureArray.cs` (`RhiWorldTextureArray.WorldArrayAnisotropy`); measured in plan §5.5.19, reframed §5.5.24 | Not assumed — narrowed by measurement while both backends still existed, on an offline capture with no session, no entities and both clocks pinned. Anisotropy 1 → 41,509 differing pixels in the tree band; anisotropy 16 (GL's value, and retail's `m_D3DCaps.MaxAnisotropy`) → 22,266, and the rest of the frame fell to 497 px of 563,200, i.e. 8.8e-04, inside the campaign's 0.001 threshold. The residual was not a sub-pixel shift (an integer shift search found none), not a sharpness change (high-frequency energy matched within 5%), and not depth precision (forcing Vulkan's window-depth range to GL's compressed [0.5, 1] moved it by 3%). Monotone improvement toward GL's own anisotropy with no knob left is what made it a driver property rather than a bug. | Distant foliage shimmers or reads denser than retail's. The class is confined to alpha-blended dense overlap: opaque terrain, roofs, walls, water, statics, the character and the whole retained UI are inside threshold. **Now unfalsifiable by self-differential** — with GL gone, the only way to retire this row is a side-by-side against the retail client, not against another acdream backend. | `RenderDeviceD3D::SetDefaultD3DStates @ 0x005a3800`, whose `SetSamplerState(stage, 0xA /* D3DSAMP_MAXANISOTROPY */, m_D3DCaps.MaxAnisotropy)` at `0x005a4230` is the value acdream requests |
|
||||
| AD-47 | **Filed at Campaign V slice V11 (2026-07-29); the campaign's risk register scheduled this row here.** Multisample resolve sample POSITIONS are unspecified by both the Vulkan and D3D9 specifications, so acdream's MSAA-on silhouette edges do not match retail's pixel-for-pixel even at the same sample count. acdream's strict pixel gates therefore run with MSAA forced OFF on every arm, and MSAA-on gets only a relaxed visual smoke. | `src/AcDream.App/RuntimeOptions.cs` (`ACDREAM_MSAA_SAMPLES`); forced to 0 in `tools/run-offline-pixel-gate.ps1` | Measured, not assumed: plan §5.5.16 compared two backends at 4x and found **8.83% of the frame differing — 81,359 px of 921,600 — essentially all of it hugging foliage and silhouette edges**, which is ninety-fold over the 0.001 gate threshold. That is two implementations' sample patterns, not a renderer divergence, which is why forcing MSAA off is what makes the remaining difference attributable rather than a threshold relaxation. | Edge quality on thin geometry (fence rails, foliage, distant railings) differs from retail at the sub-pixel level whenever MSAA is on, which is the ordinary player configuration. Because the gates run MSAA off, **a real regression confined to the multisample path would not be caught by them** — that is the actual exposure this row records. | D3D9 `D3DRS_MULTISAMPLEANTIALIAS` / `D3DMULTISAMPLE_TYPE` as set by `RenderDeviceD3D::SetDefaultD3DStates @ 0x005a3800`; retail's sample pattern is the driver's, exactly as ours is |
|
||||
| AD-48 | **Filed at Campaign V slice V11 (2026-07-29).** Presentation is paced by the Vulkan swapchain present mode (FIFO, i.e. VSync) or by a refresh-rate software pacer when uncapped, rather than by retail's D3D9 `Present` with its own frame-rate limiter. Frame delivery cadence, and therefore input-to-photon latency, is a property of our present path rather than a port of retail's. | `src/AcDream.App/RuntimeOptions.cs:98-100`; `src/AcDream.App/Rendering/Gpu/Vk/VulkanSwapchain.cs` | Retail's limiter and ours both bound the frame rate to the display; the simulation is fixed-step and clock-driven, so gameplay timing does not ride on presentation cadence. The uncapped path exists for measurement and is not the shipping default. | A pacing mismatch shows up as judder or input latency that differs from retail's feel without any visual difference in a captured frame — invisible to every pixel gate by construction. Issue **#235** (the capped/RDP jump-presentation cadence alias) is the known live instance of this class. | D3D9 `IDirect3DDevice9::Present`; retail's frame limiter in `RenderDeviceD3D` |
|
||||
|
|
@ -114,7 +117,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
|
||||
---
|
||||
|
||||
## 3. Documented approximation (AP) — 93 active rows (AP-25 retired 2026-07-30 at Campaign P Slice P1 — the vitae/enchantment-aware run/jump skill chain; AP-127 filed same slice for the two minor unmodeled bonus properties)
|
||||
## 3. Documented approximation (AP) — 92 active rows (AP-25 retired 2026-07-30 at Campaign P Slice P1 — the vitae/enchantment-aware run/jump skill chain; AP-127 filed same slice for the two minor unmodeled bonus properties; AP-7 retired 2026-07-30 at Campaign P Slice P2 — `calc_friction`'s threshold ported to retail's confirmed 0.25f; its still-open cos(10°)-vs-0.99999536f Sledding constant question moved to AD-55)
|
||||
|
||||
Wave-0 UI ledger repair (2026-07-10) retired stale AP-38, resolved the AP-84
|
||||
collision, restored overwritten paperdoll rows as AP-92/AP-93, and registered
|
||||
|
|
@ -127,7 +130,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| AP-3 | Step-down chain triggered only when contact is invalid OR steeper than walkable; retail's `transitional_insert` OK-path ALWAYS runs it | `src/AcDream.Core/Physics/TransitionTypes.cs:1197` | Conditional preserves the observed-to-matter cases (edge departure, steep cliff-slide) without running the chain every step (per pc:273191 agent reports) | Steps where retail runs step-down despite a valid walkable contact (bump maintenance, edge-slide arming) are skipped — float-off or missed edge slides in untested geometry | `transitional_insert` OK-path pc:273191 |
|
||||
| AP-4 | CliffSlide check moved BEFORE retail's Branch-1 (`!OnWalkable` → restore+OK) gate, compensating our L.2.3i FloorZ OnWalkable bookkeeping | `src/AcDream.Core/Physics/TransitionTypes.cs:1316` | Retail's order with our incomplete OnWalkable stops the player dead every frame on steep slopes ("stay on the roof"); reorder restores downhill drift | CliffSlide fires in states where retail's Branch 1 would restore-and-OK — body slides where retail holds, e.g. contact-plane-bearing steep geometry near edges | retail EdgeSlide dispatch order (transitional_insert step-down failure) |
|
||||
| AP-5 | Step-down skips Placement validation for the contact-maintenance call (`runPlacement=false`); ACE/retail run it unconditionally (kept for DoStepUp) | `src/AcDream.Core/Physics/TransitionTypes.cs:3393` | Residual wall-slide artifacts made Placement misfire, leaving players stuck near walls; the skip was the targeted L.2.3h fix | Step-down can settle into positions Placement would reject — slight wall embedding, or accepting a step-down through overlap geometry retail catches | `CTransition::step_down` pc:272952; ACE Transition.cs:731-741 |
|
||||
| AP-7 | `calc_friction` threshold 0.0 with retail's state gate missing; retail uses 0.25 gated by an undecoded state check | `src/AcDream.Core/Physics/PhysicsBody.cs:307` | Bumping the threshold without the gate hammered normal walking (3 → 0.16 m/s); as-read 0.0 kept; locomotion probably state-exempted in retail. Filed L.3c-followup | Friction engages under different conditions — post-landing slides, knockback decay, sledding speeds mismatch retail's deceleration | pc:276702-276705 (state gate + 0.25) |
|
||||
| ~~AP-7~~ | **RETIRED 2026-07-30 (Campaign P Slice P2) — the "state gate" was a BN decompiler artifact, not a locomotion exemption.** `calc_friction` now ports retail's confirmed 0.25f threshold (`if (angle >= 0.25f) return;`) unconditionally, no special-cased gate. The "state check at pc:276702" the old row cited is `PhysicsState.Sledding` (confirmed via ACE's `PhysicsObj.calc_friction`, references/ACE/Source/ACE.Server/Physics/PhysicsObj.cs:2120-2141, and `SLEDDING_PS=0x800000` in acclient.h:2838) — it gates the 1.5625/6.25/near-flat friction-value OVERRIDE, not the threshold return itself; acdream had no live Sledding setter then or now (see #166 research, docs/research/2026-07-30-response-layer-edge-family-pseudocode.md §3), so the branch was simply unreachable dead code, not an exemption for ordinary walking. The reverted 2026-04-30 L.3c attempt (naive 0.0→0.25 bump, forward locomotion 3→0.16 m/s in `PlayerMovementControllerTests`) does not reproduce on the production graphical local-player path post-R6: `PlayerMovementController` zeroes `Velocity.X/Y` to exactly zero every tick before `calc_friction` runs whenever animation root motion drives the walk, so friction has no horizontal velocity left to hammer (pinned at the PhysicsBody level by `GroundedRootMotion_FrictionThreshold_DoesNotHammerLocomotionTests`). The headless/`get_state_velocity` movement-controller path and remote/NPC movers still feed real velocity into this function and remain the ones to watch if a similar regression resurfaces there. | `src/AcDream.Core/Physics/PhysicsBody.cs` (`calc_friction`); `tests/AcDream.Core.Tests/Physics/PhysicsBodyTests.cs` (AP-7 test block) | — | — | `CPhysicsObj::calc_friction` pc:276694-276822 (0050ee70); ACE `PhysicsObj.calc_friction` PhysicsObj.cs:2120-2141; `docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §1 |
|
||||
| AP-10 | Dry-corner water depth: retail's 0.1 m allowed sink-in collapsed to 0 | `src/AcDream.Core/Physics/TerrainSurface.cs:481` | The 0.1 offset destabilizes the feet-exactly-on-plane contact-touch check (dist > EPSILON → SetContactPlane never fires → float/fall); retail's ~10 cm sink-in is visually indistinguishable | Masks a contact-touch epsilon fragility — other water-depth values exercising the same instability could oscillate shoreline walkable validation; retail's wet/dry corner sink-in visual absent | `ObjCell.get_water_depth` / `calc_water_depth` (via ACE port) |
|
||||
| AP-11 | Hand-authored 4-keyframe fallback sky set (sunrise/noon/sunset, fog ~80–350 m) when the Region dat isn't loaded yet | `src/AcDream.Core/World/SkyState.cs:167` | A renderable sky is needed during boot before the Region dat parses; safety net on region-load failure | Any window where the fallback is active shows sky/fog lighting only roughly resembling retail's dat-driven values | SkyTimeOfDay keyframes, Region dat 0x13000000 |
|
||||
| AP-12 | Enchantment family-stacking tiebreak by largest SpellId; retail picks highest Generation, tie-broken by latest cast | `src/AcDream.Core/Spells/EnchantmentMath.cs:89` | `ActiveEnchantmentRecord` doesn't carry Generation; SpellId correlates with generation level in practice | Where spell ids don't track power within a family (or same-generation re-cast), the wrong buff wins — vital-max / stat values diverge from retail | `CEnchantmentRegistry::EnchantAttribute` 0x00594570 (pc:416110) |
|
||||
|
|
@ -231,11 +234,11 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| AP-126 | One monotonic Stopwatch-backed clock (`TransportClock`) drives every transport gate (2.0 s ack, 0.6 s NAK, 0.333 s handshake retry, 0.5 s interval, 5 s assembler sweep); retail splits gates between `Timer::cur_time` (server-adjusted) and `Timer::local_time`. | `src/AcDream.Core.Net/Transport/TransportClock.cs` | The cur/local split only matters for gates that must track server clock adjustments; none of the ported gates semantically depend on server time — they are local cadences. A single injectable source also gives the virtual-clock test seam every conformance suite relies on. | A future port of a genuinely server-clock-relative gate could silently use the wrong clock if it reuses TransportClock without checking this row. | `SharedNet::EnqueuePak @ 0x00543B10` (cur_time); `ClientNet::ProcessConnection @ 0x00545450` (local_time for the 140 s check) |
|
||||
| AP-127 | Campaign P Slice P1's run/jump base-skill chain omits two minor retail additive/multiplier terms feeding `CACQualities::InqRunRate`/`InqJumpVelocity` BEFORE `EnchantSkill` runs (property `0x146` "> 0 → +5" bonus; property `0x158` "specialized skill" doubling of a PP-derived term), and reads the raw wire current-stamina value for the zero-skill gate rather than the retail-adjusted local copy (`EnchantAttribute2nd(ATTR2ND_STAMINA)` can apply a Stamina-buff to that check's own copy without changing the displayed vital) | `src/AcDream.Runtime/Gameplay/RuntimeCharacterState.cs` (`ApplySkillEnchantments`); `src/AcDream.Core/Physics/PlayerWeenie.cs` (`InqRunRate`/`InqJumpVelocity` stamina==0 gate) | Bounded per the P1 plan's explicit scope ("port only what the run/jump query path needs... not a general effective-skill engine"); both terms are rare/small relative to the dominant formulaBonus+init+ranks+vitae chain, which IS fully ported | A character with the specific rare property set (0x146/0x158) or an active Stamina-buff at exactly 0 raw stamina predicts a slightly different run/jump skill than retail; low practical impact | `CACQualities::InqRunRate` 0x00592800 pc 413824 (0x146/0x158 reads); `CEnchantmentRegistry::EnchantAttribute2nd` 0x00594670 pc 416169 |
|
||||
|
||||
## 4. Temporary stopgap (TS) — 42 active rows (TS-5 retired 2026-07-30 at Campaign P Slice P1 — real burden-gated CanJump + real JumpStaminaCost, both decomp-verbatim; TS-57..TS-61 filed 2026-07-29 during Campaign N — no outbound RejectRetransmit; TS-27 narrowed same slice to the inbound direction) + TS-37 historical note (TS-20 retired 2026-07-16 — the later named-retail audit disproved the proposed DrawingBSP polygon filter; TS-37 is a retired-row historical note, not an active count; TS-39 retired R5-V3 — sticky seams bound to the ported PositionManager/StickyManager, radii threaded; TS-45 retired 2026-07-07 — hand-rolled `SphereCollision` replaced by the faithful CSphere family port, fixing the player-vs-monster crowd wedge; TS-3 retired 2026-07-07 — `frames_stationary_fall` accounting ported in the #182 verbatim UpdateObjectInternal rebuild, fixing the airborne falling-animation wedge; TS-41 retired 2026-07-07 — SERVERVEL synth-velocity remote body-drive replaced by the retail interp catch-up + unconditional MovementManager::UseTime, the remote-creature de-overlap #184; TS-42 retired 2026-07-19 — semantic animation completion now precedes the ordered Target/Movement/PartArray/Position tail; TS-44 narrowed again 2026-07-19 — complete orientation joined interpolation, only during-stick enqueue suppression remains)
|
||||
## 4. Temporary stopgap (TS) — 41 active rows (TS-5 retired 2026-07-30 at Campaign P Slice P1 — real burden-gated CanJump + real JumpStaminaCost, both decomp-verbatim; TS-1 retired 2026-07-30 at Campaign P Slice P2 — the row was stale; the EdgeSlide → PrecipiceSlide/CliffSlide chain is already a real, tested port, see the AD-53/AD-54 rows for the two compensating branches it left registered; TS-57..TS-61 filed 2026-07-29 during Campaign N — no outbound RejectRetransmit; TS-27 narrowed same slice to the inbound direction) + TS-37 historical note (TS-20 retired 2026-07-16 — the later named-retail audit disproved the proposed DrawingBSP polygon filter; TS-37 is a retired-row historical note, not an active count; TS-39 retired R5-V3 — sticky seams bound to the ported PositionManager/StickyManager, radii threaded; TS-45 retired 2026-07-07 — hand-rolled `SphereCollision` replaced by the faithful CSphere family port, fixing the player-vs-monster crowd wedge; TS-3 retired 2026-07-07 — `frames_stationary_fall` accounting ported in the #182 verbatim UpdateObjectInternal rebuild, fixing the airborne falling-animation wedge; TS-41 retired 2026-07-07 — SERVERVEL synth-velocity remote body-drive replaced by the retail interp catch-up + unconditional MovementManager::UseTime, the remote-creature de-overlap #184; TS-42 retired 2026-07-19 — semantic animation completion now precedes the ordered Target/Movement/PartArray/Position tail; TS-44 narrowed again 2026-07-19 — complete orientation joined interpolation, only during-stick enqueue suppression remains)
|
||||
|
||||
| # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle |
|
||||
|---|---|---|---|---|---|
|
||||
| TS-1 | PrecipiceSlide context missing — conservative stop-at-edge instead of retail's EdgeSlide → PrecipiceSlide / CliffSlide | `src/AcDream.Core/Physics/TransitionTypes.cs:1254` | Awaiting the next L.2c slice; a diagnostic records which ingredient (precipice context / steep plane / EdgeSlide flag) is missing | Player stops dead at precipice edges where retail slides along/over — visible mismatch at cliff and roof edges | retail EdgeSlide → PrecipiceSlide chain |
|
||||
| ~~TS-1~~ | **RETIRED 2026-07-30 (Campaign P Slice P2) — the row was stale, not the code.** The cited `:1254` line is unrelated stepping-loop code; the file moved substantially since the row was written. Retail's `EdgeSlide → PrecipiceSlide / CliffSlide` chain is already a real, tested port: `SpherePath.PrecipiceSlide` (`TransitionTypes.cs:943-970`, retail `SPHEREPATH::precipice_slide` pc:274316), `Transition.CliffSlide` (`:2080-2164`, retail `CTransition::cliff_slide` pc:272397, return-value mapping verified against `acclient.h:6100-6108`), and `Transition.EdgeSlideAfterStepDownFailed` (`:1907-2078`, mirrors `CTransition::edge_slide` pc:273001-273090). The one real gap (back-probe fallback skipping retail's `walkable_check_pos`/`localspace_sphere` recache, pc:274318-274326) needed no code change: acdream's `WalkableVertices`/`GlobalSphere` are populated in unified world space at assignment time (`SetWalkable`/`SetWalkableTransformed`, `SetCheckPos`/`RestoreCheckPos`), so both operands `BSPQuery.FindCrossedEdge` compares are already commensurable — retail's per-cell local-frame reprojection is a no-op correction here. Documented in-code at the back-probe site and pinned by `EdgeSlideBackProbePrecipiceSlideTests`. The chain's two acdream-only compensating branches (CliffSlide's three-source reference-normal fallback; the walkable-steepness reroute to CliffSlide before PrecipiceSlide) are real, non-retail additions — filed as AD-53 / AD-54 rather than folded into this row. | `src/AcDream.Core/Physics/TransitionTypes.cs` (`SpherePath.PrecipiceSlide`, `Transition.CliffSlide`, `Transition.EdgeSlideAfterStepDownFailed`); `tests/AcDream.Core.Tests/Physics/EdgeSlideBackProbePrecipiceSlideTests.cs` | — | — | `SPHEREPATH::precipice_slide` pc:274316 (0050cc80); `CTransition::cliff_slide` pc:272397 (0050a6d0); `CTransition::edge_slide` pc:273001-273090 (0050b3d0); `SPHEREPATH::get_walkable_pos`/`cache_localspace_sphere`/`set_walkable_check_pos` pc:274318-274326 (0050a8f0/0050c9d0/00509ce0); `docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §2, §6 Step 1 |
|
||||
| TS-4 | Path-6 steep-poly slide-tangent shortcut: airborne hits on >FloorZ polys skip retail's SetCollide → Path-4 → ContactPlane landing chain, returning Slid in place. **Includes a `SetSlidingNormal` write at both sites** — retail's BSP layer never writes `collision_info.sliding_normal` (only `validate_transition` 0x0050ac21 does; the #137 mechanism-2 class), so on transition success the steep-face normal persists to the body and seeds the next frame | `src/AcDream.Core/Physics/BSPQuery.cs` (Path-6 steep branches, `worldNormal.Z < FloorZ`) | Deliberate deviation: our faithful port DID wedge (missing step_up_slide / cliff_slide details on grounded-steep); validated against the 2026-04-30 retail cdb trace (retail body didn't wedge). Filed L.5+ for retail-strict | Airborne steep contact never commits Contact / lands as retail — roof-bounce trajectories, landing events, grounded-steep transitions diverge; a persisted steep-face normal can absorb an exactly-anti-parallel next-frame push (#137 wedge class) until an oblique input clears it | `BSPTREE::find_collisions` SetCollide pc:323783-323821 |
|
||||
| TS-6 | Weather particle emission suppressed — all weathery DayGroups map to Overcast (correct fog/cloud tone, no precipitation); retail's camera-attached weather subsystem not yet located in the decomp | `src/AcDream.Core/World/WeatherState.cs:200` | Decomp research verified the sky loop never reads `DefaultPesObjectId`; an earlier name-based rain spawn regressed (rained where retail didn't, 2026-04-23) — inventing a name→rain path is forbidden until the real subsystem is found | Rainy/snowy/stormy days never show retail's precipitation effects (permanent missing visuals until the subsystem is found and ported) | FUN_00508010 / FUN_0051bed0→FUN_0051bfb0 (negative findings) |
|
||||
| TS-7 | SkyObject `weather_enabled` gate not honored — weather-flagged sky objects (bit 0x04) always instantiate | `src/AcDream.Core/World/SkyDescLoader.cs:50` | No weather_enabled toggle exists yet; IsWeather flag parsed + documented as the gate to wire | Weather-only sky meshes (rain cylinders) appear where retail-with-weather-off suppresses them | `GameSky::MakeObject` 0x00506ee0, guard at decomp:268630 |
|
||||
|
|
@ -309,12 +312,11 @@ WITH that phase, not before.
|
|||
1. **TS-27 — INBOUND retransmit handling** — the outbound sent-packet cache + resend landed with Campaign N Slice N1 (2026-07-29, class-doc gap list fixed same commit); the inbound sequence-aligned ISAAC + client NAK emission (N2/N4) remain the hard blocker for non-loopback play — one lost S2C packet still deafens the session permanently.
|
||||
2. **TS-4 — Path-6 steep slide-tangent shortcut** — landing/contact state diverges on every airborne-steep hit; the L.5+ retail-strict followup is already filed with the missing-ingredient analysis.
|
||||
3. **UN-1 — CheckOtherCells iteration order** — behavior-bearing halt order with a log-cosmetics justification; trivial to fix (iterate CELLARRAY build order, sort only in probe output).
|
||||
4. **TS-1 — PrecipiceSlide stop-at-edge** — visible movement mismatch at every cliff/roof edge; diagnostic already records which ingredient is missing.
|
||||
5. **UN-6 — 200 ms ConnectResponse sleep** — unexplained constant on every login with an intermittent-failure shape; either find the ACE race and cite it, or replace with an acknowledged-ready check.
|
||||
6. **UN-4 — GfxObj sides/negative-surface logic** — diagnose against the retail-cited CellStruct interpretation on a known double-sided GfxObj; promote to AP with a citation or align it.
|
||||
7. **TS-8 — MagicUpdateEnchantment StatMod parse (#7/#12)** — vitals wrong for the whole session after any buff; parser shape is known from holtburger.
|
||||
8. **TS-55 — AdminEnvirons fog/radar presentation** — exact retail mechanism is known; port the authored ambient/fog fields, radar blanking, Clear, and `0x270F` together.
|
||||
9. **TS-19 — Legacy ChaseCamera deletion** — already marked "pending the follow-up deletion commit"; its continued existence can mask or manufacture flap symptoms during debugging.
|
||||
4. **UN-6 — 200 ms ConnectResponse sleep** — unexplained constant on every login with an intermittent-failure shape; either find the ACE race and cite it, or replace with an acknowledged-ready check.
|
||||
5. **UN-4 — GfxObj sides/negative-surface logic** — diagnose against the retail-cited CellStruct interpretation on a known double-sided GfxObj; promote to AP with a citation or align it.
|
||||
6. **TS-8 — MagicUpdateEnchantment StatMod parse (#7/#12)** — vitals wrong for the whole session after any buff; parser shape is known from holtburger.
|
||||
7. **TS-55 — AdminEnvirons fog/radar presentation** — exact retail mechanism is known; port the authored ambient/fog fields, radar blanking, Clear, and `0x270F` together.
|
||||
8. **TS-19 — Legacy ChaseCamera deletion** — already marked "pending the follow-up deletion commit"; its continued existence can mask or manufacture flap symptoms during debugging.
|
||||
|
||||
**Phase-gated (do WITH the phase, flagged here so they aren't forgotten):**
|
||||
M2 combat must land TS-23 (PK bits), TS-25
|
||||
|
|
|
|||
|
|
@ -84,6 +84,15 @@ character state continuously.
|
|||
|
||||
### P2 — Response-layer edge family — retires TS-1, TS-4, AP-7; closes #166, #116
|
||||
|
||||
**Status (2026-07-30):** TS-1 and AP-7 retired; #166 reattributed in
|
||||
ISSUES.md (no new code — see the research doc §3). **TS-4 is deferred, not
|
||||
retired** — the fixture-first removal attempt this doc's §6 Step 3 requires
|
||||
reproduced the historical 2026-04-30 L.4 wedge (a dat-free capture,
|
||||
`Ts4SteepRoofWedgeCaptureTests`); the shortcut stays in `BSPQuery.cs`. Root
|
||||
cause and the concrete next research step are recorded in
|
||||
`docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §7 item
|
||||
6. #116 is untouched (oracle-first, own session).
|
||||
|
||||
The collision *response* layer (what happens after a hit): ground
|
||||
friction, cliff edges, downhill landings, near-perpendicular wall
|
||||
slides. One oracle-driven pass; the physics digest's DO-NOT-RETRY table
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
# P2 — Collision response-layer edge family: port-ready pseudocode
|
||||
|
||||
**Status: RESEARCH PASS COMPLETE (2026-07-30).** Research-only doc for
|
||||
Campaign P Slice P2 (`docs/plans/2026-07-29-physics-parity-campaign.md`
|
||||
§P2). Retires TS-1, TS-4, AP-7; closes #166, #116. No source changes made
|
||||
by this doc; it is the pre-port research artifact for a future
|
||||
implementation session. **Headline findings that change the plan's
|
||||
assumptions:** TS-1 is already substantially ported (the register row
|
||||
and plan phrasing are stale — see §2); #166 is very likely NOT about a
|
||||
literal `PhysicsState.Sledding` auto-toggle at all (see §3); AP-7's L.3c
|
||||
regression may no longer reproduce under the post-R6 animation-root-motion
|
||||
architecture for the graphical path, but likely still reproduces for the
|
||||
headless/test path (see §1); TS-4's shortcut removal is coupled to TS-1's
|
||||
completion and must not be done independently (see §6 Step 3-4); #116
|
||||
remains a genuine oracle-first research item needing live cdb/Ghidra, not
|
||||
an implementation item (see §5). Read §6 (port order) before starting
|
||||
implementation — the safe sequence is not the plan's listed item order.
|
||||
**Status: RESEARCH PASS COMPLETE (2026-07-30); IMPLEMENTATION PASS PARTIAL
|
||||
(2026-07-30).** Originally a research-only doc for Campaign P Slice P2
|
||||
(`docs/plans/2026-07-29-physics-parity-campaign.md` §P2); a same-day
|
||||
implementation session landed TS-1's retirement and AP-7's fix, attempted
|
||||
TS-4 per this doc's own §6 Step 3 fixture-first order, reproduced the
|
||||
historical wedge, and stopped — see §7 item 6 for the full capture and
|
||||
root-cause diagnosis. TS-4 is NOT retired; its shortcut stays in place.
|
||||
#166 got a reattribution note in ISSUES.md rather than new code (per §3).
|
||||
#116 remains untouched (oracle-first, out of implementation scope).
|
||||
**Headline findings that change the plan's assumptions:** TS-1 was already
|
||||
substantially ported (the register row and plan phrasing were stale — see
|
||||
§2); the one real gap needed no code change (acdream's unified world-space
|
||||
`SpherePath` design makes retail's per-cell recache a no-op correction here
|
||||
— see §2 and the TS-1 register row's retirement text); #166 is very likely
|
||||
NOT about a literal `PhysicsState.Sledding` auto-toggle at all (see §3);
|
||||
AP-7's L.3c regression does not reproduce on the production graphical
|
||||
root-motion path post-R6, and now ports retail's confirmed 0.25f threshold
|
||||
(see §1); TS-4's shortcut removal is coupled to TS-1's completion and
|
||||
reproduces a wedge even after TS-1 lands — see the §7 item 6 update for the
|
||||
precise mechanism (Phase 3 of `TransitionalInsert` is structurally
|
||||
unreachable from Path 6's unconditional `SetCollide`, which returns
|
||||
`Adjusted` without repositioning the sphere) and what a future attempt
|
||||
needs to check first; #116 remains a genuine oracle-first research item
|
||||
needing live cdb/Ghidra, not an implementation item (see §5). Read §6 (port
|
||||
order) before starting further implementation on this family.
|
||||
|
||||
Every claim below is tagged **FACT** (grep/read-verified against the
|
||||
named-retail decomp, the register, ISSUES.md, or current acdream source
|
||||
|
|
@ -959,13 +969,81 @@ it blocks.
|
|||
walkable-steepness reroute as real compensating adaptations) or
|
||||
whether they're unnecessary inventions. Needs a fresh, focused
|
||||
named-decomp read (not Ghidra/cdb-gated — just not done this pass).
|
||||
6. **[TS-4 / Step 3]** Whether TS-1's Step 1 fix alone is sufficient to
|
||||
let TS-4's shortcut be safely removed, or whether the 2026-04-30 L.4
|
||||
session's wedge had additional causes not yet identified. Answerable
|
||||
only by the capture in §6 Step 3 — not a Ghidra/cdb question, but
|
||||
listed here because it's the single highest-risk unresolved item in
|
||||
this document (deleting a load-bearing shortcut based on an unproven
|
||||
assumption).
|
||||
6. **[TS-4 / Step 3] ANSWERED 2026-07-30 (implementation session) — NOT
|
||||
sufficient; the wedge reproduces, and its mechanism is now precisely
|
||||
characterized.** TS-1's gap #1 fix (this document's §2, landed the same
|
||||
session) does NOT unblock TS-4. A dat-free multi-frame capture
|
||||
(`tests/AcDream.Core.Tests/Physics/Ts4SteepRoofWedgeCaptureTests.cs`,
|
||||
using `BSPStepUpFixtures.SlopedUnwalkable`'s 63.4° slope,
|
||||
`PhysicsEngine.ResolveWithTransition` replayed at 30 Hz with gravity
|
||||
integrated between resolves — the same replay idiom as
|
||||
`Issue185OutdoorStairsSeamReplayTests`) reproduces the EXACT historical
|
||||
shape with the Path-6 steep shortcut temporarily removed (both
|
||||
`BSPQuery.cs` sphere0/sphere1 branches): the body falls cleanly (30
|
||||
ticks, position advancing every tick), lands at
|
||||
`(0.500, 0.000, 1.247)` at tick 17 with `InContact=true, OnWalkable=false`
|
||||
(confirming the steep polygon WAS accepted via the permissive
|
||||
`CTransition::check_walkable(0.0871556997f)` / `LandingZ` gate exactly
|
||||
as predicted from the retail source read below), then **freezes at that
|
||||
exact position for the remaining 16+ ticks with zero movement** — the
|
||||
test's own wedge-detection threshold (>15 consecutive frozen ticks =
|
||||
>0.5s) trips at tick 33. With the shortcut restored, the same test is
|
||||
green (the shortcut's explicit `AddOffsetToCheckPos` keeps the body
|
||||
moving every tick by construction). **The shortcut stays; TS-4 is NOT
|
||||
retired this session.**
|
||||
|
||||
**Root-cause diagnosis (`ACDREAM_DUMP_EDGE_SLIDE=1` capture against the
|
||||
scratch shortcut-removed build):** the freeze is NOT inside
|
||||
`EdgeSlideAfterStepDownFailed`/`CliffSlide` at all — none of that
|
||||
dispatch's diagnostic lines (`DumpEdgeSlideBranch`,
|
||||
`DumpStepDownBranchGate`, the `[steep-roof] PHASE3-RESET*` lines) fire
|
||||
even once during the frozen ticks. Every frozen tick instead logs only
|
||||
`edge-slide: phase2 attempt=0 env=OK obj=Adjusted` followed by
|
||||
`attempt=1 env=OK obj=Adjusted` — i.e. `TransitionalInsert`'s Phase 2
|
||||
object-collision check (`FindObjCollisionsInCell`, `TransitionTypes.cs:1572`)
|
||||
returns `Adjusted` on BOTH retry attempts, and per
|
||||
`TransitionTypes.cs:1591-1596` an `Adjusted` `objState` unconditionally
|
||||
`continue`s (retries Phase 1/2 from the top) rather than falling through
|
||||
toward Phase 2.5/Phase 3. **Phase 3 — the `if (sp.Collide) { ... }`
|
||||
block at `TransitionTypes.cs:1625` that contains the `DoCheckWalkable`
|
||||
Placement re-test AND (on walkable failure) the reset-with-conditional-
|
||||
`kill_velocity` path — is gated on Phase 1 AND Phase 2 BOTH returning
|
||||
`OK` simultaneously (`TransitionTypes.cs:1568-1621`). Path 6's own
|
||||
unconditional `SetCollide` (the retail-faithful code path TS-4 would
|
||||
restore) returns `Adjusted`, not `OK` (matching retail's own
|
||||
`return 3; // ADJUSTED_TS` at pc:323783, quoted in §4 above) AND does
|
||||
NOT itself reposition the sphere** — unlike the interim shortcut, which
|
||||
explicitly calls `AddOffsetToCheckPos` to push the sphere off the face
|
||||
every time it fires. With no repositioning, the SAME steep polygon at
|
||||
the SAME distance re-triggers Path 6 on the immediate retry, which
|
||||
again returns `Adjusted`, forever — an Adjusted↔retry oscillation at a
|
||||
fixed point that the loop's 2-attempt-per-resolve budget silently
|
||||
absorbs (returning the frozen position as though the resolve
|
||||
succeeded), repeating identically on every subsequent tick's fresh
|
||||
resolve call. **Phase 3 (and therefore `DoCheckWalkable`,
|
||||
`CliffSlide`, and the TS-1 chain entirely) is structurally unreachable
|
||||
from this state** — TS-1's completeness is moot here because the code
|
||||
path that would call into it never runs.
|
||||
|
||||
**What this means for a future attempt:** the missing piece is NOT
|
||||
(only) in `EdgeSlideAfterStepDownFailed`/`CliffSlide` — it is in how
|
||||
`TransitionalInsert`'s Phase 1/2/2.5/3 dispatch (`TransitionTypes.cs
|
||||
:1568-1710`) distinguishes "Phase 2 found a NEW collision, retry from
|
||||
the top" from "Phase 2 registered a touch via `sp.Collide` and should
|
||||
fall through toward Phase 3 regardless of its own `Adjusted` return."
|
||||
Retail's own `transitional_insert` (pc:273137, `0050b6f0`) has NOT been
|
||||
read closely enough this pass to say definitively whether it treats a
|
||||
Path-6-sourced `ADJUSTED_TS` differently from an ordinary Adjusted
|
||||
result before this session's `continue`-on-Adjusted structure was
|
||||
written — that fresh, close read (specifically: does retail's loop
|
||||
check `sphere_path.collide` on EVERY iteration regardless of the
|
||||
latest Phase-2 return value, or only when Phase 2 returns OK?) is the
|
||||
concrete next step, not a second speculative code change. Do not retry
|
||||
the plain shortcut-deletion variant without that read; do not invent a
|
||||
third variant (e.g. teaching Path 6 to reposition the sphere itself)
|
||||
without confirming that's what retail actually does — that would be
|
||||
exactly the kind of guess CLAUDE.md's workflow forbids twice in a row
|
||||
on the same item.
|
||||
7. **[#116 shape-1]** Where exactly, in the BSP/environment hit-test
|
||||
dispatch (candidate: `BSPTREE::find_collisions`'s `PathClipped`/
|
||||
`collide_with_pt` arm, pseudo-C ~323700-323830, sibling to the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue