Commit graph

137 commits

Author SHA1 Message Date
Erik
6d6e5b5fa5 refactor(render): extract world frame preparation
Move camera/root resolution, live settings and listener preview, sky/lighting/fog preparation, animated classification, and building visibility scratch behind a typed WorldRenderFrameBuilder while preserving retail frame order and borrowed lifetimes.

Co-authored-by: Codex <codex@openai.com>
2026-07-22 05:54:00 +02:00
Erik
bc6f09f987 refactor(render): extract frame resource preparation
Move ordered resource begin, clear/state setup, live upload/reveal, weather timing, and display pacing behind focused owners while preserving the accepted frame order and retryable shutdown ownership.

Co-authored-by: Codex <codex@openai.com>
2026-07-22 05:23:46 +02:00
Erik
733126a272 refactor(render): extract frame presentation diagnostics 2026-07-22 05:02:31 +02:00
Erik
7e4cfb37c3 refactor(render): establish frame orchestration contract 2026-07-22 04:18:31 +02:00
Erik
947c61d2d7 refactor(camera): extract the update frame
Move fly/chase publication, combat target tracking, and local player projection behind typed runtime owners. Preserve the inbound-created projection/reconcile barrier while removing GameWindow callbacks and duplicate shadow helpers.
2026-07-22 03:08:58 +02:00
Erik
eeb0f6b45c refactor(runtime): extract local teleport and player mode
Move local teleport, player-mode, animation, shadow, and sealed-dungeon lifetimes out of GameWindow. Make player-mode entry transactional and isolate approach completions by controller lifetime and approach generation so stale callbacks cannot affect replacements.

Co-authored-by: Codex <noreply@openai.com>
2026-07-22 02:50:15 +02:00
Erik
4e4aac2c5a refactor(runtime): extract the live object frame 2026-07-22 00:42:26 +02:00
Erik
ea0da8c8ae refactor(streaming): compose landblock presentation transaction
Own render, physics, DAT-static presentation, and retained static-resource rebinds behind retryable receipts so a failed publication resumes its exact unfinished suffix without replaying retail create-time defaults.

Co-authored-by: Codex <noreply@openai.com>
2026-07-21 21:45:16 +02:00
Erik
fe5514967c refactor(world): extract live appearance and parenting
Move ObjDesc, Parent, Pickup, child-ready, and retained projection recovery into LiveEntityHydrationController while preserving retail timestamp and leave-world semantics. Pin ready publication to exact projection authority and restore attached descendant chains synchronously.
2026-07-21 18:10:07 +02:00
Erik
d10c5f2d54 refactor(world): extract live entity hydration 2026-07-21 17:32:45 +02:00
Erik
69a2ca0c6d refactor(world): extract live projection mechanics
Move appearance rebinding, collision construction, default-pose resolution, local shadow ownership, and exact leave-world presentation into focused owners. Preserve retail parent ordering with staged validation, committed recovery, recursive attached-subtree withdrawal, and retryable exact teardown across parent, pickup, position, unwield, and pose-loss edges.

Co-Authored-By: OpenAI Codex <codex@openai.com>
2026-07-21 16:17:03 +02:00
Erik
fcb66198fc refactor(world): canonicalize live physics host ownership 2026-07-21 14:05:34 +02:00
Erik
5882b308c1 refactor(physics): extract remote motion runtime 2026-07-21 13:17:58 +02:00
Erik
4f31a5085f refactor(net): converge live session state reset 2026-07-21 12:00:48 +02:00
Erik
833520253a fix(animation): harden presentation phase handoffs
Own every borrowed PartArray frame across callbacks, version presentation state across appearance rebinding, reject recursive phase consumption, and preserve static MotionDone when only a stale visual pose is discarded.
2026-07-21 10:01:25 +02:00
Erik
a2fd61684a fix(animation): retain trailing PartArray poses
Port CPartArray::UpdateParts minimum-count behavior: preserve the prior visual and rigid transforms for setup parts absent from a short authored AnimFrame, including the initial hydrated rest pose.

Co-Authored-By: OpenAI Codex <codex@openai.com>
2026-07-21 09:41:15 +02:00
Erik
f004ac5562 refactor(animation): extract live PartArray presenter
Move final live part composition, exact-incarnation schedule handoff, MotionDone binding, and presentation diagnostics out of GameWindow while preserving the retail frame order. Reject stale schedule and completion ABA at the new owner boundary.

Co-Authored-By: OpenAI Codex <codex@openai.com>
2026-07-21 09:37:02 +02:00
Erik
047a4c83b5 fix(interaction): bind selection to live incarnations
Carry local WorldEntity identity through render hits, lighting pulses, and deferred movement actions so GUID reuse cannot target a replacement. Reset all session-owned selection and ItemHolder state and prevent combat auto-target during teardown.
2026-07-21 07:07:58 +02:00
Erik
f961d70023 feat(physics): port retail complete object frame pipeline
Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates.

Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-20 09:10:31 +02:00
Erik
c5ab99081c fix(motion): restore retail object manager order
Process animation completion at the retail process_hooks boundary, then run targeting, movement, PartArray completion, and PositionManager in the named UpdateObjectInternal order for local, remote, hidden, and position-less animated objects. Retire TS-42 with deterministic conformance coverage.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
2026-07-19 21:40:14 +02:00
Erik
3718e341be fix #225: stabilize render pacing and frame CPU
Replace scheduler-quantized software sleeps with a reusable Windows high-resolution deadline timer, expose pacing in the frame profiler, and make shutdown wake every persistent mesh worker without losing the shared signal.

Preserve retail alpha order while using a stable radix, skip duplicate deferred-alpha SSBO packing, pack light sets, cache static selection descriptors, and retire historical material groups at the whole-frame boundary. The fixed dense-Caul sample improved from roughly 9-12 ms CPU to 5.3-6.2 ms without reducing visual quality.

Release build succeeds with zero warnings and all 6,300 tests pass with five intentional skips. Three independent retail, architecture, and adversarial reviews are clean; the post-review connected route remains pending because local ACE is offline.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-19 06:29:30 +02:00
Erik
749e8ceeb1 fix(rendering): bound portal resource lifetime
Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
2026-07-18 21:35:16 +02:00
Erik
3971997689 fix(streaming): retire stale portal-region entities
Port retail's 25-second leave-visibility lifetime over canonical live records, retaining spatially resident and owned entities while using the conservative ACE visibility envelope for nonresident records. Route expiry through the normal generation-safe F747 teardown so animations, effects, physics, and render owners unwind symmetrically.

Replace the append-only modern mesh buffer with coalescing vertex/index ranges and upload each mesh's vertices once instead of once per material. Released zero-reference meshes can now reuse GPU ranges after portal and cache churn.

A connected five-region round trip returned animation ownership to baseline, recreated the starting region on revisit, and held normal FPS. Release build succeeds and all 5,927 tests pass with five intentional skips.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-18 10:02:15 +02:00
Erik
2cbf34a668 perf(rendering): preserve alpha order with bindless particles
Carry each billboard particle's resident texture handle in the instance ABI so stable retail alpha ordering can batch mixed textures without rebinding and redrawing each short texture run. Keep DAT blend transitions as the only billboard draw boundary.

The connected 0xC95B stress scene improved from 6 FPS / 171 ms to about 153 FPS / 6.6 ms at the same roughly 21,000 visible entities. Release build succeeds and all 5,916 tests pass with five intentional skips.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-18 08:32:21 +02:00
Erik
6b0472ee32 fix(rendering): port retail shared alpha list
Queue translucent world GfxObj batches and scene particles in one stable far-to-near stream using transformed DAT sort centers, then drain it at retail's landscape and final-world boundaries. Preserve authored blend, cull, lighting, opacity, and adjacent-only batching so particles behind lifestones are composited through the crystal instead of overpainting it.

Release build succeeds and all 5,914 tests pass with five intentional skips.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-18 08:14:05 +02:00
Erik
ec1bb19609 feat(selection): port retail offscreen target indicator 2026-07-18 07:41:12 +02:00
Erik
ea4f52ec51 fix(selection): port SmartBox click lighting pulse
Port the retail high/low material-lighting cadence for successful world clicks, keep it instance-scoped in the modern renderer, and restore authored lighting after 0.8 seconds. Correct the selection oracle and pin timing plus per-frame buffer lifecycle with tests.

Co-authored-by: Codex <codex@openai.com>
2026-07-17 23:00:04 +02:00
Erik
146a963aeb feat(selection): port retail polygon picking and vivid marker
Replace the projected Setup-sphere rectangle and independent physics-wall ray with retail's render-coupled picker: only visible server-object parts participate, each exact drawing sphere broad-phases the camera-eye ray, and first-in-DAT-order visual polygon hits globally outrank sphere fallbacks.

Replace the devtools-only procedural triangles with the retained gameplay VividTargetIndicator using retail client-enum surfaces 1..4, radar-blip colorization, Setup selection-sphere framing, and the exact eight-pixel viewport clamp.

Release build succeeds with zero warnings and all 5,886 tests pass with five intentional skips.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-17 21:32:51 +02:00
Erik
f1ba147ac5 perf(vfx): port retail particle visibility degradation
Resolve DAT-authored particle ranges from the hardware GfxObj, apply retail distance and completed-cell visibility gates, and preserve the exact finite/infinite off-view update semantics. This removes dense-world simulation work without shortening terrain, entity, fog, or streaming distance.

Publish doorway-clipped outdoor cells through a focused frame controller, retain effect cell identity for outdoor statics, reject hidden emitters before particle-slot scans, and offer an explicit opt-in Extended particle range.

Release build succeeds and all 5,857 tests pass with five intentional skips. Retail-conformance, architecture, and adversarial review cycles are clean; connected Aerlinthe visual/performance gate pending.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-17 15:27:36 +02:00
Erik
124e046976 fix(runtime): align portal and movement presentation
Port retail portal viewport projection and reveal behavior, preserve outbound combat style, drive remote and local grounded movement from authored CSequence root frames, and reuse the local prepared pose so animation hooks advance once.

User-verified portal, observer movement, combat stance, and short-tap locomotion gates. Release build passed with 5,767 tests and five intentional skips.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-17 08:48:27 +02:00
Erik
e95f55f25b fix(portal): synchronize destination presentation state 2026-07-16 21:17:13 +02:00
Erik
4b1bceefbb fix(world): preserve portal materialization effects
Route accepted Hidden and UnHide transitions through retail's internal typed-script path so their DAT scripts queue while the owner is cell-less and resume at the destination. Consume ACE's successful teleport control statuses silently, matching retail HandleFailureEvent.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-16 08:14:48 +02:00
Erik
dded9e6b17 fix(world): remove non-retail portal exit fade 2026-07-15 23:20:52 +02:00
Erik
842bd89c16 fix(world): match retail portal passage and exit warp 2026-07-15 22:14:21 +02:00
Erik
eab23cbdd1 feat(world): port retail portal-space viewport
Replace the opaque teleport cover with the DAT-authored CreatureMode tunnel, exact retail camera/light/animation timing and easing, and animation-hook audio routing. Compose portal and fade passes below retained UI so gameplay windows and input remain active through F751 travel.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-15 21:59:23 +02:00
Erik
b26f84cc69 fix(ui): restore radar, retail wield switching, and protection meshes
Late-bind radar snapshots to the canonical LiveEntityRuntime maps so the retained radar survives bootstrap and session replacement instead of capturing empty sentinels.

Route paperdoll drops through the retail AutoWield blocker transaction. Move conflicting held weapons, shields, explicit jewelry destinations, and mismatched ammo to the backpack one authoritative confirmation at a time; preserve compatible arrows when switching to melee.

Render mode-1 and no-degrade particle GfxObjs as their authored modern-pipeline meshes, retain Always2D billboards, interleave both paths back-to-front, balance emitter mesh ownership, and fail safely on corrupt DAT material metadata. This restores the closed apex on Armor Self/protection effects.

Retain Studio fixture controller lifetimes, add installed-DAT and adversarial regression coverage, synchronize retail research/divergence bookkeeping, and pass all three review tracks plus the full Release suite.

Co-Authored-By: Codex <noreply@openai.com>
2026-07-14 20:52:45 +02:00
Erik
8d63e5c28a test(vfx): harden live missile and effect lifetimes
Add deterministic 96-owner lifecycle and renderer-resource stress gates plus an exact twelve-cycle recall/portal ownership gate. Prove zero retained records, projectiles, spatial buckets, script queues, particle/light owners, shadows, pending effects, and mesh references after churn, GUID reuse, deletion, and session reset.

Make logical teardown incarnation-specific and reentrancy-safe with lifetime epochs, atomic resource registration, generation-aware effect/teleport cleanup, projection mutation tokens, and failure-isolated visibility fan-out. Finish canonical spatial transactions before reporting observer failures and never discard superseded cleanup failures.

Synchronize architecture, roadmap, milestones, retail research, divergence bookkeeping, and durable memory. All three independent review tracks are clean; Release build and the full 5,454-pass/5-skip suite are green.

Co-Authored-By: Codex <noreply@openai.com>
2026-07-14 15:47:00 +02:00
Erik
1e98d81448 feat(vfx): port retail hidden and teleport presentation
Preserve canonical live-object ownership across Hidden transitions and remote teleport placement so effects, collision, streaming, and targeting remain synchronized.
2026-07-14 14:59:48 +02:00
Erik
542dcfc384 feat(vfx): bind effects to live animated poses 2026-07-14 10:56:01 +02:00
Erik
96ddfdf175 feat(vfx): port retail effect scheduling and delivery 2026-07-14 09:25:44 +02:00
Erik
363e046112 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>
2026-07-14 08:17:44 +02:00
Erik
bb5acab9e6 fix(rendering): restore retail terrain texture tiling
Carry TerrainTex.TexTiling through the terrain atlas and upload a layer-indexed table to the modern bindless shader so base, overlay, and road textures repeat at retail scale. Keep alpha masks cell-scaled and preserve retail's verified source-level-zero high-detail selection.

Add the named-retail pseudocode, WorldBuilder/ACE/ACME cross-reference, adapter conformance tests, and inventory documentation so a future renderer migration keeps the contract.

Co-Authored-By: Codex <noreply@openai.com>
2026-07-13 21:04:22 +02:00
Erik
b0c175afc0 fix(streaming): commit EnvCell landblocks atomically (#214)
Carry one complete cell payload with each streaming completion and publish visibility, physics, and render state on the render thread. Remove the obsolete post-readiness login reload that triggered a duplicate dungeon build.

Co-Authored-By: Codex <noreply@openai.com>
2026-07-13 18:45:24 +02:00
Erik
8be933fc94 fix(animation): preserve wire motion on every spawn path
Replace the door-specific static-animation seed with one retail description-then-enter-world initializer shared by normal and reactive spawns. This preserves authoritative Dead and On/Off states, prevents replacement corpses from returning to Ready, and pins the lifecycle with Core and App tests.

Co-Authored-By: Codex <codex@openai.com>
2026-07-12 14:27:44 +02:00
Erik
5276a83087 fix(combat): match retail live attack feedback
Keep AttackDone control statuses out of chat, preserve dead motion across zero-velocity position updates, render the authored power meter from right to left, and retain the rotatable viewer offset during target tracking.

Co-Authored-By: Codex <codex@openai.com>
2026-07-11 21:10:01 +02:00
Erik
33cc9aa16a fix(combat): track targets with retail camera
Port ClientCombatSystem's Keep in View consumer through CameraSet's pivot-to-target boom heading, including the transformed half-metre target offset and melee/missile validity gates. Preserve the existing stateful chase damping and collision architecture, with conformance coverage and named-retail pseudocode.

Co-Authored-By: Codex <codex@openai.com>
2026-07-11 20:36:58 +02:00
Erik
c7607f019c fix #203: preserve animation on appearance updates 2026-07-11 00:25:29 +02:00
Erik
119a2326be fix(#193): clear InstanceGroup.Opacities each frame — stop the ~1 GB/min LOH leak
Root cause (measured, not guessed): the OOM was a MANAGED Large-Object-Heap leak
(~5 GB in 6 min of roaming; working set 1.6 GB -> 7.6 GB), dominated by ~3.8 GB of
live System.Single[]. A heap-retention analysis of a captured gcdump traced 241 of
242 giant float arrays to a single List<float>: WbDrawDispatcher.InstanceGroup.Opacities.

#188 (2026-07-09, fading doors) added Opacities as a 5th per-instance parallel list
alongside Matrices/Slots/LightSets/IndoorFlags, but left it out of the per-frame
clear loop (WbDrawDispatcher.cs:959). So Opacities.Add(1.0f) fired once per drawn
instance per frame and the list never reset — growing forever; List<float> capacity
doubling produced the observed ~128 MB / ~512 MB power-of-two arrays and OOM after
~50 min. Hit both UI builds because it's core render, and started only recently
because Opacities did not exist before #188.

Fix: extract the per-frame reset into InstanceGroup.ClearPerInstanceData() (promoted
InstanceGroup private->internal) that clears ALL FIVE parallel lists in one place, so
a future 6th list can't silently drift out of the frame lifecycle again. Line 959 now
calls it. TDD: InstanceGroupClearTests asserts every parallel list resets (RED with
the old 4-list clear, GREEN after adding Opacities.Clear()).

Static-audit note: this is exactly why we measured before fixing — the audit ranked
GlobalMeshBuffer (GPU) #1 and the entity-dict leak #3; both were wrong about the
dominant cause. The dotnet-counters (managed vs native) + gcdump retention graph
named the real one-line culprit.

Secondary (NOT this commit): _groups dictionary is never pruned (bounded by distinct
group keys, empty groups are cheap) — filed as a follow-up, not the multi-GB driver.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 14:15:10 +02:00
Erik
8257b9ba10 fix(#186): render side-cull mis-sided thin connectors — use dat PortalSide bit, not AABB centroid
The indoor GREY flap at a top-floor connecting room. The render portal side-cull
reconstructed each doorway's "interior side" (PortalClipPlane.InsideSide) from the
cell's AABB CENTROID. For a THIN connector cell (0xF6820118, 5 render polys), the
bounding-box center falls on the WRONG side of the 0118->0116 doorway, so the eye
read as a back-portal and the forward room 0116 was culled -> the aperture showed
the fog clear color = grey.

Retail's PView::InitCell (0x005a4b70) and acdream's own PHYSICS path
(CellTransit.cs:190) both read the explicit dat PortalSide bit ((Flags&2)==0)
instead of guessing from geometry. Port the render path (GameWindow.BuildLoadedCell)
to the same bit.

Proven by a live retail cdb trace (retail draws 0116 from the 0118 root at the grey
pose; tools/cdb/issue186-connector-decider.cdb) + an offline dat diagnostic
(Issue186...PortalSide_CentroidVsDatBit_AtGreyEye): the dat bit matches the old
centroid on every portal of these cells EXCEPT the one #186 breaks, so the switch is
surgical. Full regression green (App 741 / Core 2631); the CornerFlood + Issue113
dat-loading helpers updated to the same bit confirm every real Holtburg/tower/hall
cell floods identically. Touches neither PortalSideEpsilon nor the deleted
EyeInsidePortalOpening rescue (the two DO-NOT-RETRY traps).

Live-gated: user-confirmed no grey at any camera angle; probe shows 216 root=0118
frames, 0 still grey (0118->0116 now TRV, vis=4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 15:06:33 +02:00
Erik
554af5042d park #177: retail cdb trace DISPROVES the portal-flood theory
Attached cdb to live retail (PDB MATCH), broke on PView::DrawCells (0x005a4840),
dumped cell_draw_num + cell_draw_list cell ids + the eye
(Render::FrameCurrent->viewer.viewpoint) while descending the Facility Hub spiral.
Retail's flood is dynamic and IDENTICAL in character to ours: from the spiral cells
it swings num 3->27 with gaze and collapses to 3 cells at many poses (cam=015d ->
{015d 015e 015f}). Our flood does the same (3->43). So retail does NOT keep the
staircase where we drop it -- the flood is exonerated as the cause.

Session trail (all in ISSUES #177): ruled out lighting, membership, camera coherence,
the collision sweep, the 0178/0182/0183 handoff cells, and edge-on eye-in-opening
(fix#1 shipped -> visual-gate-failed -> reverted, PortalVisibilityBuilder + AP-86 both
restored exactly). Freshest un-chased lead: the steps are STATIC objects (GfxObj
0x010000DE x6/cell) drawn via the viewcone cull, not cell shell.

Adds: Issue177StairDescentCameraFloodTests (real-camera+flood + composition +
flood-depth characterization pins) and a reusable retail-cdb capture toolchain
(tools/cdb/pview-verify.cdb, pview-spiral2.cdb with the correct top-level-qd detach --
qd in a CONDITIONAL bp action does NOT fire and strands cdb attached).

No production code change (fix#1 reverted). PARKED per user; M1.5 critical path next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 08:43:21 +02:00