docs(plan): pin live-entity integration slice
This commit is contained in:
parent
ed9dc36643
commit
db5a11707d
3 changed files with 368 additions and 2 deletions
|
|
@ -499,6 +499,10 @@ fields / 190 methods. Slice 4 is next.
|
||||||
|
|
||||||
#### Slice 4 — extract live-entity App integration
|
#### Slice 4 — extract live-entity App integration
|
||||||
|
|
||||||
|
Detailed execution ledger:
|
||||||
|
[`docs/plans/2026-07-21-gamewindow-slice-4-live-entity-integration.md`](../plans/2026-07-21-gamewindow-slice-4-live-entity-integration.md)
|
||||||
|
(active).
|
||||||
|
|
||||||
This is two owners, not one replacement god object:
|
This is two owners, not one replacement god object:
|
||||||
|
|
||||||
- `LiveEntityHydrationController` owns CreateObject/ObjDesc/Parent/Pickup/Delete
|
- `LiveEntityHydrationController` owns CreateObject/ObjDesc/Parent/Pickup/Delete
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ accepted-divergence entries (#96, #49, #50).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Documented approximation (AP) — 85 active rows
|
## 3. Documented approximation (AP) — 87 active rows
|
||||||
|
|
||||||
Wave-0 UI ledger repair (2026-07-10) retired stale AP-38, resolved the AP-84
|
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
|
collision, restored overwritten paperdoll rows as AP-92/AP-93, and registered
|
||||||
|
|
@ -180,7 +180,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
||||||
| AP-75 | **NARROWED 2026-07-19 — adapter-boundary `adjust_motion` only.** `SetCycle` remaps TurnLeft/SideStepLeft/WalkBackward to their mirror command with negated speed before dispatch. Retail performs that normalization in `CMotionInterp`; GameWindow's local-player adapter can still pass raw ids directly | `src/AcDream.Core/Physics/AnimationSequencer.cs` (`SetCycle` head remap) | Preserves raw local callers until every caller enters through `MotionInterpreter`; literal DAT velocity and omega now flow through CSequence's complete Frame | A future caller that already normalizes a raw left/back command but still passes the original id can be adjusted twice | `CMotionInterp::adjust_motion` @305343; retire with the remaining local caller unification |
|
| AP-75 | **NARROWED 2026-07-19 — adapter-boundary `adjust_motion` only.** `SetCycle` remaps TurnLeft/SideStepLeft/WalkBackward to their mirror command with negated speed before dispatch. Retail performs that normalization in `CMotionInterp`; GameWindow's local-player adapter can still pass raw ids directly | `src/AcDream.Core/Physics/AnimationSequencer.cs` (`SetCycle` head remap) | Preserves raw local callers until every caller enters through `MotionInterpreter`; literal DAT velocity and omega now flow through CSequence's complete Frame | A future caller that already normalizes a raw left/back command but still passes the original id can be adjusted twice | `CMotionInterp::adjust_motion` @305343; retire with the remaining local caller unification |
|
||||||
| AP-77 | **NARROWED 2026-07-19 — animation-less/headless movement fallback only.** When `MotionInterpreter.DefaultSink` or the local PartArray callback is absent, acdream writes grounded command-derived body velocity and applies the DAT-pinned Humanoid `TurnRight` rate (1.5 radians/second) directly to the body Frame. Production animated players/remotes bind `MotionTableDispatchSink` plus CSequence and instead consume the complete DAT-authored root Frame; that path preserves airborne orientation while suppressing only origin exactly like retail | `src/AcDream.Core/Physics/MotionInterpreter.cs` (`ApplyCurrentMovementInterpreted`); `src/AcDream.App/Input/PlayerMovementController.cs` (no-PartArray object-quantum fallback) | Keeps isolated/headless physics tests and a deliberately animation-less entity controllable without fabricating a PartArray | A future production entity missing its animation binding uses Humanoid-only yaw/velocity, can foot-slide, and can rotate through an airborne quantum differently from a real creature's DAT Frame | `CMotionInterp::apply_interpreted_movement` 0x00528600; `CPhysicsObj::UpdatePositionInternal` 0x00512C30; retire when animation-less production objects have an explicit motion owner |
|
| AP-77 | **NARROWED 2026-07-19 — animation-less/headless movement fallback only.** When `MotionInterpreter.DefaultSink` or the local PartArray callback is absent, acdream writes grounded command-derived body velocity and applies the DAT-pinned Humanoid `TurnRight` rate (1.5 radians/second) directly to the body Frame. Production animated players/remotes bind `MotionTableDispatchSink` plus CSequence and instead consume the complete DAT-authored root Frame; that path preserves airborne orientation while suppressing only origin exactly like retail | `src/AcDream.Core/Physics/MotionInterpreter.cs` (`ApplyCurrentMovementInterpreted`); `src/AcDream.App/Input/PlayerMovementController.cs` (no-PartArray object-quantum fallback) | Keeps isolated/headless physics tests and a deliberately animation-less entity controllable without fabricating a PartArray | A future production entity missing its animation binding uses Humanoid-only yaw/velocity, can foot-slide, and can rotate through an airborne quantum differently from a real creature's DAT Frame | `CMotionInterp::apply_interpreted_movement` 0x00528600; `CPhysicsObj::UpdatePositionInternal` 0x00512C30; retire when animation-less production objects have an explicit motion owner |
|
||||||
| AP-80 | **PlanFromVelocity survives for velocity-only NPC cycles** (M16): UpdatePosition-derived speed picks Ready/Walk/Run cycles for server-controlled creatures whose UMs never arrive (scripted-path NPCs); retail derives every cycle from motion messages through the motion tables. The adaptation is now structurally limited to replacing Ready/Walk/Run-family states, so authoritative actions/substates (especially Dead) always win. | `src/AcDream.Core/Physics/ServerControlledLocomotion.cs` (`PlanFromVelocity`, `CanApplyVelocityCycle`); consumer `GameWindow.ApplyServerControlledVelocityCycle` | Some ACE entities move by position updates alone — without this, they slide in T-pose; constants (StopSpeed 0.2, RunThreshold 1.25) tuned against live ACE traffic | Cycle-pick thresholds are acdream inventions — a creature intended to walk fast may show run legs near the threshold | retire in R6 (root motion + full per-tick order) |
|
| AP-80 | **PlanFromVelocity survives for velocity-only NPC cycles** (M16): UpdatePosition-derived speed picks Ready/Walk/Run cycles for server-controlled creatures whose UMs never arrive (scripted-path NPCs); retail derives every cycle from motion messages through the motion tables. The adaptation is now structurally limited to replacing Ready/Walk/Run-family states, so authoritative actions/substates (especially Dead) always win. | `src/AcDream.Core/Physics/ServerControlledLocomotion.cs` (`PlanFromVelocity`, `CanApplyVelocityCycle`); consumer `GameWindow.ApplyServerControlledVelocityCycle` | Some ACE entities move by position updates alone — without this, they slide in T-pose; constants (StopSpeed 0.2, RunThreshold 1.25) tuned against live ACE traffic | Cycle-pick thresholds are acdream inventions — a creature intended to walk fast may show run legs near the threshold | retire in R6 (root motion + full per-tick order) |
|
||||||
| AP-81 | **Remote-DR gravity toggled via the Gravity STATE bit**: the jump handler sets `Body.State \|= Gravity` at VectorUpdate and both landing blocks clear it after `HitGround()`; retail keeps GRAVITY set for the object's whole life and gates gravity ACCELERATION on the Contact transient (`calc_acceleration`) (pre-existing K-fix9/K-fix15 mechanism, row added during #161 — which also fixed the ordering so `Motion.HitGround()`'s verbatim `state&0x400` gate runs BEFORE the clear) | `src/AcDream.App/Rendering/GameWindow.cs` (VectorUpdate jump handler + the two landing blocks) | The DR tick integrates gravity only for airborne remotes; the flag dance delivers exactly that without porting the full contact-gated `calc_acceleration` chain; the #161 ordering fix keeps the retail HitGround contract satisfied | Any NEW call into `Motion.HitGround`/`LeaveGround` placed after the clear silently no-ops on the gravity gate (the #161 leg-2 class); grounded remotes carry a non-retail state word (probes comparing state bits vs retail mislead) | `CPhysicsObj::calc_acceleration` (contact-gated); `set_on_walkable` 0x00511310; retire in R6 (contact-gated accel + persistent GRAVITY) |
|
| AP-81 | **Remote-DR VectorUpdate adds airborne/contact state beyond retail and toggles gravity via the Gravity STATE bit**: the handler writes velocity/omega, then may set Airborne, set `Body.State \|= Gravity`, clear contact, and call `LeaveGround`; both landing blocks clear Gravity after `HitGround()`. Retail `DoVectorUpdate` only writes velocity and omega, keeps GRAVITY set for the object's whole life, and gates gravity ACCELERATION on the Contact transient (`calc_acceleration`) (pre-existing K-fix9/K-fix15 mechanism, row added during #161 — which also fixed the ordering so `Motion.HitGround()`'s verbatim `state&0x400` gate runs BEFORE the clear). | `src/AcDream.App/Rendering/GameWindow.cs` (VectorUpdate jump handler + the two landing blocks), moving to `LiveEntityNetworkUpdateController` in Slice 4 | The extra branch makes an inbound nonzero vertical velocity start the current remote airborne integration even without the complete retail contact-gated acceleration chain; the flag dance delivers gravity only while airborne and the #161 ordering fix keeps the retail HitGround contract satisfied. Slice 4 isolates it rather than changing accepted remote motion during extraction. | A VectorUpdate sent while contact state should remain authoritative can make the remote leave ground earlier than retail; any new call into `Motion.HitGround`/`LeaveGround` placed after the clear silently no-ops on the gravity gate; grounded remotes carry a non-retail state word. | `SmartBox::DoVectorUpdate @ 0x004521C0`; `CPhysicsObj::calc_acceleration`; `set_on_walkable @ 0x00511310`; retire when the complete contact-gated acceleration path owns remote motion. |
|
||||||
| AP-82 | **StickyManager deep-overlap back-off sign pin**: when the stick-gap overlap exceeds one tick's step (`speed×quantum < \|dist\|`, `dist < 0`), acdream applies `delta = −(speed×quantum)` (rate-limited back-off); ACE's literal port keeps `+delta` there — a runaway that steers INTO the target with equilibrium at centers-coincident. The BN mush (0x00555554-0x00555597) is unreadable on exactly this compare; the pin is refuted-by-evidence against ACE-literal: #171 gate-3 probe showed 1661 deep-overlap ticks all steering inward (monsters converged to centerDist≈0 — "monster inside the player") while retail side-by-side on the same ACE shows separation. ACE servers essentially never reach the branch (quantum ≥1/30 → threshold ~1 m; render-rate quanta → ~0.13 m) | `src/AcDream.Core/Physics/Motion/StickyManager.cs` (`AdjustOffset` delta clamp; conformance `StickyManagerTests.AdjustOffset_DeepOverlap_BacksOff_RateLimited`) | Minimal interpretation consistent with the mush structure AND observed retail; identical to ACE-literal in every shallow/outside case | If retail's true deep-overlap behavior differs (e.g. no movement at all), our back-off rate diverges in that rare state; verify via cdb `StickyManager::adjust_offset` trace with a forced overlap when convenient | `StickyManager::adjust_offset` 0x00555430 (x87 mush); ACE StickyManager.cs:117-121 (the literal branch this pin overrides) |
|
| AP-82 | **StickyManager deep-overlap back-off sign pin**: when the stick-gap overlap exceeds one tick's step (`speed×quantum < \|dist\|`, `dist < 0`), acdream applies `delta = −(speed×quantum)` (rate-limited back-off); ACE's literal port keeps `+delta` there — a runaway that steers INTO the target with equilibrium at centers-coincident. The BN mush (0x00555554-0x00555597) is unreadable on exactly this compare; the pin is refuted-by-evidence against ACE-literal: #171 gate-3 probe showed 1661 deep-overlap ticks all steering inward (monsters converged to centerDist≈0 — "monster inside the player") while retail side-by-side on the same ACE shows separation. ACE servers essentially never reach the branch (quantum ≥1/30 → threshold ~1 m; render-rate quanta → ~0.13 m) | `src/AcDream.Core/Physics/Motion/StickyManager.cs` (`AdjustOffset` delta clamp; conformance `StickyManagerTests.AdjustOffset_DeepOverlap_BacksOff_RateLimited`) | Minimal interpretation consistent with the mush structure AND observed retail; identical to ACE-literal in every shallow/outside case | If retail's true deep-overlap behavior differs (e.g. no movement at all), our back-off rate diverges in that rare state; verify via cdb `StickyManager::adjust_offset` trace with a forced overlap when convenient | `StickyManager::adjust_offset` 0x00555430 (x87 mush); ACE StickyManager.cs:117-121 (the literal branch this pin overrides) |
|
||||||
| AP-85 | **Point-light pool = single 128-cap player-nearest list, optionally FILTERED by LAST FRAME's rendered visible-cell set, vs retail's dual pools (7 dynamic + 40 static, degrade-scaled) collected from a DBObj-load/flush-bounded resident registry** (A7.L1, 2026-07-09 — third revision, Town Network starvation fix #79/#93/#176/#177): retail's `CEnvCell::visible_cell_table` (`add_visible_cell` 0x0052de40) is populated ON DEMAND as cells are approached/seen (`DBObj::Get`-loads) and pruned by `flush_cells` — so a real dungeon's per-frame candidate set stays small (naturally proximity-bounded) even though the collection walk itself (`add_dynamic_lights` 0x0052d410) is "the whole resident table, not a re-flood." acdream's `_all` list instead registers at LANDBLOCK-granularity load/unload (a whole single-landblock dungeon streams as ONE unit), so for the Town Network (463 registered fixtures, one landblock) `_all` is effectively "everything ever loaded in this dungeon," not a proximity-bounded set — wide enough that the player-nearest-128 cap alone let a straight-line-closer-but-wall-disconnected corridor's fixtures out-rank the player's own room, starving it. Fix: `BuildPointLightSnapshot(playerWorldPos, visibleCells)` takes an optional candidacy FILTER — a light joins the pool iff `CellId==0` (cell-less, always in) or `visibleCells.Contains(CellId)` — narrowing candidates to the frame's actual visible cells BEFORE the existing dynamics-first player-nearest cap runs; `GameWindow` feeds LAST FRAME's already-rendered `RetailPViewFrameResult.DrawableCells` (one frame / ~16 ms latency, chosen specifically to avoid re-threading a mid-`DrawInside` callback — the exact mechanism, `c500912b`, that caused the #176 seam-floor flicker regression when it re-flooded an independent CAMERA-seeded set mid-frame). The distance-sort anchor stays the PLAYER (unchanged from the prior revision) — only candidacy narrows. Remaining deviation: this is a RENDER-visibility approximation of retail's true on-demand-load/flush RESIDENCY bound, with one frame of latency, not a port of the DBObj-load/flush mechanism itself; and the pool is still ONE 128-cap list vs retail's separate 7-dynamic/40-static degrade-scaled pools | `src/AcDream.Core/Lighting/LightManager.cs` (`BuildPointLightSnapshot`, `MaxGlobalLights`); `src/AcDream.App/Rendering/GameWindow.cs` (`_lightPoolVisibleCells`/`_lightPoolVisibleCellsValid`); pins `PointSnapshot_HubScaleLightCount_ObjectSelectionIsCameraInvariant`, `PointSnapshot_OverCap_DynamicsNeverEvictedByNearerStatics`, `PointSnapshot_ResidentCollection_CellTagDoesNotFilter`, `BuildPointLightSnapshot_VisibleCellScoping_RoomLightsSurviveOverEuclideanCloserInvisibleCell`, `BuildPointLightSnapshot_VisibleCellScoping_CellLessLightAlwaysIncluded` | The render already computes a visible-cell set every frame for drawing (single source of truth, no duplicate flood) — reusing it as a candidacy filter approximates retail's proximity-bounded residency without porting DBObj on-demand load/flush; one-frame latency is imperceptible at normal camera speeds and structurally differs from the reverted mechanism (no independent re-flood mid-frame) | On a portal crossing, the FIRST indoor frame after re-entry (or after any outdoor-only frame) is unscoped (fail-open) — one frame may show slightly wider pool composition than steady-state; a room with >7 resident dynamics still shows them all (retail trims to 7 player-nearest) — slightly purpler wedge than retail; adopt the dual pools + degrade caps + true DBObj-bounded residency in later A7-arc work | `insert_light` 0x0054d1b0 (player-sorted, capped); `add_visible_cell` 0x0052de40 (on-demand-load resident registry + flush); `add_dynamic_lights` 0x0052d410 (whole-table walk); caller 0x00452d30; `calc_point_light` 0x0059c8b0 (static 1/d³ curve — A7 fix #2) |
|
| AP-85 | **Point-light pool = single 128-cap player-nearest list, optionally FILTERED by LAST FRAME's rendered visible-cell set, vs retail's dual pools (7 dynamic + 40 static, degrade-scaled) collected from a DBObj-load/flush-bounded resident registry** (A7.L1, 2026-07-09 — third revision, Town Network starvation fix #79/#93/#176/#177): retail's `CEnvCell::visible_cell_table` (`add_visible_cell` 0x0052de40) is populated ON DEMAND as cells are approached/seen (`DBObj::Get`-loads) and pruned by `flush_cells` — so a real dungeon's per-frame candidate set stays small (naturally proximity-bounded) even though the collection walk itself (`add_dynamic_lights` 0x0052d410) is "the whole resident table, not a re-flood." acdream's `_all` list instead registers at LANDBLOCK-granularity load/unload (a whole single-landblock dungeon streams as ONE unit), so for the Town Network (463 registered fixtures, one landblock) `_all` is effectively "everything ever loaded in this dungeon," not a proximity-bounded set — wide enough that the player-nearest-128 cap alone let a straight-line-closer-but-wall-disconnected corridor's fixtures out-rank the player's own room, starving it. Fix: `BuildPointLightSnapshot(playerWorldPos, visibleCells)` takes an optional candidacy FILTER — a light joins the pool iff `CellId==0` (cell-less, always in) or `visibleCells.Contains(CellId)` — narrowing candidates to the frame's actual visible cells BEFORE the existing dynamics-first player-nearest cap runs; `GameWindow` feeds LAST FRAME's already-rendered `RetailPViewFrameResult.DrawableCells` (one frame / ~16 ms latency, chosen specifically to avoid re-threading a mid-`DrawInside` callback — the exact mechanism, `c500912b`, that caused the #176 seam-floor flicker regression when it re-flooded an independent CAMERA-seeded set mid-frame). The distance-sort anchor stays the PLAYER (unchanged from the prior revision) — only candidacy narrows. Remaining deviation: this is a RENDER-visibility approximation of retail's true on-demand-load/flush RESIDENCY bound, with one frame of latency, not a port of the DBObj-load/flush mechanism itself; and the pool is still ONE 128-cap list vs retail's separate 7-dynamic/40-static degrade-scaled pools | `src/AcDream.Core/Lighting/LightManager.cs` (`BuildPointLightSnapshot`, `MaxGlobalLights`); `src/AcDream.App/Rendering/GameWindow.cs` (`_lightPoolVisibleCells`/`_lightPoolVisibleCellsValid`); pins `PointSnapshot_HubScaleLightCount_ObjectSelectionIsCameraInvariant`, `PointSnapshot_OverCap_DynamicsNeverEvictedByNearerStatics`, `PointSnapshot_ResidentCollection_CellTagDoesNotFilter`, `BuildPointLightSnapshot_VisibleCellScoping_RoomLightsSurviveOverEuclideanCloserInvisibleCell`, `BuildPointLightSnapshot_VisibleCellScoping_CellLessLightAlwaysIncluded` | The render already computes a visible-cell set every frame for drawing (single source of truth, no duplicate flood) — reusing it as a candidacy filter approximates retail's proximity-bounded residency without porting DBObj on-demand load/flush; one-frame latency is imperceptible at normal camera speeds and structurally differs from the reverted mechanism (no independent re-flood mid-frame) | On a portal crossing, the FIRST indoor frame after re-entry (or after any outdoor-only frame) is unscoped (fail-open) — one frame may show slightly wider pool composition than steady-state; a room with >7 resident dynamics still shows them all (retail trims to 7 player-nearest) — slightly purpler wedge than retail; adopt the dual pools + degrade caps + true DBObj-bounded residency in later A7-arc work | `insert_light` 0x0054d1b0 (player-sorted, capped); `add_visible_cell` 0x0052de40 (on-demand-load resident registry + flush); `add_dynamic_lights` 0x0052d410 (whole-table walk); caller 0x00452d30; `calc_point_light` 0x0059c8b0 (static 1/d³ curve — A7 fix #2) |
|
||||||
| AP-84 | **BSP shadow-shape part poses = motion-table default-state frame snapshot at registration, not retail's live CPhysicsPart pose** (#175): server entities with a wire MotionTableId register their BSP part shapes at the default style's first-cycle LowFrame pose (the closed pose for doors — `GameWindow.MotionTableDefaultPose`); retail collision reads each part's CURRENT pose every test. Equivalent for the door lifecycle (closed = default pose; open = ETHEREAL bypasses collision entirely, #150) and for idle statics | `src/AcDream.App/Rendering/GameWindow.cs` (`MotionTableDefaultPose` + the RegisterServerEntityCollision override); `src/AcDream.Core/Physics/ShadowShapeBuilder.cs` (`partPoseOverride`) | Registration is one-shot in acdream (retail re-poses parts per frame); the default-state pose is the correct idle pose and the only non-ethereal pose doors ever collide in | An entity whose server-driven motion state materially MOVES a BSP-bearing part while NON-ethereal would collide at the stale default pose (no known case — doors are the dominant BSP-part weenies); revisit if animated non-ethereal BSP movers appear | `CPhysicsPart` live pose (see #150 notes); motion-table default state = CPartArray init; ShadowShapeBuilder placement-frame fallback for table-less entities |
|
| AP-84 | **BSP shadow-shape part poses = motion-table default-state frame snapshot at registration, not retail's live CPhysicsPart pose** (#175): server entities with a wire MotionTableId register their BSP part shapes at the default style's first-cycle LowFrame pose (the closed pose for doors — `GameWindow.MotionTableDefaultPose`); retail collision reads each part's CURRENT pose every test. Equivalent for the door lifecycle (closed = default pose; open = ETHEREAL bypasses collision entirely, #150) and for idle statics | `src/AcDream.App/Rendering/GameWindow.cs` (`MotionTableDefaultPose` + the RegisterServerEntityCollision override); `src/AcDream.Core/Physics/ShadowShapeBuilder.cs` (`partPoseOverride`) | Registration is one-shot in acdream (retail re-poses parts per frame); the default-state pose is the correct idle pose and the only non-ethereal pose doors ever collide in | An entity whose server-driven motion state materially MOVES a BSP-bearing part while NON-ethereal would collide at the stale default pose (no known case — doors are the dominant BSP-part weenies); revisit if animated non-ethereal BSP movers appear | `CPhysicsPart` live pose (see #150 notes); motion-table default state = CPartArray init; ShadowShapeBuilder placement-frame fallback for table-less entities |
|
||||||
|
|
@ -214,6 +214,8 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
||||||
| AP-116 | Default `Particle Range = Extended` multiplies DAT-authored particle degradation distances by 2; the `Retail` option restores exact values | `src/AcDream.UI.Abstractions/Panels/Settings/DisplaySettings.cs`; `src/AcDream.App/Rendering/Vfx/ParticleVisibilityController.cs`; `src/AcDream.Core/Vfx/ParticleSystem.cs` | User explicitly requested doubled range as the normal non-dev-UI behavior; it changes no terrain, scenery, entity, fog, or streaming distance, and remains reversible through settings | The default roughly enlarges the active particle area and reduces the CPU gain from MP2; distant VFX remain visible beyond retail's authored cutoff | `CPhysicsPart::GetMaxDegradeDistance @ 0x0050D510`; `GfxObjDegradeInfo::get_max_degrade_distance @ 0x0051E2D0`; `CPhysicsObj::ShouldDrawParticles @ 0x0050FE60` |
|
| AP-116 | Default `Particle Range = Extended` multiplies DAT-authored particle degradation distances by 2; the `Retail` option restores exact values | `src/AcDream.UI.Abstractions/Panels/Settings/DisplaySettings.cs`; `src/AcDream.App/Rendering/Vfx/ParticleVisibilityController.cs`; `src/AcDream.Core/Vfx/ParticleSystem.cs` | User explicitly requested doubled range as the normal non-dev-UI behavior; it changes no terrain, scenery, entity, fog, or streaming distance, and remains reversible through settings | The default roughly enlarges the active particle area and reduces the CPU gain from MP2; distant VFX remain visible beyond retail's authored cutoff | `CPhysicsPart::GetMaxDegradeDistance @ 0x0050D510`; `GfxObjDegradeInfo::get_max_degrade_distance @ 0x0051E2D0`; `CPhysicsObj::ShouldDrawParticles @ 0x0050FE60` |
|
||||||
| AP-117 | Outdoor particle `CLandCell::IsInView` state is reconstructed with the modern landscape renderer's per-cell frustum plus active doorway clip-plane/scissor-AABB tests; retail `LScape::landcell_check` uses `Render::get_clip_height` + `Render::block_check` on terrain-cell corner intervals | `src/AcDream.App/Rendering/TerrainModernRenderer.cs` (`CollectVisibleCells`) | The mandatory modern renderer batches terrain by landblock and has no retail `ViewIntervalType` product. Publishing cell visibility from the exact landscape draw slices preserves ownership/order and removes the former object-survivor dependency without adding a second view pipeline | At a terrain cell grazing a frustum or doorway boundary, the conservative AABB test may freeze or resume particles on a slightly different frame than retail; whole regions outside the active doorway slice are rejected, and authored distance, login/portal fail-closed behavior, and indoor PView cells remain exact | `LScape::landcell_check @ 0x005050A0`; `CLandCell::IsInView @ 0x00532CB0`; `CPhysicsObj::ShouldDrawParticles @ 0x0050FE60` |
|
| AP-117 | Outdoor particle `CLandCell::IsInView` state is reconstructed with the modern landscape renderer's per-cell frustum plus active doorway clip-plane/scissor-AABB tests; retail `LScape::landcell_check` uses `Render::get_clip_height` + `Render::block_check` on terrain-cell corner intervals | `src/AcDream.App/Rendering/TerrainModernRenderer.cs` (`CollectVisibleCells`) | The mandatory modern renderer batches terrain by landblock and has no retail `ViewIntervalType` product. Publishing cell visibility from the exact landscape draw slices preserves ownership/order and removes the former object-survivor dependency without adding a second view pipeline | At a terrain cell grazing a frustum or doorway boundary, the conservative AABB test may freeze or resume particles on a slightly different frame than retail; whole regions outside the active doorway slice are rejected, and authored distance, login/portal fail-closed behavior, and indoor PView cells remain exact | `LScape::landcell_check @ 0x005050A0`; `CLandCell::IsInView @ 0x00532CB0`; `CPhysicsObj::ShouldDrawParticles @ 0x0050FE60` |
|
||||||
| AP-118 | An AutoWield transaction begun in active combat preserves the ready mode implied by the requested weapon. After authoritative `WieldObject`, a mode that settled without a blocker transition clears immediately; local ACE's observed pre-wield transition plus `ready -> NonCombat`, or post-wield `NonCombat -> ready -> NonCombat`, causes one normal `ChangeCombatMode` request from the trailing notice. Explicit user combat input cancels settlement. Retail's client does not need this extra request against the retail server. | `src/AcDream.App/UI/AutoWieldController.cs`; production binding in `GameWindow.cs` | Local ACE queues a trailing NonCombat callback during primary-weapon replacement and rejects an earlier request while the shuffle is busy; responding to the authoritative notice that completes that exact sequence orders the ordinary request after it without suppressing any server state | A non-ACE server that emits a different intermediate sequence can retain the settlement until a later explicit combat request, replacement, or logout clears it; peace-mode equips send none | `CPlayerSystem::AutoWield @ 0x00560A60`; `ACCWeenieObject::ServerSaysMoveItem @ 0x0058DBB0`; ACE `Player_Inventory.TryShuffleStance` / `TryDequipObjectWithNetworking` |
|
| AP-118 | An AutoWield transaction begun in active combat preserves the ready mode implied by the requested weapon. After authoritative `WieldObject`, a mode that settled without a blocker transition clears immediately; local ACE's observed pre-wield transition plus `ready -> NonCombat`, or post-wield `NonCombat -> ready -> NonCombat`, causes one normal `ChangeCombatMode` request from the trailing notice. Explicit user combat input cancels settlement. Retail's client does not need this extra request against the retail server. | `src/AcDream.App/UI/AutoWieldController.cs`; production binding in `GameWindow.cs` | Local ACE queues a trailing NonCombat callback during primary-weapon replacement and rejects an earlier request while the shuffle is busy; responding to the authoritative notice that completes that exact sequence orders the ordinary request after it without suppressing any server state | A non-ACE server that emits a different intermediate sequence can retain the settlement until a later explicit combat request, replacement, or logout clears it; peace-mode equips send none | `CPlayerSystem::AutoWield @ 0x00560A60`; `ACCWeenieObject::ServerSaysMoveItem @ 0x0058DBB0`; ACE `Player_Inventory.TryShuffleStance` / `TryDequipObjectWithNetworking` |
|
||||||
|
| AP-119 | Equal-generation CreateObject refresh applies the packet's complete `PhysicsDesc` to the existing `EntityEffectProfile`, including replacing its network sound/PES-table/default-script description. Retail's equal-`INSTANCE_TS` branch applies the individual ObjDesc/Parent-or-Position/Movement/State/Vector/WeenieDesc tail and does not call `CPhysicsObj::set_description` again. | `src/AcDream.App/Rendering/GameWindow.cs` (`RouteSameGenerationCreateObject`); moving to `LiveEntityNetworkUpdateController` in Slice 4 | Existing spell/projectile/portal VFX tests and connected behavior were accepted with this refresh. Slice 4 records and isolates it rather than silently changing DAT-effect ownership during an architecture extraction. | A same-generation CreateObject whose PeTable/sound/default-script fields differ from the original can replace effect lookup state where retail would retain the original table, producing a different later typed effect. | `SmartBox::HandleCreateObject @ 0x00454C80`; `CPhysicsObj::set_description @ 0x00514F40`; `docs/research/2026-07-13-retail-projectile-vfx-pseudocode.md` |
|
||||||
|
| AP-120 | `ObjectTableWiring.ApplyEntitySpawn` publishes the CreateObject's WeenieDesc/item state before the same-generation physics update tail. Retail applies WeenieDesc after ObjDesc, Parent-or-Position/Pickup, Movement, State, and Vector. | `src/AcDream.Core.Net/ObjectTableWiring.cs` (`ApplyEntitySpawn`); App same-generation route in `GameWindow.cs`, moving to `LiveEntityNetworkUpdateController` in Slice 4 | Core.Net owns item-table ingestion before App callbacks and the current single-thread FIFO prevents a second network packet from interleaving; changing publication order crosses the Core.Net/App ownership boundary and requires a separately tested event transaction. | A synchronous item-table observer can see the refreshed WeenieDesc while the same object's physics/parent/state still reflects the prior snapshot; retail observers see the completed physics tail first. | `SmartBox::HandleCreateObject @ 0x00454C80`; `ACCObjectMaint::CreateObject @ 0x00558870` |
|
||||||
|
|
||||||
## 4. Temporary stopgap (TS) — 34 active rows + 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) — 34 active rows + 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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,360 @@
|
||||||
|
# GameWindow Slice 4 — live-entity App integration
|
||||||
|
|
||||||
|
**Status:** Active 2026-07-21.
|
||||||
|
**Parent program:** [`docs/architecture/code-structure.md`](../architecture/code-structure.md), Slice 4.
|
||||||
|
**Baseline:** `ed9dc366`; `GameWindow.cs` is 14,310 lines, 274 fields, and
|
||||||
|
190 methods before this slice. Release baseline: 6,714 passed / 5 skipped.
|
||||||
|
**Behavior rule:** Preserve the accepted live-object identity, packet FIFO,
|
||||||
|
independent timestamp authorities, render/physics/effect lifetime, and connected
|
||||||
|
R6 behavior while moving their App integration to focused owners. Correct only
|
||||||
|
proven retail-conformance defects with an explicit oracle and test; do not hide
|
||||||
|
them inside a mechanical extraction.
|
||||||
|
|
||||||
|
**Progress ledger:**
|
||||||
|
|
||||||
|
- [ ] A — canonical seams: `LiveWorldOriginState`, single-bind runtime-component
|
||||||
|
lifecycle bridge, extracted `RemoteMotion`, and canonical physics-host
|
||||||
|
ownership. No wire or presentation behavior changes.
|
||||||
|
- [ ] B — pure appearance/projection helpers and controller-level
|
||||||
|
characterization tests.
|
||||||
|
- [ ] C — hydration create/materialize/landblock-recovery ownership and direct
|
||||||
|
CreateObject/streaming cutover.
|
||||||
|
- [ ] D — ObjDesc/Parent/Pickup/ready/withdraw ownership and exact retained
|
||||||
|
identity across leave-world/re-entry.
|
||||||
|
- [ ] E — Delete/prune and retryable exact-incarnation teardown ownership.
|
||||||
|
- [ ] F — Position/Vector/State/Movement controller, same-generation CreateObject
|
||||||
|
tail, independent authority gates, and direct inbound cutover.
|
||||||
|
- [ ] G — `GameWindow` cleanup, three-agent review cycle, full Release suite,
|
||||||
|
connected gates, documentation, and durable memory closeout.
|
||||||
|
|
||||||
|
## 1. Outcome and non-goals
|
||||||
|
|
||||||
|
This slice creates two cooperating App owners:
|
||||||
|
|
||||||
|
- `LiveEntityHydrationController` owns CreateObject, ObjDesc, Parent, Pickup,
|
||||||
|
Delete/prune, DAT-backed appearance hydration, first materialization,
|
||||||
|
landblock rehydration, ready publication, projection withdrawal, and exact
|
||||||
|
App-component teardown over `LiveEntityRuntime`.
|
||||||
|
- `LiveEntityNetworkUpdateController` owns accepted Position, Vector, State,
|
||||||
|
and Movement routing into the existing motion, physics, projectile,
|
||||||
|
presentation, and teleport owners. It also consumes the same-generation
|
||||||
|
CreateObject update tail without reconstructing the entity.
|
||||||
|
|
||||||
|
`LiveEntityRuntime` remains the only owner of server GUID ↔ local identity,
|
||||||
|
incarnation, accepted snapshots, independent authority versions, spatial
|
||||||
|
worksets, and teardown tombstones. Neither new controller may contain a GUID
|
||||||
|
dictionary or cache an incarnation outside the runtime record it is currently
|
||||||
|
validating.
|
||||||
|
|
||||||
|
This is not a new entity model, physics algorithm, renderer, packet decoder, or
|
||||||
|
DAT reader. It does not retire the documented future-packet, visibility-PVS,
|
||||||
|
or null-parent-queue divergences. It does not redesign the accepted update
|
||||||
|
thread or `RetailInboundEventDispatcher` FIFO.
|
||||||
|
|
||||||
|
## 2. Retail and shipped oracles
|
||||||
|
|
||||||
|
The extraction is pinned to:
|
||||||
|
|
||||||
|
- `SmartBox::HandleCreateObject @ 0x00454C80`;
|
||||||
|
- `ACCObjectMaint::CreateObject @ 0x00558870`;
|
||||||
|
- `SmartBox::ProcessObjectNetBlobs @ 0x00454B20` and
|
||||||
|
`QueueBlobForObject @ 0x00451B90`;
|
||||||
|
- `SmartBox::HandleReceivedPosition @ 0x00453FD0`;
|
||||||
|
- `SmartBox::DoPickupEvent @ 0x00452240` and
|
||||||
|
`DoParentEvent @ 0x00452290`;
|
||||||
|
- `SmartBox::HandleDeleteObject @ 0x00451EA0`;
|
||||||
|
- `CPhysicsObj::set_description @ 0x00514F40`;
|
||||||
|
- `CPhysicsObj::set_state @ 0x00514DD0`;
|
||||||
|
- `CPhysicsObj::change_cell @ 0x00513390`;
|
||||||
|
- `CPhysicsObj::exit_world @ 0x00514E60` and
|
||||||
|
`leave_world @ 0x005155A0`;
|
||||||
|
- [`2026-07-13-retail-projectile-vfx-pseudocode.md`](../research/2026-07-13-retail-projectile-vfx-pseudocode.md);
|
||||||
|
- [`2026-07-18-retail-object-liveness-and-mesh-reclamation-pseudocode.md`](../research/2026-07-18-retail-object-liveness-and-mesh-reclamation-pseudocode.md);
|
||||||
|
- [`2026-07-19-r6-update-object-order-pseudocode.md`](../research/2026-07-19-r6-update-object-order-pseudocode.md);
|
||||||
|
- ACE and Holtburger object-update handlers as interpretation and protocol
|
||||||
|
cross-checks, never as replacements for the named-retail ordering.
|
||||||
|
|
||||||
|
The following rules are load-bearing:
|
||||||
|
|
||||||
|
1. One logical object exists per accepted `INSTANCE_TS`. Equal generation
|
||||||
|
mutates it; newer generation tears down and replaces it; older generation
|
||||||
|
is ignored using retail's wrap-safe comparison, including the `0x8000`
|
||||||
|
boundary.
|
||||||
|
2. A fresh CreateObject constructs, describes, attaches, enters world, then
|
||||||
|
replays queued blobs. An equal-generation CreateObject applies ObjDesc,
|
||||||
|
Parent or Position/Pickup, Movement, State, Vector, then WeenieDesc without
|
||||||
|
recreating the object.
|
||||||
|
3. Position, Movement, State, Vector, ObjDesc, Teleport, ForcePosition,
|
||||||
|
ServerControlledMove, and Instance authorities remain independent. After
|
||||||
|
every reentrant callback, the exact record and relevant authority version
|
||||||
|
are revalidated.
|
||||||
|
4. Movement consumes strict `MOVEMENT_TS` before it can discover a stale
|
||||||
|
server-controlled timestamp; equality on the latter is accepted.
|
||||||
|
5. Parent, Pickup, and Position share `POSITION_TS`. Pickup unparents and
|
||||||
|
leaves world; a later fresh Position unparents and restores the same object
|
||||||
|
identity/resources.
|
||||||
|
6. `leave_world` is not deletion. It clears cell/shadow/contact/transient
|
||||||
|
presentation while retaining PartArray, movement, animation, scripts,
|
||||||
|
effects, timestamps, and identity. Logical destruction alone performs
|
||||||
|
`exit_world` manager cleanup.
|
||||||
|
7. Inbound processing is one FIFO. Nested heterogeneous packets wait until the
|
||||||
|
active packet reaches its complete tail.
|
||||||
|
|
||||||
|
## 3. Architecture and interfaces
|
||||||
|
|
||||||
|
### 3.1 Shared origin state
|
||||||
|
|
||||||
|
`LiveWorldOriginState` owns the session-scoped world center currently spread
|
||||||
|
across `_liveCenterX`, `_liveCenterY`, and `_liveCenterKnown`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
bool IsKnown { get; }
|
||||||
|
double CenterX { get; }
|
||||||
|
double CenterY { get; }
|
||||||
|
bool TryInitialize(double worldX, double worldY);
|
||||||
|
void Recenter(double worldX, double worldY);
|
||||||
|
void Reset();
|
||||||
|
```
|
||||||
|
|
||||||
|
Hydration, accepted Position/teleport, streaming, and render consumers share
|
||||||
|
this state directly. No GameWindow callback conceals origin mutation.
|
||||||
|
|
||||||
|
### 3.2 Runtime component lifecycle
|
||||||
|
|
||||||
|
Introduce:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
interface ILiveEntityRuntimeComponentLifecycle
|
||||||
|
{
|
||||||
|
void TearDown(LiveEntityRecord exactRecord);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`DeferredLiveEntityRuntimeComponentLifecycle` is a composition-only,
|
||||||
|
single-assignment bridge. Construction order is fixed:
|
||||||
|
|
||||||
|
1. construct the unbound bridge;
|
||||||
|
2. construct `LiveEntityRuntime` with the bridge;
|
||||||
|
3. construct effect, projectile, presentation, child, and related owners;
|
||||||
|
4. construct hydration;
|
||||||
|
5. bind the bridge exactly once to hydration;
|
||||||
|
6. construct and bind the network-update sink;
|
||||||
|
7. only then permit a live session to start.
|
||||||
|
|
||||||
|
Calling before bind fails fast; second bind is rejected. This removes the
|
||||||
|
circular GameWindow teardown façade without making runtime mutation optional.
|
||||||
|
The existing `CompositeLiveEntityResourceLifecycle` remains the sole logical
|
||||||
|
mesh/script registration transaction; hydration does not become a second
|
||||||
|
resource owner.
|
||||||
|
|
||||||
|
### 3.3 Canonical runtime components
|
||||||
|
|
||||||
|
Move `RemoteMotion` out of `GameWindow` into `AcDream.App.Physics` without
|
||||||
|
semantic changes. Store physics-host resolution on the exact
|
||||||
|
`LiveEntityRecord` (or an explicit runtime-owned component index) rather than
|
||||||
|
the duplicate `_physicsHosts` GUID dictionary. Local-player host publication
|
||||||
|
uses the same focused owner, not a second identity table. Preserve the full-cell
|
||||||
|
binding and remote-placement contract.
|
||||||
|
|
||||||
|
### 3.4 Hydration API
|
||||||
|
|
||||||
|
The controller surface is narrow and event-shaped:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
void OnCreate(WorldSession.EntitySpawn spawn);
|
||||||
|
void OnObjDesc(ObjDescEvent.Parsed update);
|
||||||
|
void OnDelete(DeleteObject.Parsed update);
|
||||||
|
void OnPrune(LiveEntityPruneCandidate candidate);
|
||||||
|
void OnPickup(PickupEvent.Parsed update);
|
||||||
|
void OnParent(ParentEvent.Parsed update);
|
||||||
|
bool TryAcceptParentForRender(ParentEvent.Parsed update);
|
||||||
|
void OnLandblockLoaded(uint landblockId);
|
||||||
|
bool EnsureWorldOriginAndRecover(
|
||||||
|
LiveEntityRecord expected,
|
||||||
|
ulong positionAuthorityVersion,
|
||||||
|
WorldSession.EntitySpawn acceptedSpawn);
|
||||||
|
void OnEntityReady(uint guid);
|
||||||
|
bool WithdrawWorldProjection(uint guid);
|
||||||
|
```
|
||||||
|
|
||||||
|
Hydration receives focused concrete collaborators/ports: runtime and object
|
||||||
|
table, the sole `DatCollection` and shared DAT lock, physics data cache,
|
||||||
|
spawn/animation/collision builders, spatial state/origin/streaming, and the
|
||||||
|
existing effect/projectile/child/presentation/light owners. It must not receive
|
||||||
|
a generic GameWindow context or a bundle of callbacks to old substantial
|
||||||
|
methods.
|
||||||
|
|
||||||
|
ObjDesc mutates the captured exact entity. Acquire-before-publish rollback and
|
||||||
|
post-callback incarnation checks preserve local ID, body, motion, physics host,
|
||||||
|
effect owner, and parent state. Parent-on-spawn may reenter; the canonical
|
||||||
|
snapshot is reread afterward. A materialized object only rebuckets; a pending,
|
||||||
|
never-materialized object hydrates once. Landblock load never replays
|
||||||
|
create-time logical resource activation.
|
||||||
|
|
||||||
|
### 3.5 Network-update API
|
||||||
|
|
||||||
|
The network owner accepts Position, Vector, State, Movement, and the equal-
|
||||||
|
generation CreateObject tail. It resolves through `LiveEntityRuntime`, captures
|
||||||
|
the relevant authority version, calls existing focused runtime owners, and
|
||||||
|
revalidates exact record plus authority after every callback.
|
||||||
|
|
||||||
|
Hydration calls a once-bound `ILiveEntityNetworkUpdateSink` for the
|
||||||
|
same-generation tail. Network position recovery calls
|
||||||
|
`EnsureWorldOriginAndRecover`; no substantial dispatch returns to GameWindow.
|
||||||
|
Local ForcePosition, teleport presentation, projectile corrections, shadow
|
||||||
|
updates, movement animation, and static/headless branches retain their shipped
|
||||||
|
ordering.
|
||||||
|
|
||||||
|
### 3.6 Direct composition
|
||||||
|
|
||||||
|
At slice exit these callbacks point directly to the new owners:
|
||||||
|
|
||||||
|
- session Create/Delete/Pickup/Parent/ObjDesc/Position/Vector/State/Movement;
|
||||||
|
- streaming landblock-loaded rehydration;
|
||||||
|
- liveness prune;
|
||||||
|
- equipped-child parent acceptance and entity-ready replay;
|
||||||
|
- runtime exact-component teardown.
|
||||||
|
|
||||||
|
`GameWindow` keeps controller construction plus narrow frame calls. The old
|
||||||
|
handler, hydration, collision-registration, appearance-rebind, ready,
|
||||||
|
leave-world, and teardown method bodies must be absent, not retained as
|
||||||
|
one-line façades.
|
||||||
|
|
||||||
|
## 4. Commit sequence and gates
|
||||||
|
|
||||||
|
### A — canonical seams
|
||||||
|
|
||||||
|
- Add and test `LiveWorldOriginState`.
|
||||||
|
- Add and test the fail-fast, single-bind lifecycle bridge.
|
||||||
|
- Extract `RemoteMotion` mechanically; retarget all 51 App-test references.
|
||||||
|
- Canonicalize physics-host ownership and remove `_physicsHosts` only after
|
||||||
|
resolution/teardown/local-player tests prove equivalence.
|
||||||
|
- Run App remote-motion, runtime, physics-host, session-reset, and structural
|
||||||
|
suites; full Release build/test; three-agent review and re-review.
|
||||||
|
|
||||||
|
### B — presentation helpers and characterization
|
||||||
|
|
||||||
|
- Extract `AppearanceUpdateState`, appearance rebind, default-pose, and
|
||||||
|
collision-build helpers behind focused App types.
|
||||||
|
- Preserve the exact DAT lock and existing extracted WorldBuilder pipeline.
|
||||||
|
- Add controller-harness fakes that require no Silk/GL context.
|
||||||
|
- Characterize current leave-world failure ordering rather than silently
|
||||||
|
redesigning it.
|
||||||
|
- Run appearance, animation, spawn-adapter, collision, resource-lifecycle,
|
||||||
|
and new characterization suites; full Release build/test; review cycle.
|
||||||
|
|
||||||
|
### C — create and materialization
|
||||||
|
|
||||||
|
- Implement hydration CreateObject registration, first materialization,
|
||||||
|
pending deferral, landblock recovery, and ready publication.
|
||||||
|
- Preserve prior-incarnation cleanup failure aggregation and rollback.
|
||||||
|
- Route session CreateObject and streaming callbacks directly.
|
||||||
|
- Tests: loaded create order; pending→loaded; materialized rebucket; same/
|
||||||
|
older/new/wrapped generations; nested callback replacement; registration
|
||||||
|
failure/retry; no duplicate mesh/script/effect activation.
|
||||||
|
- Full Release build/test; review cycle.
|
||||||
|
|
||||||
|
### D — appearance, parenting, pickup, withdrawal
|
||||||
|
|
||||||
|
- Move ObjDesc, Parent, Pickup, child-ready, collision registration, and
|
||||||
|
projection withdrawal.
|
||||||
|
- Tests: identity/component preservation; acquire failure rollback; stale or
|
||||||
|
reentrant ObjDesc; parent-before-create canonical reread; Pickup/Parent
|
||||||
|
leave-world; Position re-entry with unchanged local ID/resources.
|
||||||
|
- Bind child callbacks directly and remove their GameWindow façades.
|
||||||
|
- Full Release build/test; review cycle.
|
||||||
|
|
||||||
|
### E — delete and exact teardown
|
||||||
|
|
||||||
|
- Move Delete/prune, teardown-plan construction, leave-world component cleanup,
|
||||||
|
and retry tombstone behavior.
|
||||||
|
- Bind runtime bridge and liveness directly.
|
||||||
|
- Tests: unknown/stale/current Delete; callback deletion; dual failure;
|
||||||
|
teardown retry; same-GUID newer generation during old teardown; reset
|
||||||
|
convergence; no surviving renderer/collision/script/effect/light owner.
|
||||||
|
- Full Release build/test; review cycle.
|
||||||
|
|
||||||
|
### F — network-update owner
|
||||||
|
|
||||||
|
- Move Movement, Vector, State, and Position routing plus the equal-generation
|
||||||
|
CreateObject tail.
|
||||||
|
- Preserve FIFO and independent authorities; test stale, equal, wrap, nested,
|
||||||
|
and callback-invalidated updates for every channel.
|
||||||
|
- Preserve ForcePosition, server-controlled move, teleport, projectile,
|
||||||
|
shadow, static, airborne, autonomous, animated, and headless branches.
|
||||||
|
- Route session callbacks directly and remove old GameWindow handlers.
|
||||||
|
- Full Release build/test; review cycle.
|
||||||
|
|
||||||
|
### G — closeout
|
||||||
|
|
||||||
|
- Structural tests prove both controllers lack identity dictionaries and
|
||||||
|
`GameWindow` lacks every moved field/type/method/body.
|
||||||
|
- Run all live-runtime, object-table, parent/attachment, appearance/animation,
|
||||||
|
effect/light/projectile, remote-motion/physics, inbound FIFO, liveness,
|
||||||
|
stress, session-reset, and router suites.
|
||||||
|
- Run `dotnet build AcDream.slnx -c Release` and full Release tests.
|
||||||
|
- Run the connected R6 route and graceful reconnect gate, then user visual
|
||||||
|
checks for inventory/equip/parenting, death/corpse, and portals when
|
||||||
|
available.
|
||||||
|
- Update architecture, roadmap, milestones, issues/divergence register, this
|
||||||
|
ledger, `AGENTS.md`/`CLAUDE.md` when current-state pointers change, and
|
||||||
|
durable GameWindow/live-runtime memory.
|
||||||
|
|
||||||
|
## 5. Adversarial acceptance matrix
|
||||||
|
|
||||||
|
Automated acceptance includes:
|
||||||
|
|
||||||
|
- equal/old/new/wrapped `INSTANCE_TS`, including `0x8000`;
|
||||||
|
- full same-generation call trace and WeenieDesc tail ordering;
|
||||||
|
- nested heterogeneous FIFO and exact authority invalidation;
|
||||||
|
- fresh loaded create and pending→loaded without replay;
|
||||||
|
- loaded→loaded rebucket and loaded↔pending oscillation;
|
||||||
|
- ObjDesc identity/body/motion/host/effect/parent preservation;
|
||||||
|
- Parent/Pickup→cell-less→Position re-entry with unchanged local ID;
|
||||||
|
- unknown/stale/current Delete, callback delete, GUID reuse, and retry;
|
||||||
|
- state side-effect order and movement/ForcePosition timestamp order;
|
||||||
|
- projectile and remote-position corrections across landblocks;
|
||||||
|
- bridge unbound/second-bind failures;
|
||||||
|
- session reset with zero surviving live owners;
|
||||||
|
- 96-owner and repeated-recall stress with no resource growth;
|
||||||
|
- source/reflection ownership gates.
|
||||||
|
|
||||||
|
Connected acceptance includes:
|
||||||
|
|
||||||
|
- capped login and populated radar/world;
|
||||||
|
- remote creature motion and target/status projection;
|
||||||
|
- wield/dewield, bow/wand/melee switching, ammo parenting, inventory pickup;
|
||||||
|
- open and loot a corpse after approach;
|
||||||
|
- kill/death/corpse lifetime;
|
||||||
|
- recall and portal travel across landblocks, revisit, graceful close, and
|
||||||
|
fresh-process reconnect;
|
||||||
|
- uncapped post-reconnect performance checkpoint.
|
||||||
|
|
||||||
|
## 6. Review discipline
|
||||||
|
|
||||||
|
Every ownership commit receives three independent read-only reviews:
|
||||||
|
|
||||||
|
1. retail conformance — symbols, ordering, timestamps, and documented
|
||||||
|
divergences;
|
||||||
|
2. architecture/integration — identity ownership, dependency direction,
|
||||||
|
update-thread rules, lifecycle symmetry, and GameWindow reduction;
|
||||||
|
3. adversarial testing — malformed/stale packets, callback reentrancy,
|
||||||
|
registration failure, GUID reuse, landblock churn, reset, and leaks.
|
||||||
|
|
||||||
|
The primary agent reproduces and fixes confirmed findings, reruns focused and
|
||||||
|
full gates, and requests re-review until no actionable finding remains. No
|
||||||
|
subagent writes the implementation.
|
||||||
|
|
||||||
|
## 7. Explicit carried divergences
|
||||||
|
|
||||||
|
Slice 4 preserves, rather than disguises:
|
||||||
|
|
||||||
|
- AD-32 future-generation non-effect packet dropping;
|
||||||
|
- AP-65 picked-up data-only weenie retention;
|
||||||
|
- AP-69 approximate 384-unit visibility envelope;
|
||||||
|
- TS-32 missing complete child-before-parent null-object queue;
|
||||||
|
- the equal-generation PES-table refresh and VectorUpdate airborne/state
|
||||||
|
adaptations recorded during Slice 4 planning.
|
||||||
|
|
||||||
|
These remain visible in the divergence register and are not evidence that the
|
||||||
|
ownership extraction itself is incomplete.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue