Complete Slice D4 by adding aggregate lifecycle occupancy and traffic facts, validating physical source reports, and including decoded audio under the typed startup budget. Exercise every domain under forced pressure and retain the real cache/fence convergence gates.
Complete Slice D3 by replacing the unbounded animation dictionary with a concurrent byte/count LRU and by putting the three retail alpha scratch owners behind one typed aggregate budget. Preserve immediate growth and draw order while reclaiming one-frame density spikes after sustained under-use. Close stale bounds-cache issue evidence without inventing a cache.
Define Slice D's transition and concurrency contract, add generation-safe asset handles and owner leases, and parse the existing cache ceilings through typed runtime budgets. Stale generations cannot release or revive replacements, and worker observations are bounded and coalesced.
Co-authored-by: Codex <noreply@openai.com>
Introduce explicit loaded, missing, and corrupt outcomes over typed pak keys, validate package/DAT identity at open, add zero-I/O TOC probes, preserve cancellation, and route the package path through RuntimeOptions. Production injection follows in the next checkpoint.
Starting dotnet-counters in the process-creation race can suspend the CLR before the graphical host creates its window. Wait for the guaranteed in-world boundary, then attach counters and contention tracing during the route's warm-up interval.
Co-Authored-By: Codex <noreply@openai.com>
Free-running sleeps could label a sample as the old scene exactly when automation began the next teleport. Give every route stop an explicit post-input liveness dwell and post-checkpoint hold, then capture process and frame facts only after its named canonical barrier.
Co-Authored-By: Codex <noreply@openai.com>
The first-player-position line is emitted only when initial streaming must recenter. Treating it as a required login event made a valid same-landblock login time out while route automation was already running. Gate on in-world and the route's authoritative materialization barriers instead.
Co-Authored-By: Codex <noreply@openai.com>
Correct whole-frame GPU timestamps so they bracket only the accepted render transaction and associate delayed query results with the owning CPU frame. Add route-wide frame-history summaries, fixed-camera screenshot comparison, process counters, contention traces, a pinned Arwic workload, and credential-safe launch disclosure.
The reference hardware/display contract now keeps local and RDP populations separate and defines the screenshot and re-baseline rules needed by later prepared-content gates.
Co-Authored-By: Codex <noreply@openai.com>
An adversarial performance review found our own instruments cannot
measure the project's own performance gates:
- FrameProfiler aggregated CPU/GPU/alloc/stage samples into ~5-second
windows and reset the ring buffers after each report, so route-wide
p50/p95/p99 distributions across a whole soak could not be
reconstructed after the fact. ACDREAM_FRAME_HISTORY=<path> now opts
into a separate per-frame history (one record per frame, ~72
bytes/record, accumulated in memory with zero frame-thread I/O) that
a shutdown-only Dispose() writes as CSV. The aggregated [frame-prof]
report format and its existing metrics are unchanged.
- The canonical checkpoint JSON tracked cache residency (entry/byte
counts) but never LOH size/fragmentation, process-wide allocated
bytes, or cache hit/miss/eviction traffic — a committed audit JSON
showed 65% LOH fragmentation that no tracked instrument recorded,
and "does a revisit portal hit or miss the caches" was unanswerable
from an artifact alone. WorldLifecycleResourceSnapshot now carries
loh_size_bytes/loh_fragmentation_bytes (GCMemoryInfo.GenerationInfo
index 3), process_total_allocated_bytes (GC.GetTotalAllocatedBytes),
and Interlocked hit/miss/eviction counters for the CPU mesh cache,
decoded-texture cache, and the four bounded DAT-object caches
(portal/cell/highRes/language, aggregated).
- run-connected-r6-soak.ps1 unconditionally forced
ACDREAM_UNCAPPED_RENDER=1 with no capped mode, while its sibling
lifecycle-gate script correctly gated it behind a switch. Added
-Uncapped (default capped, matching the sibling script's pattern),
fixed the stationary dwell (12s -> 26s, past the 25s
LiveEntityLivenessController deadline the adjacent comment already
cited), and now write an env-disclosure.json into the automation
artifact directory before every launch listing every ACDREAM_* var
the script sets plus -Uncapped, since the prior audit could only see
ACDREAM_DUMP_MOVE_TRUTH and nothing else was ever recorded anywhere.
Cache counters are wired via the existing composition path
(ObjectMeshManager already owns the CPU mesh cache and the mesh
extractor directly; content.Dats is threaded into
WorldLifecycleResourceSnapshotSource the same way every other
composition consumer receives it). The DAT-object cache lives behind
IDatReaderWriter, a third-party interface from the DatReaderWriter
package that cannot be extended; RuntimeDatCollection (the one
production implementation) exposes the aggregate stats directly and a
pattern match reads them, degrading to zero for any test double —
no new static registry was introduced (GpuMemoryTracker remains the
one precedented process-wide static).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 1da2c33c875b41fa383dd79694ee2765f0e21896)
Two audit-verified caches grew monotonically for process lifetime, which is
fatal for the 30-bot long-uptime headless-fleet goal:
- DatSoundCache._waves (Core) memoized every decoded PCM WaveData forever
in a bare ConcurrentDictionary — no LRU, no byte budget.
- OpenAlAudioEngine._bufferByWaveId (App) retained a native OpenAL buffer
copy of the same PCM per wave id until engine disposal — a second,
independent unbounded cache.
DatSoundCache now bounds payload residency with a 32 MiB byte-budget LRU
(decoded PCM waves run ~100-500 KB each, so this holds a comfortable
working set). Missing/unsupported-format waves are memoized separately in
an unbounded-but-cheap negative-result set (bounded by the finite Wave dat
id space) so they can never compete with or get evicted alongside payload
entries. Concurrent first-touch decodes of the same wave id are deduped
via a shared Lazy<T> so racing callers don't pay for WaveDecoder.Decode
twice. AcDream.Core cannot reference AcDream.Content (code-structure rule
2), so the LRU is a local reimplementation mirroring
BoundedDatObjectCache/DecodedTextureCache's shape rather than a shared
dependency.
OpenAlAudioEngine._bufferByWaveId now bounds native buffer residency with
a 48 MiB byte-budget LRU (AlBufferBudgetTracker), evicting least-recently-
used buffers once oversized. alDeleteBuffers fails on a buffer still
attached to a source, so eviction queries live AL per-source state
(GetSourceInteger.Buffer) rather than tracking a second, easily-stale
copy — several call sites (Play3DWave, PlayUiWave) set a source's buffer
directly. The buffer EnsureBuffer just created is explicitly protected
from its own eviction pass, since the caller hasn't attached it to a
source yet at that point. Evicted waves simply replay through
DatSoundCache -> EnsureBuffer on next use, identical to a first play.
Verified before implementing: AudioHookSink is the only GetWave caller
(single per-frame render-thread path per AnimationHookRouter's own
threading doc), and PcmBytes is read only at DatSoundCache.Admit (byte
accounting) and EnsureBuffer's first-upload branch — confirmed dead after
AL upload on the steady-state replay path, so bounding either cache
independently is correctness-safe; a cold replay after both evict simply
falls back to a full re-decode + re-upload, identical to a first play.
AlBufferBudgetTracker's eviction/budget decision is extracted as pure
logic (no AL dependency) specifically so it's unit-testable: the existing
OpenAlResourceLifetimeTests fake exposes a null AL, which short-circuits
every native buffer call before it runs, so the engine's actual AL wiring
isn't testable headless.
Tests: 9 new DatSoundCacheTests (Core.Tests) covering eviction order, byte
accounting, negative-result memoization, oversize-single-entry handling,
and concurrent-access smoke tests; 10 new AlBufferBudgetTrackerTests
(App.Tests) covering the pure LRU/budget/protection logic. Full suite:
3214/2 skip (Core.Tests), 3471/3 skip (App.Tests) plus one pre-existing,
unrelated failure (LandblockBuildOriginTests.FarLoad_..., reproduces
identically with these changes stashed out — landblock streaming, not
audio).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 76c880d35bcf30b50e3f7b4cb8635dc9ab9e3ec7)
Three small verified MeshExtractor.cs fixes from an allocation audit:
- CellStruct clip-map clone guard: the CellStruct polygon-surface path
cloned decoded texture data unconditionally before applying clip-map
transparency, even though the comment above it says "if we got this
from the cache, we need to clone it." The GfxObj path already had the
correct pattern (clone only when textureDataIsCached). Wired the same
textureDataIsCached flag through CellStruct's TryGet/GetOrCreate/
RetainOrUse call sites and gated the clone on it, matching GfxObj
exactly — freshly-decoded arrays (the common case) now skip a
redundant clone + BlockCopy.
- Solid-color texture cache: isSolid surfaces (Base1Solid / NoPos-
stippled polys) allocated a fresh 32x32 RGBA array (4 KiB) on every
polygon, even for repeated colors. Added a MeshExtractor-scoped
ConcurrentDictionary<uint, byte[]> keyed by packed ARGB, bounded by
distinct colors observed (a few hundred at most) rather than call
volume. Traced every downstream consumer of the solid-color array
(clip-map clone-guard, translucency clone-guard, GL upload, pak
serialization) — none currently mutate a solid-color array in place,
since isSolid is mutually exclusive with the texture-decode branch
those guards live in — but wired textureDataIsCached = true at both
call sites anyway so the existing clone-before-mutate machinery
protects the shared array if that ever changes.
- Dropped the redundant .ToList() on both _dats.ResolveId(...) calls:
DatCollectionAdapter.ResolveId already returns a materialized List<T>
typed as IEnumerable; the immediate .OrderByDescending().FirstOrDefault()
enumerates once, so the extra copy was pure waste.
Added SolidColorTextureCacheTests (dat-gated, matching suite convention)
exercising the new cache directly via internal visibility.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit e9f9d7539966cc26343e45d653a751c5d1045810)
WorldSession.NetReceiveLoop wrapped its entire while loop in a single
try/catch, so ANY non-timeout SocketException permanently killed the
background receive thread: the catch block at the loop's end was empty
(misattributed the error to "socket closed during shutdown"), and the
finally called _inboundQueue.Writer.TryComplete(), which silently and
irrecoverably stopped all inbound processing for the rest of the
session — no log line, no recovery path, and LiveSessionHost.Reconnect
has zero production callers to notice.
The realistic trigger is a well-known Windows UdpClient quirk: an ICMP
"port unreachable" reply to an EARLIER Send (e.g. against a stale ACE
session that already tore down its socket) surfaces as a
WSAECONNRESET SocketException on this socket's NEXT, completely
unrelated Receive call. NetClient.Receive already swallows the
expected SocketError.TimedOut heartbeat case; anything else reaching
WorldSession was a real, transient, per-datagram error being treated
as session-fatal.
Three changes, root-cause not a band-aid:
- NetClient's constructor now disables SIO_UDP_CONNRESET reporting on
Windows, so a delayed ICMP error can't poison receives at all.
- NetReceiveLoop now catches SocketException PER ITERATION, logs it,
and continues polling instead of exiting. The existing clean-shutdown
paths (cancellation, ObjectDisposedException during Dispose) are
unchanged — only the non-timeout-socket-error case that used to kill
the loop is now recoverable.
- NetClient.Receive no longer calls the ReceiveTimeout setter (a
setsockopt syscall) on every single call — only when the requested
timeout differs from the last-applied value, cached in a new field.
This was an unrelated but adjacent finding (4x/sec syscall churn at
the 250ms heartbeat cadence) in the same audit.
No change to outbound wire behavior, ack cadence, heartbeat interval,
or datagram ordering — this is purely receive-loop resilience.
Tests: NetClientTests gained a SIO_UDP_CONNRESET construction smoke
test and two ReceiveTimeout-caching tests. A new
WorldSessionNetReceiveLoopResilienceTests drives the actual private
NetReceiveLoop method (via the existing internal
IWorldSessionTransport seam + reflection) with a scripted transport
that throws a non-timeout SocketException on the first call, proving
the loop survives it and keeps enqueueing subsequent datagrams — fully
deterministic, no real sockets. Full solution suite green: 3204/3206
Core, 3462/3465 App, 552/552 Core.Net (skips pre-existing).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit c72ce028a927e15b8a54a86bbd0661a723dc84ca)
PrepareRenderBatches rebuilt the full EnvCell visibility snapshot every
frame any indoor/building root was resolved: a Parallel.ForEach dispatch
over every GpuReady EnvCell landblock (with per-landblock locks), a fresh
outer dictionary plus one fresh inner dictionary per visible cell, a new
snapshot object, and a complete transparency rescan - all while standing
perfectly still. The NeedsPrepare flag existed since Phase A8 as the
intended rebuild gate but was never read by production code.
This wires the gate on the snapshot's actual inputs:
- landblock commits/removals (the existing NeedsPrepare flag),
- the visible-cell filter (content-compared; the caller reuses one
scratch HashSet across frames),
- the trim window (center/radius),
- mesh render-data availability - new
ObjectMeshManager.RenderDataAvailabilityVersion, bumped at publish,
pending-release hide, release completion, and teardown, because the
snapshot bakes per-cell transparency from TryGetRenderData and a
late-arriving transparent shell must reclassify its cell,
- the camera: eye position under a 1 mm ABSOLUTE epsilon (swallows the
documented ~36 um rest jitter, dirties on any real movement; the VP
translation row scales with AC's ~5e4 world coordinates where a
relative tolerance would mask sub-meter motion) plus rows 1-3 of
view*projection (position-independent rotation x projection) under
relative 1e-5.
Skipping is pool-safe: RenderCore re-anchors _poolIndex to the active
snapshot's PostPreparePoolIndex on every call, so consecutive Renders
without an intervening Prepare reuse scratch lists past the snapshot's
owned region exactly as within-frame passes already do. The empty-filter
branch is now also idempotent instead of allocating a fresh empty
snapshot per frame. Render still receives the current frame's
view-projection every frame (the U.4 stale-matrix rule) - only the
snapshot rebuild is gated.
Pixels must be identical; needs the standard user visual pass (dungeon +
town-near-buildings) before the change is considered accepted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Install examination formula icons as the template root UIRegion image, matching retail ClearImage/SetImage behavior while retaining the authored missing-component overlay. Add the real DAT template fixture and conformance coverage.
Co-authored-by: Codex <codex@openai.com>
Resolve spell-examination component cells through their DAT icon DIDs, project scarab and prismatic-taper formulas when ACE disables component enforcement, and version authored window geometry so stale examination sizes reset once without losing user layout behavior.
Co-authored-by: Codex <codex@openai.com>
Port UIElement_ListBox's press-time selection ordering through the shared retained item-list contract. Inventory, loot, paperdoll, and physical shortcuts now update canonical selection before release or drag promotion, while target-mode consumption suppresses drag and release-time activation.
Co-authored-by: Codex <codex@openai.com>
Port UIElement_ItemList's physical-item right-click branch through the shared retained list. Select and appraise backpack, loot, paperdoll, and shortcut items through their canonical owners, while preventing RMB movement from lifting items or issuing appraisal requests.
Co-authored-by: Codex <codex@openai.com>
Port SmartBox's release-completed sr_Examine gesture through the configurable SelectRight binding. Cancel camera drags at the shared retail three-pixel threshold, then reuse the canonical picker, selection pulse, and appraisal request path without inventing another wire or UI owner.
Co-authored-by: Codex <codex@openai.com>
Carry PublicWeenieDesc material type into the live object model so examination titles use the DAT-authored material prefix. Preserve retail AddItemInfo empty appends and embedded armor separator, restoring the deliberate blank rows between appraisal sections.
Co-authored-by: Codex <codex@openai.com>
Follow ItemExamineUI's EoR dispatch and wording for item assessment instead of the generic projection. Resolve material and creature names through installed DAT maps, cover specialized item branches and item-XP curves, and narrow AP-110 to the remaining live/localized preview seams.
Mirror PublicWeenieDesc::UnPack MOVSX behavior so ACE's FF capacity sentinels remain -1 instead of becoming 255. This suppresses non-container capacity prose through the normal retail appraisal checks, with raw-wire and formatter regression coverage.
Co-authored-by: Codex <codex@openai.com>
Preserve PublicWeenieDesc hook identity from CreateObject through the item model so hook appraisals suppress sentinel capacities exactly. Use appraisal-only Value and Burden presence, retain AddItemInfo paragraph and authored font-color selection, and port retail lock, page, enchantment, and spell-block formatting.
Co-authored-by: Codex <codex@openai.com>
Restore the authored examination geometry and top-origin item list, then port retail's ordered weapon, armor, magic, requirement, capacity, cooldown, use, and description branches into a dedicated formatter with DAT spell prose. Keep the remaining specialized display-name and preview gaps explicit in AP-110.
Co-authored-by: Codex <codex@openai.com>
Port the separate creature rating list, layer the animated preview between authored row chrome and text, and follow selection while the examination floaty is visible. Preserve the remaining item-preview and font-state gaps in AP-110.
Co-authored-by: Codex <codex@openai.com>
Render assessed creatures through the shared private viewport with retail heading, bounding-box camera, and light. Build the exact authored nine-row stat list and resolve creature names from the retail EnumMapper while keeping remaining font/sequencer adaptations explicit.
Co-authored-by: Codex <codex@openai.com>
Preserve retail's one-pending-appraisal busy lifetime, parse the complete gated response, and mount the authored examination layout in the shared main-panel host. Keep known 3D preview and inscription-write gaps explicit in AP-110.
Preserve public shared-cooldown metadata, resolve the authoritative cooldown enchantment with retail expiry semantics, and project the exact ten DAT-authored radial steps through the shared retained item-slot architecture.
Co-authored-by: Codex <codex@openai.com>
Use the exact low USEABLE_NO-bit predicate so reset/zero-valued direct-use items such as Blackmoor's Favor reach the ordinary Use request. Port AutoWear's clothing-priority blocker lookup and exact named system notice through the shared activation owner.
Co-authored-by: Codex <codex@openai.com>
Port the retail selected-object availability predicate into Core and project it through the shared interaction owner. The imported hand now follows canonical selection/object notices, keeps weapon and targeted-tool activation on the existing wield/use cursor paths, and ghosts empty or explicitly unusable selections per the connected UX requirement.
Co-authored-by: Codex <codex@openai.com>
Place favorite-bar arrows by their authored sides, import rollover and pressed media through the shared scrollbar, and preserve manual offsets across passive refreshes. Carry the mixed-parent DAT anchor chain to a fixed 18-cell favorite viewport so overflow controls and the Cast button remain inside the retail-sized combat frame.
Co-authored-by: Codex <codex@openai.com>
Import the retail arrow-only spell bar scrollbar from LayoutDesc, preserve authored end-button extents and HideDisabled behavior in the shared retained widget, and bind each favorite list to its sole horizontal pixel-scroll model. Match retail selection exposure for off-screen spells and pin the behavior with real-fixture conformance tests.
Document the six-slice world-interaction completion program as the active pre-M4 work order.
Co-authored-by: Codex <codex@openai.com>
Preserve the fixed per-cell collision walk across nested registry mutations while replacing the repeated List allocation with an explicitly owned pooled snapshot. Capture cardinality once, return storage on every exit, and pin live-list mutation behavior with a focused regression test.
Co-authored-by: Codex <codex@openai.com>
Preserve prepublication local motion completion, require the PartArray enter-world lifecycle port, and balance deferred Use busy ownership across dispatch and cancellation. Reconcile the completed GameWindow connected gates and add regression coverage.
Co-authored-by: Codex <codex@openai.com>
Pin every ACE teleloc to an explicit starting quaternion and distinguish stable-workload owner growth from legitimate authoritative population, visibility, and cache-retirement changes. Preserve the original process residency and frame-cost limits while making canonical deltas actionable.
Co-authored-by: Codex <codex@openai.com>
Make scripted lifecycle checkpoints acknowledged post-diagnostics render barriers, capture the exact frame outcome beside canonical resource ownership, and harden the nine-stop route with ordered same-location cache and lifetime gates without weakening process residency thresholds.
Co-authored-by: Codex <codex@openai.com>