feat(vfx): decode retail hooks and typed tables
Replace the incomplete package path with one DatCollection-backed compatibility seam for PhysicsScripts and Animations. Preserve CreateBlockingParticle's inherited payload and following cursor, route every production and audit consumer through the corrected loaders, and apply retail's post-UnPack StartTime ordering. Add exact stored-order PhysicsScriptTable upper-threshold resolution, high-byte DID and embedded-ID validation, plus live effect profiles with Setup-to-PhysicsDesc precedence across top-level and attached entity lifetimes. Keep blocking execution deferred and narrow TS-11 accordingly. Pin synthetic malformed/cursor/order fixtures, installed-DAT blocking and recall audits, high-index IDs, IEEE boundaries, profile teardown, and ordinary decoder parity; synchronize architecture, inventory, milestones, roadmap, research, and memory. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
8dd996053d
commit
363e046112
31 changed files with 1102 additions and 73 deletions
|
|
@ -258,7 +258,9 @@ Ownership by phase:
|
|||
`LiveEntityRuntime` is now the shipped bridge to this target. It owns one
|
||||
`LiveEntityRecord` per accepted server-object incarnation, ServerGuid-to-local-id
|
||||
translation, accepted snapshots/timestamp gates, animation and remote-motion
|
||||
components, parent-event state, and exact logical teardown. `GpuWorldState`
|
||||
components, parent-event state, effect-profile defaults, and exact logical
|
||||
teardown. Live `PhysicsDesc` effect fields replace Setup defaults on that same
|
||||
record; rebucketing never recreates them. `GpuWorldState`
|
||||
owns spatial buckets only: register/rebucket/unregister are separate operations,
|
||||
and landblock reloads reuse the same `WorldEntity` without replaying renderer or
|
||||
script creation. Its canonical materialized view remains stable across pending
|
||||
|
|
@ -267,6 +269,14 @@ targeting. Pickup/parent leave-world clears cell membership and pauses root
|
|||
movement/animation without destroying retained owners. `GameWindow` retains
|
||||
storage-free typed views while its large feature loops are extracted.
|
||||
|
||||
DAT decoding for runtime VFX crosses one narrow `AcDream.Content.Vfx` seam.
|
||||
`RetailAnimationLoader` and `RetailPhysicsScriptLoader` still read through the
|
||||
single shared `DatCollection`, delegate ordinary animation-hook schemas to
|
||||
Chorizite.DatReaderWriter, and replace only its incomplete
|
||||
`CreateBlockingParticleHook` model with retail's inherited CreateParticle
|
||||
payload. Core consumes the loaders through delegates/interfaces and therefore
|
||||
does not depend on Content or a database implementation.
|
||||
|
||||
The remaining aggregation is primarily `_playerController`'s player-specific
|
||||
movement plus the separate `WorldEntity`/animation/physics component types.
|
||||
Those should become ONE class:
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| TS-8 | `MagicUpdateEnchantment` (0x02C2) records carry no StatMod — mid-session buffs don't move vital max until relog (**#7/#12**) | `src/AcDream.Core/Spells/Spellbook.cs:150` | The wire parser hasn't been extended to the full ~60-64 byte Enchantment payload; PlayerDescription's block IS parsed | Vitals HUD percent reads differently from retail for the whole session after any buff cast | `EnchantAttribute` 0x00594570; holtburger magic/types.rs |
|
||||
| TS-9 | MP3 (0x55) and MS-ADPCM (0x02) waves undecoded — affected sounds skipped; retail decoded both via winmm ACM | `src/AcDream.Core/Audio/WaveDecoder.cs:33` | Managed decoder (NAudio or similar) deferred; PCM covers the vast majority of ~3500 waves | Any MP3 (common for music-ish clips) or ADPCM cue plays as silence where retail plays it | winmm ACM path (r05 §2.1) |
|
||||
| TS-10 | Setup lights anchored at entity root — per-light Frames not transformed through the animated part chain | `src/AcDream.Core/Lighting/LightInfoLoader.cs:31` | Per-part world transforms aren't exposed to the lighting layer; awaiting animation hook integration | A carried torch glows from the character origin, not the hand, and doesn't track swing/idle animations | LightInfo.ViewSpaceLocation per-part Frame (r13 §1) |
|
||||
| TS-11 | `CreateBlockingParticleHook` is consumed as a no-op because the package model omits its inherited CreateParticle payload; acdream therefore also lacks retail's same-live-nonzero-logical-ID suppression | `src/AcDream.Core/Vfx/ParticleHookSink.cs:112` | A narrow raw-hook compatibility reader is required so the shared payload is decoded without replacing `DatCollection` or duplicating ordinary hook schemas | Blocking-particle hooks emit nothing today; without the corrected decoder the next hook can also be cursor-misaligned, and repeated logical IDs cannot follow retail replacement/suppression rules | `CreateBlockingParticleHook::Execute` 0x00526EF0; `ParticleManager::CreateBlockingParticleEmitter` 0x0051B8A0; r04 §6 |
|
||||
| TS-11 | Retail's complete inherited `CreateBlockingParticleHook` payload is decoded by the narrow `DatCollection`-backed compatibility reader, but `ParticleHookSink` deliberately no-ops it until the live emitter-binding owner can apply same-live-nonzero-logical-ID suppression | `src/AcDream.Core/Vfx/ParticleHookSink.cs` (`RetailCreateBlockingParticleHook` case); decoder `src/AcDream.Content/Vfx/RetailAnimationHookReader.cs` | Decoding and cursor alignment are now exact for both PhysicsScripts and Animations; executing it as an ordinary replacement before Step 5 would encode the wrong lifetime rule | Blocking-particle hooks still emit nothing until Step 5; ordinary hooks following them are no longer cursor-misaligned | `CreateBlockingParticleHook::Execute` 0x00526EF0; `ParticleManager::CreateBlockingParticleEmitter` 0x0051B8A0; r04 §6 |
|
||||
| TS-12 | Animated entities' emitters use rest-pose part transforms anchored at entity root; retail attaches to the live animated part (per-tick refresh deferred; statics fixed by C.1.5b/#56) | `src/AcDream.Core/Vfx/ParticleHookSink.cs:80` (+ :20) | The renderer doesn't expose per-part world transforms to VFX; root + precomputed matrices reproduce retail placement for everything that doesn't animate | Effects hooked to animated parts (swinging hand, nodding head) emit from the rest pose / float at spawn offsets instead of tracking motion | `ParticleEmitter::UpdateParticles` 0x0051d2d4 |
|
||||
| TS-13 | `DefaultScriptHook` / `DefaultScriptPartHook` / `CallPESHook` animation hooks dropped (no OnHook case); blocker comment predates PhysicsScriptRunner (C.1.5a) and may be STALE | `src/AcDream.Core/Vfx/ParticleHookSink.cs:130` | Originally blocked on PhysicsScript dat exposure; spawn-time DefaultScript firing landed via EntityScriptActivator, the animation-frame path never did | VFX retail triggers from specific animation frames (mid-animation script calls) never appear | CallPES / DefaultScript hook dispatch (r04 §6) |
|
||||
| TS-14 | Setup `Flatten` ignores ParentIndex part hierarchy (treats every placement as root-local); still in production use (GameWindow hydration, SkyRenderer) | `src/AcDream.Core/Meshing/SetupMesh.cs:15` | Most Setups are flat single-level rigs where root-local equals composed; hierarchical composition deferred ("Phase 3") | Any Setup with genuinely nested parts renders them at wrong offsets — mis-assembled multi-part objects in the Flatten paths | retail Setup ParentIndex chain composition |
|
||||
|
|
|
|||
|
|
@ -101,6 +101,18 @@ behavior change, no divergence-register row.
|
|||
running with no GL context, so baked pak output and live-client output stay
|
||||
byte-identical.
|
||||
|
||||
**Retail VFX hook compatibility seam (2026-07-14).** Chorizite.DatReaderWriter
|
||||
2.1.7 models `CreateBlockingParticleHook` as the common hook header only, while
|
||||
retail inherits the complete `CreateParticleHook` payload. The narrow readers in
|
||||
`src/AcDream.Content/Vfx/` read raw bytes through the existing `DatCollection`
|
||||
database, delegate every ordinary hook to the package, and substitute only the
|
||||
retail blocking-particle shape. Both live animation playback and PhysicsScript
|
||||
loading use those cached readers; `MeshExtractor` uses the same PhysicsScript
|
||||
loader when preloading emitter meshes. This is not a second DAT reader:
|
||||
`DatCollection` remains the sole database owner and access path. Retail anchors:
|
||||
`CreateBlockingParticleHook::Execute` `0x00526EF0` and
|
||||
`ParticleManager::CreateBlockingParticleEmitter` `0x0051B8A0`.
|
||||
|
||||
**Workflow:** Before re-implementing any AC-specific rendering or dat-handling
|
||||
algorithm, **check this inventory first**. If we already extracted it (🟢
|
||||
sections), it's in `src/AcDream.App/Rendering/Wb/` — use our copy. If WB has
|
||||
|
|
|
|||
|
|
@ -500,7 +500,7 @@ behavior. Estimated 17–26 days focused work, 3–5 weeks calendar.
|
|||
- **Wave 4 inventory drag visuals implemented — corrective live visual gate pending.** `IconComposer` now preserves retail's separate underlay-free `m_pDragIcon` for cursor tracking across inventory, paperdoll, and toolbar bindings. Physical source cells keep their full icon in place and reveal authored ghost mesh `0x0600109A` from drag begin through every release/cancel path, with the persistent selection square above the mesh; shortcut aliases remain unghosted. `ItemList_DragOver` destination states are split exactly: contents-grid placement uses the green accept circle `0x060011F9`, while side-bag/main-pack container drop-in uses the green arrow `0x060011F7`. AP-47 retired. Research: `docs/research/2026-07-13-retail-item-drag-visuals-pseudocode.md`.
|
||||
- **Wave 4.6 item giving implemented 2026-07-13; starter-dungeon live gate pending.** SmartBox drag release now preserves the exact release coordinates, world-picks the target, and runs the existing retail `ItemHolder::AttemptPlaceIn3D` policy. NPC/creature targets send exact `GiveObjectRequest 0x00CD(target,item,amount)` bytes, selected partial stacks use the shared split quantity, and the object table waits for ACE's authoritative remove/stack-size response. `PlayerDescription.Options1` supplies the real `DragItemOnPlayerOpensSecureTrade` option. Issue #216; research: `docs/research/2026-07-13-retail-give-item-pseudocode.md`.
|
||||
- **M2 held-object parenting shipped and live-gated 2026-07-11.** The combat toggle now ports `GetDefaultCombatMode` over ordered equipped contents, so a bow requests Missile instead of the old hardcoded Melee. CreateObject preserves parent/placement/timestamp fields, `0xF749` ParentEvent is handled, and `EquippedChildRenderController` renders the weapon as a separate child composed from the animated hand part + holding frame + child placement frame. Live gate passed: bow selected missile stance, rendered in-hand, followed animation, unequipped cleanly, and melee remained correct. App Release builds with zero warnings; the full 4,765-pass / 5-skip suite is green. AP-111 is retired; research: `docs/research/2026-07-11-combat-default-and-parent-event-pseudocode.md`.
|
||||
- **Missile/portal VFX campaign Steps 0–2 implemented and independently reviewed 2026-07-14.** The retail oracle and packet fixtures are pinned, complete `PhysicsDesc` plus F754/F755 parsing and nine-channel gates are shipped, and App now has one canonical `LiveEntityRuntime` record per server-object incarnation. Logical register/unregister is separate from spatial rebucketing, so landblock churn and attached equipment retain identity without replaying renderer/script creation or reconstructing from stale spawn data. Canonical materialized and visible target/radar views are distinct; pickup/parent leave-world preserves owners while pausing root simulation; spawn publication is once per incarnation. `GpuWorldState` is spatial-only for live objects; AP-69 is narrowed to the missing retail liveness cull and AD-32 to the remaining mixed pre-create queue.
|
||||
- **Missile/portal VFX campaign Steps 0–3 implemented and independently reviewed 2026-07-14.** The retail oracle and packet fixtures are pinned, complete `PhysicsDesc` plus F754/F755 parsing and nine-channel gates are shipped, and App now has one canonical `LiveEntityRuntime` record per server-object incarnation. Logical register/unregister is separate from spatial rebucketing, so landblock churn and attached equipment retain identity without replaying renderer/script creation or reconstructing from stale spawn data. Canonical materialized and visible target/radar views are distinct; pickup/parent leave-world preserves owners while pausing root simulation; spawn publication is once per incarnation. `GpuWorldState` is spatial-only for live objects. Production PhysicsScript and Animation loading now shares a narrow `DatCollection`-backed compatibility reader for retail's inherited blocking-particle payload, including mesh-side preloading; typed-table selection preserves DAT order and retail's first `intensity <= Mod` boundary, and live `PhysicsDesc` effect defaults replace rather than fall back to Setup. AP-69 remains narrowed to the missing retail liveness cull, AD-32 to the remaining mixed pre-create queue, and TS-11 to Step 5's not-yet-landed live logical-emitter suppression.
|
||||
- **M2 local attack receive funnel implemented 2026-07-11; live gate pending.** Retail `ExecuteAttack` only sends the request; ACE chooses the concrete melee/missile action and returns it in a non-autonomous mt-0 `UpdateMotion`. The local branch now runs that state through the same constructor-defaulted `MoveToInterpretedState` funnel and 15-bit action-stamp gate as remotes, then applies sticky/long-jump tails. The old local-only direct `Commands[]` replay is deleted. Shared conversion lives in `InboundInterpretedMotionFactory`; research: `docs/research/2026-07-11-local-combat-motion-pseudocode.md`.
|
||||
- **M2 basic retail combat bar implemented 2026-07-11; corrective live visual gate pending.** Production mounts authored `gmCombatUI` LayoutDesc `0x21000073`, shows it only for Melee/Missile, and routes mouse plus keyboard through one `CombatAttackController`. Corrections include the authored wider centered dark-red child range (accepted IA-20), full-width left-to-right bright live attack-charge feedback, exact Speed-left/Power-right justification, silent control-only `AttackDone(ActionCancelled)`, target-frame Keep in View with manual orbit, and persistent corpse motion: the AP-80 velocity-only NPC adaptation can now replace only Ready/Walk/Run, never authoritative Dead/actions. `CombatTargetController` ports the selection-cleared AutoTarget consumer, so a selected creature's authoritative Dead motion clears it and selects the nearest eligible creature when enabled. The 2026-07-12 replacement-corpse correction unifies both multi-frame and static/reactive spawns behind retail's CreateObject lifecycle: apply the wire's Dead state while detached, then `MotionTableManager::HandleEnterWorld` strips Ready→Dead links before the first in-world tick; multiple corpses remaining fallen passed the live user gate that day. Follow-up #205 makes the toolbar read the final canonical selection after a reentrant Auto Target notice and restricts automatic candidates to hostile non-player monsters (intentional PK-edge divergence IA-19); that live gate also passed 2026-07-12. Research: `docs/research/2026-07-11-retail-combat-bar-pseudocode.md`, `docs/research/2026-07-11-combat-target-camera-pseudocode.md`, `docs/research/2026-07-12-death-and-auto-target-pseudocode.md`; AP-24/AP-95 retired, AP-80 narrowed, AP-110 narrowed, AP-112 records the remaining attack-start and exact trained-Recklessness seams.
|
||||
- **Retail client command families implemented 2026-07-13; corrective live gate pending.** One shared typed catalog now separates retail client actions from ACE administrator commands for both chat backends. Named-decomp ports cover recall/house/PK travel; age/birth; framerate, lock, version, location, corpse, and die confirmation; clear plus named/automatic UI layouts; AFK/consent; emotes; friends; squelch/filter/message types; and fill-components. App owns execution, Core owns authoritative friends/squelch state, Core.Net owns exact UIQueue/ControlQueue packets. Confirmation reuse now ports retail `DialogFactory` contexts, queue groups/priority, fresh DAT roots, property results, callbacks/close notices, and server abort handling; `/die`, gameplay request tuples, and guarded item-use prompts share its type-1 LayoutDesc `0x2100003C` presenter. The first live gate passed the family except for raw suicide-success code `0x004A` and the title-bar-only FPS presentation; the correction maps retail's text and mounts SmartBox element `0x10000047` with live two-decimal `FPS`/`DEG`. #L.6 is closed; TS-31/TS-47 are narrowed. Research: `docs/research/2026-07-13-retail-client-command-families-pseudocode.md`, `docs/research/2026-07-13-retail-dialog-factory-pseudocode.md`.
|
||||
|
|
|
|||
|
|
@ -471,15 +471,18 @@ include dungeons.
|
|||
`PlayerDescription.Options1` preserves retail's player secure-trade
|
||||
preference. See
|
||||
`docs/research/2026-07-13-retail-give-item-pseudocode.md` and issue #216.
|
||||
- **M2/M3 missile, effect, and portal presentation campaign (Steps 0–2
|
||||
- **M2/M3 missile, effect, and portal presentation campaign (Steps 0–3
|
||||
implemented and independently reviewed 2026-07-14)** — named-retail projectile and
|
||||
physics-script behavior is pinned in one oracle, the complete `PhysicsDesc`
|
||||
and F754/F755 wire surfaces are parsed with retail timestamp gates, and
|
||||
`LiveEntityRuntime` now separates logical lifetime from spatial rebucketing.
|
||||
Landblock churn and equipment attachment preserve one stable local entity and
|
||||
cannot replay create-time scripts. The remaining steps port DAT hook decoding,
|
||||
per-owner effect scheduling, live animated attachment poses, projectile motion
|
||||
and sweeps, and Hidden/UnHide portal presentation.
|
||||
cannot replay create-time scripts. A narrow raw-hook seam now decodes retail's
|
||||
inherited `CreateBlockingParticle` payload for both PhysicsScripts and
|
||||
Animations, while exact stored-order `PhysicsScriptTable` threshold lookup and
|
||||
Setup-versus-network default precedence are pinned by conformance tests. The
|
||||
remaining steps port per-owner effect scheduling, live animated attachment
|
||||
poses, projectile motion and sweeps, and Hidden/UnHide portal presentation.
|
||||
- **L.1c local attack receive path (implemented 2026-07-11; live gate pending)** —
|
||||
local non-autonomous mt-0 UpdateMotion now uses retail's wholesale interpreted
|
||||
funnel and action-stamp gate, so ACE's server-selected melee/missile action
|
||||
|
|
|
|||
|
|
@ -339,6 +339,11 @@ Oracles:
|
|||
- `ScriptManager::NextHook` at `0x0051B3F0`
|
||||
- `ScriptManager::UpdateScripts` at `0x0051B480`
|
||||
|
||||
`PhysicsScript::UnPack` at `0x005218B0` first sorts its decoded hook records
|
||||
by ascending `StartTime` with `PhysicsScriptData::Sort` at `0x00521600`.
|
||||
This sort applies to PhysicsScript hook time, not to PhysicsScriptTable `Mod`
|
||||
thresholds (which must remain in stored DAT order).
|
||||
|
||||
```text
|
||||
add(script):
|
||||
node = new ScriptData
|
||||
|
|
@ -661,11 +666,13 @@ noncopyrighted fixtures:
|
|||
- `tests/AcDream.Core.Net.Tests/Messages/ProjectileVfxPacketFixtures.cs`:
|
||||
exact F754/F755, a broad projectile-field CreateObject, isolated Movement
|
||||
and AnimationFrame branches, and absent-versus-present-zero PeTable shapes.
|
||||
Step 1 adds truncation at each gated cursor.
|
||||
Step 1 tests include truncation at each gated cursor.
|
||||
- `tests/AcDream.Content.Tests/Vfx/ProjectileVfxDatFixtures.cs`: a synthetic raw
|
||||
PhysicsScript and Animation container, each containing a complete
|
||||
CreateBlocking payload followed by an AnimationDone hook at the exact
|
||||
expected cursor.
|
||||
expected cursor. `RetailDatLoaderTests` verifies both corrected containers,
|
||||
ordinary-hook parity with the package decoder, malformed-entry rejection,
|
||||
and representative installed blocking scripts.
|
||||
|
||||
Representative installed raw-entry identities are pinned as metadata, not
|
||||
copied bytes:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue