docs(architecture): plan GameWindow update-frame extraction
This commit is contained in:
parent
d4ecac1da3
commit
a36a7015c4
6 changed files with 594 additions and 37 deletions
|
|
@ -574,12 +574,18 @@ nine-destination resource soak pass.
|
|||
|
||||
#### Slice 6 — extract update-frame orchestration
|
||||
|
||||
Detailed execution ledger:
|
||||
[`docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md`](../plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md)
|
||||
(active).
|
||||
|
||||
After the stateful bodies above have owners, make the update path a real
|
||||
orchestrator instead of delegates back into the window. It owns the fixed
|
||||
retail phase order: input/local object → ordinary/static objects → hooks/
|
||||
particles/scripts → inbound network → command interpreter → non-advancing
|
||||
spatial reconcile → streaming/UI updates. `GameWindow.OnUpdate` becomes a
|
||||
short time/input handoff.
|
||||
orchestrator instead of delegates back into the window. It preserves the
|
||||
accepted production order—not an overclaimed retail host order—covering
|
||||
teardown/clock/streaming convergence, input, the retail-shaped object → inbound
|
||||
network → command barrier, conditional spatial reconciles, liveness, local
|
||||
teleport, player-mode entry, and camera presentation. The exact twelve-phase
|
||||
graph and registered TS-53 host-order adaptation live in the detailed ledger.
|
||||
`GameWindow.OnUpdate` becomes a short time/input handoff.
|
||||
|
||||
Frame-order tests and the existing R6 gate must produce the same lifecycle and
|
||||
movement traces before and after extraction.
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| 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/Physics/LiveEntityNetworkUpdateController.cs` (`ILiveEntitySameGenerationUpdateSink.OnDescription`); `LiveEntitySameGenerationUpdateRouter.cs` | 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`); `src/AcDream.App/World/LiveEntitySameGenerationUpdateRouter.cs` | 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) — 35 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)
|
||||
|
||||
| # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle |
|
||||
|---|---|---|---|---|---|
|
||||
|
|
@ -258,8 +258,9 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| TS-48 | Dragging an item onto another player honors the authoritative `DragItemOnPlayerOpensSecureTrade` option, but the option's default-true branch stops at the existing unavailable toast because the secure-trade transaction and UI are not ported. Direct player giving through `GiveObjectRequest 0x00CD` works when the option is disabled; NPC giving is complete. | `src/AcDream.App/UI/ItemInteractionController.cs` (`PlaceIn3D`, `PolicyActionMessage`); `src/AcDream.Core/Items/ItemInteractionPolicy.cs` | The player/NPC distinction and character preference are now faithful; inventing a direct gift while the option requests secure trade would be a worse behavioral divergence. Secure trade is a separate multi-party state machine beyond the starter-dungeon NPC-give slice. | With retail's default character options, an item dragged onto another player cannot be exchanged until the secure-trade subsystem lands. | `ItemHolder::AttemptPlaceIn3D @ 0x00588600`; `PlayerModule::DragItemOnPlayerOpensSecureTrade @ 0x005D31B0`; `ClientTradeSystem`; `docs/research/2026-07-13-retail-give-item-pseudocode.md` |
|
||||
| TS-49 | Hidden-object availability is bridged through `TargetManager.NotifyVoyeurOfEventAndClear(ExitWorld)` because acdream has not ported retail's DetectionManager. Retail `CObjCell::hide_object` sends `LeftDetection` to detection voyeurs; acdream instead withholds Hidden hosts from ordinary `GetObjectA` relationship creation and uses the existing non-Ok target update to tear down MoveTo/Sticky consumers and clear watched-role subscriptions while preserving the hidden object's own watcher role. | `src/AcDream.App/Physics/EntityPhysicsHost.cs` (`NotifyHidden`); `src/AcDream.App/Physics/LiveEntityMotionRuntimeController.cs` (`ResolvePhysicsHost`); `src/AcDream.Core/Physics/Motion/TargetManager.cs` (`NotifyVoyeurOfEventAndClear`) | The current movement consumers already share TargetManager's status fan-out; the bridge prevents pursuit of an unavailable object without inventing a second partial detection database. | Plugins or future systems listening specifically for retail detection enter/leave events receive no `LeftDetection`; only movement/sticky target consumers observe the equivalent availability loss. | `CObjCell::hide_object @ 0x0052BE30`; retire by porting DetectionManager/CObjCell detection-voyeur delivery and routing Hidden through `LeftDetection` |
|
||||
| TS-50 | `AnimationDone` executes semantically at each owner's retail `CPhysicsObj::process_hooks` boundary, but all other animation hooks are retained in `AnimationHookFrameQueue` until final root/part/equipped-child pose publication. Retail executes the complete hook stream before transition and the Target/Movement/PartArray/Position manager tail because its current CPartArray pose already exists in-place. Static owners correctly reach `process_hooks` only after their root, parts, and children are current. | `src/AcDream.App/Rendering/Vfx/AnimationHookFrameQueue.cs`; `src/AcDream.App/Rendering/RetailStaticAnimatingObjectScheduler.cs`; frame drain in `src/AcDream.App/Rendering/GameWindow.cs` | The modern renderer publishes immutable effect-pose snapshots after all root/child composition; deferred visual sinks avoid attaching particles/lights/audio to the previous pose. Semantic `AnimationDone` is split out and exact, so motion completion and manager behavior are not delayed. Pose-owner lifetime tokens prevent deferred hooks from crossing delete/local-ID reuse. | A non-AnimationDone hook with same-quantum semantic consequences (notably `CallPES`, default-script chaining, audio/particle creation relative to a transition) runs later than retail and can observe post-tail state or start one render frame late. | `CPhysicsObj::process_hooks @ 0x00511550`; `CPhysicsObj::UpdatePositionInternal @ 0x00512C30`; `CPhysicsObj::animate_static_object @ 0x00513DF0`; retire by publishing the current per-object/child pose before hook routing or splitting semantic and presentation sinks without changing authored hook order |
|
||||
| TS-51 | Particle and PhysicsScript tails advance once per render frame after the complete ordinary/static object worksets. Retail advances each object's ParticleManager and ScriptManager inside every admitted `UpdateObjectInternal` quantum; `animate_static_object` also advances that static owner's managers using its whole admitted elapsed interval. | `src/AcDream.App/Rendering/GameWindow.cs` (`AdvanceLiveObjectRuntime` final `_particleSystem.Tick(dt)` / `_scriptRunner.Tick(...)`) | The current managers are shared presentation/runtime owners rather than per-object manager instances. R6 makes root motion, animation, object clocks, workset membership, and manager order faithful without pretending the shared tails have per-owner timing. Splitting ownership safely requires a later effect-lifetime slice. | A render fragment below retail's minimum object quantum can advance an effect while its owner waits; a catch-up frame advances an owner's root through several quanta but its effect tail only once; static default scripts/particles use render elapsed rather than `animate_static_object` elapsed/discard timing. | `CPhysicsObj::UpdateObjectInternal @ 0x005156B0`; `CPhysicsObj::animate_static_object @ 0x00513DF0`; retire by giving live/static owners incarnation-bound particle/script managers and ticking each manager in the owning object quantum |
|
||||
| TS-51 | Particle and PhysicsScript tails advance once per render frame after the complete ordinary/static object worksets. Retail advances each ordinary object's ParticleManager then ScriptManager inside every admitted `UpdateObjectInternal` quantum; `animate_static_object` instead advances that static owner's ScriptManager then ParticleManager and only then `process_hooks`, using its whole admitted elapsed interval. acdream's shared tail is Particle → Script after static hook capture. | `src/AcDream.App/Rendering/GameWindow.cs` (`AdvanceLiveObjectRuntime` final `_particleSystem.Tick(dt)` / `_scriptRunner.Tick(...)`); `src/AcDream.App/Rendering/RetailStaticAnimatingObjectScheduler.cs` | The current managers are shared presentation/runtime owners rather than per-object manager instances. R6 makes root motion, animation, object clocks, workset membership, and ordinary manager order faithful without pretending the shared tails have per-owner timing or static-tail order. Splitting ownership safely requires a later effect-lifetime slice. | A render fragment below retail's minimum object quantum can advance an effect while its owner waits; a catch-up frame advances an owner's root through several quanta but its effect tail only once; static hooks can route before their script/particle managers and static default scripts/particles use render elapsed in Particle → Script order rather than `animate_static_object` elapsed/discard and Script → Particle → hooks timing. | `CPhysicsObj::UpdateObjectInternal @ 0x005156B0`; `CPhysicsObj::animate_static_object @ 0x00513DF0`; retire by giving live/static owners incarnation-bound particle/script managers and ticking each manager in the owning object quantum/order |
|
||||
| TS-52 | The terrain shader applies retail-authored base/overlay/road `TerrainTex.TexTiling` but omits the separate Environment Detail Textures pass and its viewer-distance fade (**#226**). | `src/AcDream.App/Rendering/TerrainAtlas.cs`; `src/AcDream.App/Rendering/TerrainModernRenderer.cs`; `src/AcDream.App/Rendering/Shaders/terrain_modern.frag` | `bb5acab9` fixed the user-visible stretched/blurry regression by porting the distinct base-tiling contract. An earlier experimental detail array darkened the whole ground because its source/neutral blend contract was wrong, so it was correctly reverted rather than guessed into production. | With retail's Environment Detail Textures preference enabled, close terrain lacks the extra high-frequency modulation/fade even though authored base texture scale is correct. | `LScape::GenerateDetailSurfaces` / `SetDetailTexturing @ 0x00506B40`; `ACRender::landPolyDraw @ 0x006B6450..0x006B6525`; issue #226 |
|
||||
| TS-53 | acdream advances retained UI time on the draw seam and local teleport/UI-camera presentation after its SmartBox-shaped object → inbound network → CommandInterpreter barrier. Retail `Client::UseTime` calls `UIElementManager::UseTime` first, whose global time message reaches `gmSmartBoxUI::UseTime`, and publishes player-camera work from the physics/player callback rather than one post-network camera tail. Slice 6 preserves the accepted host order as an ownership-only extraction. | `src/AcDream.App/Rendering/GameWindow.cs` (`OnUpdate` teleport/camera tails and `_retailUiRuntime.Tick` in `OnRender`); `docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md` | Current retained UI, portal transit, reveal, camera, and connected movement traces are accepted; changing cross-subsystem host order while extracting ownership would combine a behavior change with the structural cutover. | Retained UI, teleport, and camera presentation can observe same-frame object/inbound/player state one host update earlier or later than retail at transition boundaries; a future exact host-order port must prove UI, input, reveal, and camera consequences together. | `Client::UseTime @ 0x00411C40`; `UIElementManager::UseTime`; `gmSmartBoxUI::UseTime @ 0x004D6E30`; `CPhysics::UseTime @ 0x00509950`; retire only with a focused host-order port and connected portal/camera comparison |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ session replacement, including failure/retry and fast relogin. Shutdown keeps
|
|||
the reset graph alive until disposal converges. `GameWindow` is 8,811 raw
|
||||
lines / 247 fields / 153 methods (44% below the campaign baseline); 7,054
|
||||
Release tests, the capped/reconnect lifecycle gate, and the synchronized
|
||||
nine-stop resource soak pass. Slice 6 update-frame orchestration is active.
|
||||
nine-stop resource soak pass. Slice 6 update-frame orchestration is active;
|
||||
its detailed checkpoint ledger is
|
||||
[`2026-07-21-gamewindow-slice-6-update-frame-orchestration.md`](2026-07-21-gamewindow-slice-6-update-frame-orchestration.md).
|
||||
|
||||
This is a behavior-preserving structural program. Severe regressions still get
|
||||
root-cause fixes in separate commits; ordinary feature work resumes with M4
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ origin lifetimes. `GameWindow` is 8,811 raw lines / 247 fields / 153 methods,
|
|||
down 1,490 lines from the Slice-5 baseline and 6,912 (44%) from the campaign
|
||||
baseline. The full Release suite passes 7,054 tests / 5 skips; the 311-second
|
||||
capped/reconnect lifecycle gate and synchronized 394-second nine-destination
|
||||
resource soak both pass. Slice 6 update-frame orchestration is next.
|
||||
resource soak both pass. Slice 6 update-frame orchestration is active under
|
||||
[`2026-07-21-gamewindow-slice-6-update-frame-orchestration.md`](2026-07-21-gamewindow-slice-6-update-frame-orchestration.md).
|
||||
|
||||
Carried:
|
||||
#153 far-teleport residual, #116 slide-response, the remaining R6 ownership cleanup plus
|
||||
|
|
|
|||
|
|
@ -0,0 +1,442 @@
|
|||
# GameWindow Slice 6 — update-frame orchestration
|
||||
|
||||
**Status:** Active 2026-07-21.
|
||||
**Parent program:** [`docs/architecture/code-structure.md`](../architecture/code-structure.md), Slice 6.
|
||||
**Baseline:** `d4ecac1d`; `GameWindow.cs` is 8,811 raw lines, 247 fields, and
|
||||
153 methods. Release baseline: 7,054 passed / 5 skipped. The connected
|
||||
lifecycle gate and synchronized nine-stop resource soak both pass.
|
||||
**Behavior rule:** This is an ownership extraction. Preserve the accepted
|
||||
retail object/network order, asynchronous streaming-readiness adaptation,
|
||||
portal transit, input semantics, and camera presentation. A defect exposed by
|
||||
the boundary receives its own deterministic oracle and bisectable fix; it is
|
||||
never hidden behind a retry, delay, suppression flag, or reordered callback.
|
||||
|
||||
## Progress ledger
|
||||
|
||||
- [ ] A — freeze the complete production phase graph and introduce the typed
|
||||
orchestration contract plus structural/order guards.
|
||||
- [ ] B — extract the pre-network live-object presentation phase and the
|
||||
non-advancing post-network spatial reconciler; remove callbacks into
|
||||
`GameWindow` from `RetailLiveFrameCoordinator`.
|
||||
- [ ] C — extract streaming-origin convergence, observer selection, dungeon
|
||||
collapse, streaming tick, and rescued-entity rebucketing.
|
||||
- [ ] D — extract dispatcher/raw-mouse/combat input sampling without changing
|
||||
movement-command or UI-capture semantics.
|
||||
- [ ] E — extract the complete local-player teleport owner and wire it between
|
||||
the existing post-network liveness and player-mode auto-entry phases.
|
||||
- [ ] F — extract the shared player-mode lifecycle plus fly/chase/player
|
||||
camera presentation and cut production over to one
|
||||
`UpdateFrameOrchestrator`.
|
||||
- [ ] G — delete the old `OnUpdate` bodies, callback facades, and obsolete
|
||||
frame state; run focused corrected-diff reviews after each ownership edge.
|
||||
- [ ] H — full Release suite, connected lifecycle/reconnect gate, synchronized
|
||||
resource soak, documentation, durable memory, and line/field/method closeout.
|
||||
|
||||
Every checked checkpoint is committed as one bisectable architectural unit.
|
||||
Documentation records the exact commit and accepted test count as work lands.
|
||||
|
||||
## 1. Outcome and non-goals
|
||||
|
||||
At slice exit, `GameWindow.OnUpdate` starts the profiler scope and delegates one
|
||||
host delta to `UpdateFrameOrchestrator`. `GameWindow` composes concrete owners;
|
||||
it no longer selects a streaming observer, advances animations/effects,
|
||||
samples raw mouse input, sequences inbound network traffic, advances portal
|
||||
transit, or updates gameplay cameras.
|
||||
|
||||
The owner graph is explicit rather than a list of anonymous callbacks:
|
||||
|
||||
```text
|
||||
UpdateFrameOrchestrator
|
||||
├── live teardown convergence retry retained tombstones
|
||||
├── UpdateFrameClock normalized host time / PES clock
|
||||
├── StreamingFrameController residence/readiness pre-pass
|
||||
├── GameplayInputFrameController semantic input + mouse/combat intent
|
||||
├── RetailLiveFrameCoordinator retail SmartBox object barrier
|
||||
│ ├── LiveObjectFrameController local/ordinary/static/effect tails
|
||||
│ ├── LiveSessionController inbound network drain
|
||||
│ ├── RetailLocalPlayerFrame CommandInterpreter position tail
|
||||
│ └── LiveSpatialReconciler non-advancing authoritative refresh
|
||||
├── LiveEntityLivenessController expiry after accepted inbound state
|
||||
├── LocalPlayerTeleportController local transit/reveal/tunnel state
|
||||
├── PlayerModeAutoEntry one-shot ready-world entry
|
||||
├── PlayerModeController enter/exit/current-player lifetime
|
||||
└── CameraFrameController fly/chase/player presentation
|
||||
```
|
||||
|
||||
Concrete names may be narrowed during checkpoint A's source audit, but the
|
||||
ownership boundaries and order do not change. The orchestrator receives a
|
||||
small immutable per-frame input and concrete phase owners. It does not receive
|
||||
a hundred delegates, one mega `IUpdateFrameServices` context, or a service
|
||||
locator, and no transitive update collaborator calls a substantial method back
|
||||
on `GameWindow`.
|
||||
|
||||
This slice does not change simulation rate, object quantum rules, movement
|
||||
speed, interpolation, streaming radii, reveal readiness, portal visuals,
|
||||
particle range, combat cadence, key bindings, camera formulas, render order,
|
||||
or shutdown order. It adds no GL work and does not move retained-UI timers out
|
||||
of their render seam. Slice 7 owns the draw graph; Slice 8 owns final
|
||||
composition and Silk callback cleanup.
|
||||
|
||||
## 2. Retail oracle and accepted adaptation
|
||||
|
||||
The object/network barrier is pinned by
|
||||
[`2026-07-19-r6-update-object-order-pseudocode.md`](../research/2026-07-19-r6-update-object-order-pseudocode.md):
|
||||
|
||||
- `CPhysicsObj::update_object @ 0x00515D10` applies the strict object quantum;
|
||||
- `CPhysicsObj::UpdatePositionInternal @ 0x00512C30` advances the PartArray,
|
||||
position/physics candidate, then processes semantic hooks;
|
||||
- `CPhysicsObj::UpdateObjectInternal @ 0x005156B0` runs Detection → Target →
|
||||
Movement → `CPartArray::HandleMovement` → Position → Particle → Script;
|
||||
- `SmartBox::UseTime @ 0x00455410` advances object/physics time before the
|
||||
inbound queue and then calls `CommandInterpreter::UseTime`;
|
||||
- `gmSmartBoxUI::UseTime @ 0x004D6E30` owns UI/viewport time outside that
|
||||
object barrier.
|
||||
|
||||
At the retail host level, `Client::UseTime @ 0x00411C40` calls
|
||||
`UIElementManager::UseTime` before `SmartBox::UseTime`; the UI time broadcast
|
||||
reaches `gmSmartBoxUI::UseTime`. Retail also publishes player-camera work from
|
||||
the physics/player callback rather than from one post-network camera tail.
|
||||
acdream currently advances teleport/UI-camera presentation after its
|
||||
object/network/command barrier. Slice 6 preserves that existing order as
|
||||
registered TS-53; it does not cite `gmSmartBoxUI` as proof that the current
|
||||
post-network placement is retail-exact.
|
||||
|
||||
acdream preserves two documented presentation adaptations:
|
||||
|
||||
1. only semantic `AnimationDone` executes at retail's in-object hook slot;
|
||||
every other captured animation hook retains the current deferred path after
|
||||
final root, part, and equipped-child poses are published, including
|
||||
particles, audio, lights, translucency, `CallPES`, and default-script
|
||||
chaining (TS-50);
|
||||
2. shared particle simulation and PhysicsScript queues tick once per host
|
||||
update after both worksets rather than once per object's admitted quantum.
|
||||
This also means the current shared Particle → Script order does not reproduce
|
||||
the static workset's retail Script → Particle → hooks order (TS-51).
|
||||
|
||||
Asynchronous landblock streaming has no one-call retail equivalent. Its
|
||||
convergence pre-pass remains before inbound CreateObject dispatch so a newly
|
||||
accepted projection can find a resident bucket in the same frame. This
|
||||
pre-pass does not advance an object's retail clock. `WorldRevealCoordinator`
|
||||
remains the single login/portal readiness owner.
|
||||
|
||||
## 3. Fixed production phase order
|
||||
|
||||
The complete host-update graph is frozen as follows:
|
||||
|
||||
1. retry canonical live-entity teardown tombstones; an `AggregateException` is
|
||||
logged and later phases continue, while any other exception propagates;
|
||||
2. normalize the host delta, advance the shared PES clock once, and publish it;
|
||||
3. advance pending origin retirement/recenter, select the observer, tick
|
||||
streaming, and rebucket rescued persistent entities;
|
||||
4. tick `InputDispatcher`, consume one raw mouse sample, and advance combat
|
||||
attack intent;
|
||||
5. enter the non-reentrant inbound-event envelope and advance the pre-network
|
||||
live-object phase:
|
||||
- local object and pre-network movement/jump output;
|
||||
- queued selection/use output after movement edges;
|
||||
- ordinary live objects;
|
||||
- the distinct static-animation workset;
|
||||
- equipped children and static hook completion;
|
||||
- fades, every deferred non-`AnimationDone` hook, and current effect/light
|
||||
anchors;
|
||||
- particle visibility and particle simulation;
|
||||
- PhysicsScript queues;
|
||||
6. drain inbound session traffic inside one `GpuWorldState.MutationBatch`;
|
||||
7. run the local player's post-network `CommandInterpreter` position check;
|
||||
8. perform the ordinary post-network non-advancing root/child/emitter/light
|
||||
reconcile;
|
||||
9. advance liveness expiry from the absolute client timer;
|
||||
10. activate/tick local teleport transit. A Place edge commits the player and
|
||||
live root, immediately performs its own non-advancing reconcile, then
|
||||
notifies world reveal and continues tunnel state;
|
||||
11. evaluate one-shot player-mode auto-entry;
|
||||
12. update fly or chase/player camera presentation. If the local player was
|
||||
first created by this frame's inbound pass, publish its non-advancing
|
||||
projection, immediately reconcile a second time, then update the camera.
|
||||
|
||||
Those three reconcile edges—ordinary post-network, teleport Place, and
|
||||
inbound-created-player projection—are separate accepted production edges. Any
|
||||
future consolidation is a behavior change with its own oracle, not part of
|
||||
this extraction.
|
||||
|
||||
An invalid/non-positive host delta becomes zero for the PES/object/particle/
|
||||
tunnel delta but never blocks inbound traffic, absolute-time liveness,
|
||||
recenter convergence, or camera-state publication. Raw mouse idle uses its
|
||||
independent monotonic input clock. A missing gameplay camera/input owner never
|
||||
suppresses outer phases; the streaming controller may skip observer-driven
|
||||
streaming when it has no valid live or offline observer, while origin
|
||||
retirement still advances. Devtools keyboard capture pauses player/camera
|
||||
control including autorun; retained chat capture suppresses held WASD while
|
||||
autorun continues; mouse capture ends mouse-look but does not suppress
|
||||
keyboard, combat, network, streaming, teleport, or teardown.
|
||||
|
||||
## 4. Architecture and interfaces
|
||||
|
||||
### 4.1 Per-frame input
|
||||
|
||||
Use an immutable value equivalent to:
|
||||
|
||||
```csharp
|
||||
public readonly record struct UpdateFrameInput(double HostDeltaSeconds);
|
||||
```
|
||||
|
||||
Derived facts are read by their owning controller from stable runtime owners
|
||||
or from a narrowly typed state source. Do not pass `GameWindow`, expose its
|
||||
private fields wholesale, or create a service locator. Mutable player/session
|
||||
owners that legitimately change during login are represented by one focused
|
||||
state/source interface per domain, not anonymous closures into window methods.
|
||||
|
||||
`UpdateFrameClock` is constructed before any materializer/network owner that
|
||||
needs the current PES time. It owns the normalized host/PES clock and exposes a
|
||||
narrow `CurrentScriptTime` source; `_physicsScriptGameTime` does not remain a
|
||||
`GameWindow` field captured by later owners. Absolute liveness time and raw
|
||||
mouse idle time remain distinct monotonic sources and are not derived from the
|
||||
normalized simulation delta.
|
||||
|
||||
### 4.2 Object phase
|
||||
|
||||
`LiveObjectFrameController` owns the body currently in
|
||||
`AdvanceLiveObjectRuntimeCore`. It composes existing focused owners and keeps
|
||||
their retail order; it does not absorb their algorithms or identity maps.
|
||||
`LiveSpatialPresentationReconciler` owns the current no-time refresh body.
|
||||
`RetailLiveFrameCoordinator` depends on those typed owners plus the existing
|
||||
session and local-player owners. Its public `Tick` remains the one explicit
|
||||
object → network → command → reconcile barrier.
|
||||
|
||||
`RetailInboundEventDispatcher` continues wrapping the complete object phase.
|
||||
Reentrant packets queue until the current object's manager tail completes.
|
||||
The hot path remains allocation-free after warmup.
|
||||
|
||||
The extraction inventory includes the complete transitive update graph, not
|
||||
only `RetailLiveFrameCoordinator`: `RetailLocalPlayerFrameController` loses
|
||||
substantial window captures in favor of a focused local-player slot, movement
|
||||
input source, outbound owner, and projection owner;
|
||||
`LiveEntityAnimationPresenter` receives a concrete animation-presentation
|
||||
context instead of retaining `GameWindow` as
|
||||
`ILiveAnimationPresentationContext`; scheduler clock/player/projectile sources
|
||||
are similarly narrow. Existing small value/policy callbacks are acceptable
|
||||
only when they do not call a substantial body back on the window or create a
|
||||
second identity/state owner.
|
||||
|
||||
### 4.3 Streaming phase
|
||||
|
||||
`StreamingFrameController` owns observer selection and the sealed-dungeon
|
||||
gate. It receives the existing `StreamingOriginRecenterCoordinator`,
|
||||
`StreamingController`, `LiveWorldOriginState`/center state, camera/player
|
||||
state, physics current cell, `GpuWorldState`, and `LiveEntityRuntime` through
|
||||
typed seams. It never builds, publishes, or retires a landblock; those remain
|
||||
Slice 5 owners.
|
||||
|
||||
The controller preserves:
|
||||
|
||||
- no pre-login hardcoded-Holtburg streaming;
|
||||
- real-player-origin readiness before first streaming;
|
||||
- destination-origin pinning while in portal space;
|
||||
- authoritative player landblock during the login hold;
|
||||
- offline fly-camera observation only outside live in-world mode;
|
||||
- sealed EnvCell collapse using the cell's own landblock identity;
|
||||
- teleport-hold suppression of the stale source-cell dungeon gate;
|
||||
- rescued-entity rebucketing after the accepted streaming tick.
|
||||
|
||||
### 4.4 Input and camera phases
|
||||
|
||||
`GameplayInputFrameController` owns per-frame dispatcher hold delivery,
|
||||
UI-capture transitions, raw mouse sample/filter/idle handling, combat attack
|
||||
ticking, and the complete mouse-look/cursor-capture lifetime. It is the single
|
||||
owner of mouse-look state, saved cursor mode, begin/end/hide/restore, and
|
||||
movement teardown across press/release, capture change, focus loss, teleport,
|
||||
session reset, player-mode exit, and shutdown. Action press/release callbacks
|
||||
remain on `InputDispatcher` and movement stays owned by
|
||||
`PlayerMovementController`.
|
||||
|
||||
`PlayerModeController` owns enter/exit/toggle construction and the current
|
||||
player-controller, physics-host, chase-camera, and chase-mode slot. Manual
|
||||
input, `PlayerModeAutoEntry`, and local teleport activation all enter through
|
||||
that one owner. Streaming, camera, local-frame, and teleport owners receive
|
||||
narrow read sources from it; no path calls the substantial current
|
||||
`EnterPlayerModeNow`/exit bodies back on `GameWindow` or duplicates player-mode
|
||||
state.
|
||||
|
||||
`CameraFrameController` owns held fly-camera controls, retail chase offsets,
|
||||
post-network player presentation sampling, and both chase-camera updates. It
|
||||
does not own physics, target selection, or camera formulas. The local player
|
||||
created by the inbound pass gets a non-advancing projection before its first
|
||||
draw exactly as today, followed by the current second spatial reconcile before
|
||||
the camera samples that projection.
|
||||
|
||||
### 4.5 Teleport phase
|
||||
|
||||
`LocalPlayerTeleportController` owns only the current local-player transit
|
||||
state machine and presentation edges. It composes
|
||||
`TeleportTransitCoordinator`,
|
||||
`TeleportViewPlaneController`, `PortalTunnelPresentation`,
|
||||
`WorldRevealCoordinator`, streaming priority, and the session LoginComplete
|
||||
send. Checkpoint E first extracts the currently window-owned local aim,
|
||||
destination, hold/forced, activation, placement, reset, and session-clear
|
||||
state into this owner plus a concrete local placement collaborator where
|
||||
needed. It does not reuse `RemoteTeleportPlacement`, and network/session sinks
|
||||
do not retain `AimTeleportDestination` or reset callbacks into `GameWindow`.
|
||||
`LiveEntityLivenessController`, tombstone retry, and `PlayerModeAutoEntry`
|
||||
remain separate focused phases; teleport does not absorb them.
|
||||
|
||||
### 4.6 Composition, thread, and lifetime
|
||||
|
||||
The clock and focused dynamic sources are constructed before their first
|
||||
consumer. The final orchestrator is composed exactly once at the end of a
|
||||
successful `OnLoad`; `OnUpdate` fails fast if that invariant is broken rather
|
||||
than running a nullable partial graph. Every phase mutates state only on the
|
||||
existing update/render thread. Streaming worker completion remains behind
|
||||
`StreamingController.Tick`; the extraction adds no lock, task, or async entry.
|
||||
|
||||
Phase controllers borrow their input, session, streaming, portal, camera, and
|
||||
render-presentation owners; they do not dispose shared dependencies. Any new
|
||||
event subscription has explicit symmetric detach. During shutdown the update
|
||||
collaborators remain alive through the first-stage `LiveSessionController`
|
||||
reset, then retire before their underlying input/camera/portal/streaming
|
||||
resources. Existing retryable shutdown ordering remains authoritative.
|
||||
|
||||
## 5. Checkpoints and acceptance
|
||||
|
||||
### A — contract and guards
|
||||
|
||||
- record the exact production phase graph and every mutable dependency;
|
||||
- correct and pin the retail oracle for physics-hook retention/reentrant
|
||||
append, inactive object behavior, and static Script → Particle → hooks; pin
|
||||
the existing TS-50/TS-51 divergences without reordering runtime behavior in
|
||||
this ownership-only slice;
|
||||
- add an explicit orchestrator order test covering all twelve phases and the
|
||||
three conditional reconcile edges;
|
||||
- pin normalized simulation/PES/particle/tunnel time separately from absolute
|
||||
liveness and raw-mouse idle clocks for NaN, both infinities, non-positive
|
||||
values, and finite values above `float.MaxValue`;
|
||||
- pin missing live/offline camera behavior and the separate devtools keyboard,
|
||||
retained-chat keyboard, and mouse-capture contracts;
|
||||
- pin teardown retry success, `AggregateException` log-and-continue, other
|
||||
exception propagation, and next-frame retry;
|
||||
- add source guards that prevent `OnUpdate` or the coordinator from regaining
|
||||
substantial callback bodies, and prevent any transitive update owner from
|
||||
retaining `GameWindow` or one mega service context;
|
||||
- verify current production traces before cutover.
|
||||
|
||||
### B — object/network barrier
|
||||
|
||||
- move the pre-network animation/effect body into
|
||||
`LiveObjectFrameController`;
|
||||
- move no-time root/child/effect refresh into
|
||||
`LiveSpatialPresentationReconciler`;
|
||||
- replace coordinator callbacks with typed collaborators;
|
||||
- preserve semantic/visual hook separation, owner-incarnation revalidation,
|
||||
static-after-ordinary order, ordinary Particle-before-Script, the current
|
||||
shared-tail TS-51 adaptation, and once-per-host tails;
|
||||
- delete `AdvanceLiveObjectRuntimeCore` and
|
||||
`ReconcileLiveObjectSpatialPresentation` from `GameWindow`.
|
||||
|
||||
Focused tests cover `RetailObjectActivityGate` and
|
||||
`RetailObjectQuantumClock`: parented/cell-less/Frozen suspension, inactive
|
||||
Particle/Script-only time, Hidden+Active adjustment/hooks/manager time, and
|
||||
separate ordinary epsilon/Min/Max/Huge plus static epsilon/Huge boundaries.
|
||||
They also cover incomplete physics-hook
|
||||
retention, same-drain animation-hook append, deferred `CallPES`/default-script/
|
||||
translucency routing, physics-hook append deferred until the next drain,
|
||||
completed physics-hook removal preserving the snapshotted successor, static
|
||||
hooks, callback GUID reuse, and reentrant inbound packets. Particle then Script
|
||||
must run exactly once for an empty world,
|
||||
multiple ordinary/static owners, invalid delta, and reentrant inbound work;
|
||||
the one published PES clock remains visible from hook capture through the
|
||||
script tick.
|
||||
|
||||
### C — streaming frame
|
||||
|
||||
- extract readiness/recenter advancement, observer selection, dungeon gate,
|
||||
streaming tick, and rescued projection rebucketing;
|
||||
- test login-before-player-spawn, outdoor/dungeon/portal-hold/offline observer
|
||||
cases, missing live/offline camera sources, and cell-landblock identity;
|
||||
- prove same-frame streaming completion precedes CreateObject projection into
|
||||
the resident bucket, unknown pre-login origin enqueues nothing, and recenter
|
||||
retirement continues while ordinary streaming is gated;
|
||||
- verify no build/publication/retirement ownership leaks back from Slice 5.
|
||||
|
||||
### D — input frame
|
||||
|
||||
- extract dispatcher tick, UI mouse-capture edge, raw sample/filter/idle path,
|
||||
combat intent tick, and the one cross-lifetime mouse-look/cursor owner;
|
||||
- test capture entry while mouse-look is active, zero-idle drift stop,
|
||||
production action ordering, devtools-vs-chat autorun semantics, mouse-only
|
||||
capture, and focus/teleport teardown;
|
||||
- prove every non-input phase still runs under each capture mode;
|
||||
- retain diagnostic semantic input as the unattended connected gate seam.
|
||||
|
||||
### E — local teleport and post-network world frame
|
||||
|
||||
- extract the currently window-owned local-player teleport state and every
|
||||
aim/destination/activation/place/reset/session port before moving its tick;
|
||||
- retain liveness and auto-entry as separate orchestrator calls around pending
|
||||
transit activation, readiness/hold, tunnel events, materialization, and
|
||||
reveal completion;
|
||||
- test loaded and pending destinations, forced-readiness boundary, same-cell
|
||||
respawn, LoginComplete ordering, delete/reset, and no second reveal owner;
|
||||
- pin Place → immediate reconcile → reveal and old-GUID liveness candidate →
|
||||
inbound delete/recreate generation safety;
|
||||
- preserve the completed portal visuals and world-reveal barrier unchanged.
|
||||
|
||||
### F/G — camera, cutover, and deletion
|
||||
|
||||
- extract player-mode enter/exit/toggle and current-owner state before camera
|
||||
or teleport cutover; retain `PlayerModeAutoEntry` only as its one-shot
|
||||
readiness guard;
|
||||
- extract fly camera, retail chase adjustment, local post-network projection,
|
||||
combat target tracking, and chase-camera publication;
|
||||
- pin inbound-created projection → immediate second reconcile → camera, with
|
||||
exact root/child/emitter/light refresh counts and no duplicate time advance;
|
||||
- compose `UpdateFrameOrchestrator` only after all dependencies exist;
|
||||
- replace `OnUpdate` with profiler scope plus one `Tick` call;
|
||||
- remove obsolete frame fields/facades and prove there is no back-reference to
|
||||
`GameWindow` from an extracted owner;
|
||||
- measure line/field/method count. The target is below 8,000 lines, but the
|
||||
ownership/ordering exit criteria outrank the count.
|
||||
|
||||
### H — release and connected gates
|
||||
|
||||
After all three independent corrected-diff reviews are clean:
|
||||
|
||||
1. run every focused App/Core/Net/UI test project;
|
||||
2. run `dotnet build AcDream.slnx -c Release`;
|
||||
3. run the full Release suite;
|
||||
4. run the connected lifecycle/reconnect gate;
|
||||
5. run the synchronized nine-stop resource soak with movement, turn, jump,
|
||||
combat, portal materialization waits, fatal-log scan, and graceful close;
|
||||
6. compare lifecycle, outbound movement, materialization, entity/animation,
|
||||
frame-profile, resource, and teardown traces with the Slice 5 baseline;
|
||||
7. audit the divergence register, architecture, milestones, roadmap, issues,
|
||||
`AGENTS.md`, `CLAUDE.md`, and durable memory.
|
||||
|
||||
The slice requires no new visual behavior. If connected screenshots or traces
|
||||
change, return to the responsible checkpoint; do not defer the difference to
|
||||
Slice 7.
|
||||
|
||||
## 6. Review gate for every checkpoint
|
||||
|
||||
After each implementation checkpoint:
|
||||
|
||||
1. run focused tests and inspect the complete diff;
|
||||
2. retail-conformance review checks named functions, exact order, Hidden/
|
||||
quantum semantics, and adaptation boundaries;
|
||||
3. architecture review checks dependency direction, absence of a replacement
|
||||
god object/service locator, same-thread mutation, and symmetric teardown;
|
||||
4. adversarial review checks independent clocks, callback reentrancy,
|
||||
owner/GUID reuse, login/portal transitions, capture modes, missing observer
|
||||
sources, teardown failures, and early-return phase suppression;
|
||||
5. fix every confirmed root cause and repeat review until clean;
|
||||
6. build/test Release and commit the checkpoint before starting the next one.
|
||||
|
||||
## 7. Divergence and documentation bookkeeping
|
||||
|
||||
TS-50, TS-51, and the newly documented existing host-order TS-53 remain until
|
||||
their exact adaptations are ported; this slice must not overclaim retail
|
||||
parity. IA-14/AD-22 and the lifetime/resource rows are audited against the
|
||||
final owner graph but changed only if their mechanism actually changes. Any
|
||||
newly discovered unavoidable adaptation gets a register row in the same
|
||||
commit. The Slice 6 closeout updates the code-structure table,
|
||||
roadmap, milestones, session docs, and
|
||||
`project_gamewindow_decomposition.md` with exact commits, test counts, gates,
|
||||
and final metrics.
|
||||
|
|
@ -8,18 +8,23 @@ Retail oracle: Asheron's Call v11.4186 (September 2013 EoR)
|
|||
This note pins the control-flow order needed by R6. It deliberately separates
|
||||
two kinds of animation-hook work:
|
||||
|
||||
- **control hooks** such as `AnimDoneHook`, whose result changes the motion
|
||||
- the immediate **`AnimDoneHook` control path**, whose result changes the motion
|
||||
queues observed later in the same object update; and
|
||||
- **pose-dependent presentation hooks** such as particles and sounds, whose
|
||||
App-layer delivery may wait until the final root/part pose has been
|
||||
published without changing motion state.
|
||||
- **all other captured animation hooks**, which acdream currently defers until
|
||||
the final root/part pose has been published. This includes presentation
|
||||
hooks such as particles and sounds, but also semantically relevant
|
||||
`CallPES`, default-script chaining, and translucency hooks.
|
||||
|
||||
That split is an acdream presentation boundary. It does not change retail's
|
||||
logical hook order.
|
||||
That split is an acdream presentation boundary. Authored FIFO order is
|
||||
preserved within acdream's deferred non-`AnimationDone` stream, but moving that
|
||||
stream past retail's manager tail changes manager-relative timing; that is the
|
||||
registered TS-50 divergence.
|
||||
|
||||
## Named-retail sources
|
||||
|
||||
- `CPhysicsObj::process_hooks` `0x00511550`
|
||||
- `CPhysics::UseTime` `0x00509950`
|
||||
- `CPhysicsObj::animate_static_object` `0x00513DF0`
|
||||
- `CPhysicsObj::UpdatePositionInternal` `0x00512C30`
|
||||
- `CPhysicsObj::UpdateObjectInternal` `0x005156B0`
|
||||
- `CPhysicsObj::update_object` `0x00515D10`
|
||||
|
|
@ -58,6 +63,17 @@ CPhysicsObj::update_object
|
|||
-> PositionManager::UseTime
|
||||
-> ParticleManager::UpdateParticles
|
||||
-> ScriptManager::UpdateScripts
|
||||
|
||||
CPhysics::UseTime
|
||||
-> ordinary object table in the order above
|
||||
-> static_animating_objects
|
||||
-> CPhysicsObj::animate_static_object
|
||||
-> CPartArray::Update
|
||||
-> UpdatePartsInternal
|
||||
-> UpdateChildrenInternal
|
||||
-> ScriptManager::UpdateScripts
|
||||
-> ParticleManager::UpdateParticles
|
||||
-> CPhysicsObj::process_hooks
|
||||
```
|
||||
|
||||
## Pseudocode
|
||||
|
|
@ -66,11 +82,16 @@ CPhysicsObj::update_object
|
|||
|
||||
```text
|
||||
if parent != null or cell == null or state contains Frozen:
|
||||
clear Active transient state
|
||||
return
|
||||
|
||||
refresh active/view-distance state
|
||||
elapsed = current_physics_time - last_update_time
|
||||
|
||||
if elapsed <= FrameEpsilon (0.000199999995 seconds):
|
||||
last_update_time = current_physics_time
|
||||
return
|
||||
|
||||
if elapsed > HugeQuantum:
|
||||
last_update_time = current_physics_time
|
||||
return
|
||||
|
|
@ -85,8 +106,19 @@ if elapsed > MinQuantum:
|
|||
UpdateObjectInternal(elapsed)
|
||||
```
|
||||
|
||||
The comparisons are strict (`>`), not `>=`. A sub-minimum remainder stays in
|
||||
the clock for a later call.
|
||||
The comparisons are strict (`>`), not `>=`. Elapsed time at or below the
|
||||
roughly 0.2-millisecond frame epsilon is explicitly rebased/discarded. Above
|
||||
that epsilon, a remainder at or below `MinQuantum` stays in the clock for a
|
||||
later call; `MaxQuantum` controls subdivision and `HugeQuantum` controls stale
|
||||
discard. These are separate gates.
|
||||
|
||||
`UpdateObjectInternal` then branches on the transient Active bit. An inactive
|
||||
object skips position integration, hook processing, Detection, Target,
|
||||
Movement, PartArray, and PositionManager, but still advances its
|
||||
ParticleManager and ScriptManager tails. A Hidden object that remains Active
|
||||
is different: it enters `UpdatePositionInternal`, suppresses only PartArray
|
||||
advance and physics integration, and retains PositionManager adjustment,
|
||||
hook processing, and the complete manager tail.
|
||||
|
||||
### `CPhysicsObj::UpdatePositionInternal(dt, candidate)`
|
||||
|
||||
|
|
@ -120,13 +152,25 @@ manager tail.
|
|||
### `CPhysicsObj::process_hooks`
|
||||
|
||||
```text
|
||||
execute each queued physics hook in FIFO order
|
||||
clear physics-hook queue
|
||||
for each physics hook present in the linked list walk:
|
||||
completed = hook.Execute(owner)
|
||||
if completed:
|
||||
unlink and destroy that hook
|
||||
else:
|
||||
retain it for a later object update
|
||||
|
||||
execute each queued animation hook in FIFO order
|
||||
clear animation-hook queue
|
||||
for i from zero while i < current animation-hook count:
|
||||
animation_hooks[i].Execute(owner)
|
||||
// the bound is live: a hook appended during execution can run in this drain
|
||||
shrink animation-hook storage and set the count to zero
|
||||
```
|
||||
|
||||
The physics-hook walk snapshots each current node's `next` pointer before
|
||||
execution. A completed hook is removed; an incomplete hook remains linked.
|
||||
The animation-hook loop deliberately rereads `m_num` after each callback, so
|
||||
reentrant append is consumed in the same drain. Neither collection is an
|
||||
unconditional snapshot-and-clear queue.
|
||||
|
||||
For an `AnimDoneHook`:
|
||||
|
||||
```text
|
||||
|
|
@ -175,8 +219,52 @@ if script_manager != null:
|
|||
```
|
||||
|
||||
The tail is reached for both visible and Hidden objects when the object quantum
|
||||
runs. A missing manager is simply skipped. No manager is reordered around
|
||||
another manager based on local/remote/player/NPC classification.
|
||||
runs and the object is Active. An inactive object skips this position/manager
|
||||
body but still reaches Particle then Script. A missing manager is simply
|
||||
skipped. No manager is reordered around another manager based on
|
||||
local/remote/player/NPC classification.
|
||||
|
||||
### `CPhysicsObj::animate_static_object`
|
||||
|
||||
```text
|
||||
if cell == null:
|
||||
return
|
||||
|
||||
elapsed = current_time - update_time
|
||||
if elapsed <= FrameEpsilon (0.000199999995 seconds):
|
||||
update_time = current_time
|
||||
return
|
||||
if elapsed > HugeQuantum:
|
||||
update_time = current_time
|
||||
return
|
||||
|
||||
if part_array != null:
|
||||
if state contains HasDefaultAnim:
|
||||
part_array.Update(elapsed)
|
||||
rotate root by the current angular vector
|
||||
UpdatePartsInternal()
|
||||
UpdateChildrenInternal()
|
||||
|
||||
if state contains HasDefaultScript and script_manager != null:
|
||||
script_manager.UpdateScripts()
|
||||
|
||||
if particle_manager != null:
|
||||
particle_manager.UpdateParticles()
|
||||
|
||||
process_hooks()
|
||||
|
||||
update_time = current_time
|
||||
```
|
||||
|
||||
The static workset has no ordinary-object `MinQuantum` remainder gate or
|
||||
`MaxQuantum` subdivision loop: it uses only the frame epsilon and
|
||||
`HugeQuantum`, then passes the complete admitted elapsed time to PartArray.
|
||||
It therefore has a distinct Script → Particle → hooks tail, not ordinary
|
||||
objects' Particle → Script tail. acdream currently shares one
|
||||
ParticleSystem and one PhysicsScriptRunner across every owner and advances
|
||||
them once after the complete ordinary/static worksets. That is the registered
|
||||
TS-51 adaptation and must not be silently reordered by an ownership-only
|
||||
extraction.
|
||||
|
||||
## acdream conformance boundary
|
||||
|
||||
|
|
@ -186,9 +274,11 @@ unsupported `DetectionManager` is represented by an absent callback rather
|
|||
than an invented behavior.
|
||||
|
||||
Animation-hook capture performs `AnimationDone` control effects immediately,
|
||||
at the `process_hooks` boundary. Pose-dependent routing remains deferred until
|
||||
the current frame's root/part poses are published. Deferred routing must never
|
||||
invoke `AnimationDone` a second time and must never own
|
||||
at the `process_hooks` boundary. Every other captured animation hook currently
|
||||
remains deferred until the final root/part poses are published, including
|
||||
particle/audio/light/translucency hooks, `CallPES`, and default-script
|
||||
chaining. Deferred routing must never invoke `AnimationDone` a second time and
|
||||
must never own
|
||||
`MotionTableManager.UseTime`; that belongs only to the PartArray manager-tail
|
||||
slot.
|
||||
|
||||
|
|
@ -199,17 +289,32 @@ slot.
|
|||
2. Target runs before Movement; Movement runs before PartArray; PartArray runs
|
||||
before PositionManager.
|
||||
3. A missing manager does not shift or duplicate another callback.
|
||||
4. Hidden objects skip sequence advance but retain the same manager tail.
|
||||
5. A visual hook sees a pose published after capture, while an `AnimDoneHook`
|
||||
changes motion state at capture time.
|
||||
6. Deferred visual-hook drain never completes an animation twice and never
|
||||
performs a zero-tick motion sweep.
|
||||
7. A sub-MinQuantum frame performs no object-tail call; accumulated time is
|
||||
consumed by a later quantum.
|
||||
4. Parented, cell-less, and Frozen objects return before clock admission.
|
||||
5. Inactive objects skip position/hooks/manager time but retain Particle then
|
||||
Script; Hidden + Active objects skip sequence/physics but retain adjustment,
|
||||
hooks, and the manager tail.
|
||||
6. A non-`AnimationDone` hook sees a pose published after capture, while an
|
||||
`AnimDoneHook` changes motion state at capture time.
|
||||
7. Deferred hook drain never completes an animation twice and never performs
|
||||
a zero-tick motion sweep; representative `CallPES`, default-script, and
|
||||
translucency hooks retain their current deferred path.
|
||||
8. An incomplete physics hook remains linked, and an animation hook appended
|
||||
by another animation hook executes in the same retail drain.
|
||||
9. Static objects retain their distinct Script → Particle → hooks order.
|
||||
10. Ordinary epsilon/Min/Max/Huge boundaries and static epsilon/Huge
|
||||
boundaries preserve their separate strict retail comparisons.
|
||||
|
||||
## Cross-reference notes
|
||||
|
||||
ACE's physics/motion ports remain interpretation aids for manager ownership
|
||||
and state naming; the retail binary above is authoritative for order. The
|
||||
existing `RemoteChaseEndToEndHarnessTests` hand-codes acdream's pre-R6 order
|
||||
and is therefore a migration target, not an oracle.
|
||||
ACE's `PhysicsObj.cs` corroborates ordinary Particle → Script and static
|
||||
Script → Particle → hooks order. It does **not** corroborate the retail clock
|
||||
or hook-lifetime gates: ACE uses its server `TickRate`/Min/Max policy and
|
||||
clears physics hooks unconditionally, unlike retail's epsilon/Huge behavior
|
||||
and completion-based hook retention. ACViewer exposes the same client-shaped
|
||||
methods and manager order but shares ancestry with ACE, so that agreement is
|
||||
corroboration rather than a second oracle. holtburger's native protocol crates
|
||||
have no client physics-loop equivalent (its vendored ACViewer copy is not
|
||||
independent). The retail binary above is authoritative for every boundary.
|
||||
The existing
|
||||
`RemoteChaseEndToEndHarnessTests` hand-codes acdream's pre-R6 order and is
|
||||
therefore a migration target, not an oracle.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue