# Slice J5.6 — projectile-simulation ownership Date: 2026-07-26 Scope: ownership relocation only; no retail algorithm, packet, collision policy, impact authority, or presentation change ## Objective Move the mutable projectile component, projectile workset, prediction version, authoritative correction, and retail projectile step into the same `AcDream.Runtime` physics lifetime that already owns the canonical entity record, body, object clock, collision engine, shadows, and full cell. App remains responsible for: - resolving the projectile's immutable Setup/DAT collision sphere; - supplying the current graphical projection validity acknowledgement; - rebucketing and positioning the retained render projection after a Runtime commit; - synchronizing the render shadow and effect pose; - drawing meshes, trails, particles, sounds, and impact effects. ACE remains authoritative for targets, hits, damage, impact scripts, and deletion. ## Named-retail oracle Primary source: `docs/research/named-retail/acclient_2013_pseudo_c.txt`. - `CPhysicsObj::UpdateObjectInternal` `0x005156B0` - `CPhysicsObj::update_object` `0x00515D10` - `CPhysicsObj::SetPositionInternal(CTransition const*)` `0x00515330` - `CPhysicsObj::set_velocity` `0x005113F0` - `OBJECTINFO::missile_ignore` `0x0050CEB0` - `Frame::set_vector_heading` `0x00535DB0` The complete translated retail projectile control flow is pinned in `docs/research/2026-07-13-retail-projectile-vfx-pseudocode.md`. The existing Core `ProjectilePhysicsStepper` and collision driver were cross-checked there against ACE's physics implementation and Holtburger/ACViewer's DAT interpretation. J5.6 reuses those algorithms unchanged. ## Preserved control flow ```text for each spatial projectile owned by the current incarnation: reject static, frozen, parented, hidden, cell-less, or inactive bodies advance the retained CPhysicsObj clock in retail 0.2-second quanta for each due quantum: calculate acceleration from the final PhysicsState clamp speed to 50 world units/second integrate position, velocity, omega, and AlignPath orientation continuously sweep the authored Setup-local collision sphere apply retail missile-ignore and inelastic/elastic collision rules commit the accepted frame and full cell to the canonical record stop the split prediction if any authority/incarnation token changed publish one immutable committed frame to the host ``` The graphical host may reject the presentation acknowledgement after a re-entrant delete, replacement, rebucket, or session reset. That rejection invalidates the pending prediction; it cannot roll back or redirect a newer canonical owner. ## Previous ownership gap Before J5.6: - `RuntimeEntityRecord` owned identity, body, state, clocks, and cell; - `RuntimePhysicsState` owned the engine, shadows, remotes, and ordinary/remote worksets; - App `ProjectileController` still owned the projectile component, projectile workset snapshot, quantum stepping, correction, and canonical cell commit. That split made a no-window Runtime unable to fly the same projectile without reconstructing App behavior and left projectile mutation on the wrong side of the presentation boundary. ## Implemented boundary - `RuntimeEntityRecord.Projectile` is the sole mutable projectile component slot. - `RuntimeProjectile` owns the canonical body, authored collision sphere, and prediction-authority version. - `RuntimePhysicsState` owns binding, refresh, spatial membership, authoritative vector/state correction, and the exact-key projectile workset. - `RuntimeProjectilePhysicsUpdater` owns the unchanged retail quantum, integration, sweep, collision, cell-crossing, and split-prediction logic. - Runtime cell commits flow through the same typed `RuntimePhysicsCellCommit` used by ordinary and remote objects. - App `ProjectileController` resolves DAT/Setup shape data, delegates the simulation, and applies only retained render projection, shadow, and effect pose acknowledgements. - Delete, GUID reuse, visibility withdrawal, pending-to-loaded recovery, authoritative correction, and session teardown use the canonical Runtime record and workset; they never reconstruct from stale spawn data. No gameplay outcome moved client-side. No projectile-specific render pass, second GUID map, body mirror, effect owner, or collision owner was introduced. ## Automated acceptance On production commit `2aee33569f0268d7bc0f4f52437dfa0b405432a4`: - Runtime tests: 317 passed. - App tests: 3,718 passed / 3 skipped. - Focused Core projectile tests: 47 passed. - Complete Release solution: 8,591 passed / 5 skipped. - Release solution build: zero warnings and zero errors. The focused fixtures cover: - canonical component/body/workset identity; - 0.2-second catch-up quanta and prediction invalidation; - clamp, gravity, omega, full 3-D AlignPath, and collision; - thin walls and inelastic stopping; - missile/ethereal/designated-target exclusion; - loaded, pending, landblock crossing, and hydration; - State/Vector/Position correction order; - deletion, same-GUID reuse, reset, and 96-owner lifetime stress; - no duplicate mesh, body, shadow, script, effect, or projectile owner. ## Connected acceptance All connected processes used the exact embedded/source commit `2aee33569f0268d7bc0f4f52437dfa0b405432a4` and closed gracefully. - Arrow plus representative spell projectile: `.test-out/j56-projectile-20260726-143256` recorded an authoritative missile attack, live Arrow spawn, live Acid Stream spawn, and matching authoritative deletes. - Crossbow bolt: `.test-out/j56-crossbow-evidence-20260726-145612` recorded a missile attack, live Quarrel spawn with Setup `0x0200012A`, authoritative ammo pickup/update, projectile delete, and target death. - Lifecycle/reconnect: `logs/connected-world-gate-20260726-145731/report.json` passed capped login plus fresh uncapped reconnect, all seven checkpoints, zero failures, and two code-zero exits. - Canonical nine-stop route: `logs/connected-r6-soak-20260726-151349.report.json` materialized all nine destinations, passed the Caul/Holtburg/Caul-return forward, jump, combat, and movement-truth exercises, matched source and binary, and exited with code zero and zero failures. The first route attempt, `logs/connected-r6-soak-20260726-150347.report.json`, intentionally remains in the evidence trail. Its strict plateau check saw the already-known late lazy mesh population (`582 -> 604`) while sampled entity counts happened to match and therefore reported one failure. The immediate warm-machine rerun saw the same class of late population (`581 -> 598`) together with one changed visible owner and correctly classified it as workload variation. The accepted J5.5 baseline exhibited the same late Caul behavior (`575 -> 597`), and the J5.6 diff contains no mesh-cache, mesh-preparation, streaming, or residency change. This is not waived as a projectile result and no threshold was loosened. ## Divergence and rollback J5.6 changes ownership only. The retail algorithm, constants, packet order, server authority, and presentation are unchanged, so it introduces no new retail-divergence row. Exact rollback: ```text git revert 2aee33569f0268d7bc0f4f52437dfa0b405432a4 ```