Captures everything learned from a long worktree iteration on the foreground-rain bug (ISSUES.md #1 / #26) plus a new star-rendering bug observed in the same area. The code work from that worktree (WeatherDispatcher, EmitterDescLoader.LoadFromDat, WeatherCellRenderer, GameWindow integration) was reverted because it didn't visibly fix the rain bug — but the research findings + diagnostic tools are durable and should not have to be rediscovered. What's added: - docs/research/2026-04-26-sky-investigation-handoff.md Comprehensive seed prompt for the next session. Covers: * Bug A: foreground rain (#26) — what's open, what's confirmed, what's been tried * Bug B: stars rendering as square in corner (NEW, user-observed) * 40-agent decomp scan findings — retail rain is NOT camera- particles, NOT server-driven, NOT screen-space; the mesh IS a hollow octagonal tube; only 5 weather GfxObjs in Dereth * Things ruled out by trial (envelope, scaling, unlit, depth- always alone, Setup loading) * Things to try next (depth+zfar combined, full render-state audit, frame ordering, star UV bug as easier first target) * Acceptance criteria for "done" - docs/research/2026-04-26-chorizite-pr-draft.md Upstream PR draft for Chorizite/DatReaderWriter. Five generated DBObj source files reference nonexistent enum values and are silently excluded from the NuGet build: ParticleEmitterInfo, Clothing, PaletteSet, DataIdMapper, DualDataIdMapper. Fix: delete the duplicates. Independent of the rain work — benefits the AC modding ecosystem broadly. - docs/research/2026-04-26-datreaderwriter-reference.md Developer reference for our DatReaderWriter usage. Version, types we consume, known broken types, thread-safety caveats, upgrade procedure, NuGet-vs-vendored decision matrix. - tools/PesChainAudit/ Recursive PES walker — given a 0x33xxxxxx script id, walks all CallPES references and dumps every hook + every referenced ParticleEmitter's parameters. Used to prove no weather PES emits rain particles. - tools/TextureDump/ Dumps texture pixel statistics (alpha histogram, brightness, max) and saves as PNG for visual inspection. - tools/WeatherEnumerator/ Enumerates every DayGroup in a Region, lists weather SkyObjects (Properties & 0x04), dumps GfxObj bounding boxes. - tools/WeatherSetupProbe/ Loads a Setup id, dumps each part's GfxObj + frame + scale + surface. Used to prove weather Setups are 5cm dummy carriers. Worktree feature/sky-fixes is being deleted in a follow-up step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.2 KiB
DatReaderWriter (DRW) — developer reference
Status as of 2026-04-26. Source for "what's safe to call, what's broken, how to upgrade." Update this doc when DRW version bumps or when we discover a new safety/coverage gap.
1. Current version
- Package:
Chorizite.DatReaderWriter - Version:
2.1.7(NuGet) - Pinned in
src/AcDream.Core/AcDream.Core.csprojandsrc/AcDream.Cli/AcDream.Cli.csproj. App + Core both transitively use that one version. - Some
tools/*(WeatherSetupProbe, WeatherEnumerator, SkyObjectInspect, dump-keymap, PesChainAudit)ProjectReferencethe vendored repo directly instead of NuGet — see §7.
2. Source repo
- Origin: https://github.com/Chorizite/DatReaderWriter
- Vendored at:
references/DatReaderWriter/(HEAD63e84b6 "Bump version"— no git tag matching the NuGet version; the repo doesn't tag releases, ship version isGitVersion.yml-driven). - License: MIT, © 2024 ACClientLib (
references/DatReaderWriter/LICENSE.txt). - Targets:
net8.0,netstandard2.0,net48. We consume fromnet10.0projects; thenet8.0TFM resolves cleanly via netstandard forward-compat. - Project layout:
DatReaderWriter/(library),…SourceGenerator/(Roslyn generator emits packing code),…Tests/,…Benchmarks/.
3. Types we use
Top-level entry points:
DatReaderWriter.DatCollection— the four-database container (Portal,Cell,HighRes,Local).DatReaderWriter.Options.DatCollectionOptions(caching mode, paths).DatCollection.Get<T>(id)/Portal.Get<T>(id)— generic typed read.DatCollection.GetAllIdsOfType<T>()— used by CLI enumerators.
DatReaderWriter.DBObjs.* consumed in src/:
LandBlock,LandBlockInfo,EnvCell,RegionGfxObj,Setup,Surface,SurfaceTexture(via SurfaceDecoder)Animation,MotionTableParticleEmitter,PhysicsScriptEnvironment(note: collides withSystem.Environment, must be FQN inAcDream.Cli/Program.cs)Wave/ sound DBObjs (viaDatSoundCache,SoundCookbook)
DatReaderWriter.Types.* consumed:
AnimationFrame,MotionDataPolygon,VertexArray,Sphere(BSPQuery, collision)SoundEntry,AnimationHook,CreateParticleHookBSPNode/BSPTree/PhysicsBSPNode/CellBSPNode/DrawingBSPNode(BSP traversal inBSPQuery,PhysicsDataCache)
DatReaderWriter.Enums.* consumed:
MotionCommand(aliasedDRWMotionCommandin MotionCommandResolver)Sound(aliasedDRWSound)StipplingType, plus various surface/material flags
4. Known broken / missing types
RenderMaterial— not implemented. Per upstream README "Known Issues." We do not consume it.LayoutDesc— README marks it "supported but structure will need cleanup." Treat fields as unstable across versions. We do not depend on it directly.ParticleEmitterInfo— DRW exposes theParticleEmitterDBObj only. There is no separateParticleEmitterInfotype. OurVfx/VfxModel.cscomment that mentions "ParticleEmitterInfo dat" is conceptual (matches the retail header struct name); the actual lookup inEmitterDescLoader.csisdats.Portal.Get<ParticleEmitter>(id)with ausing DatParticleEmitter = DatReaderWriter.DBObjs.ParticleEmitter;alias because we have our ownParticleEmitterruntime class inAcDream.Core.Vfx.- Anything not in
DBObjs/— only 5 DBObj files exist (ActionMap,DBProperties,Iteration,LandBlock,MasterProperty) at the top level of that folder; the rest (GfxObj, Setup, etc.) live elsewhere in the source tree. If a type doesn't resolve, search the DRW source — don't assume it's missing.
5. Unsafe patterns
DatCollection is NOT thread-safe. DatBinReader holds a buffer
position field per database; concurrent Get<T> calls from two threads
corrupt each other and produce silently half-populated payloads
(half-empty LandBlock.Height[] in our case). See
memory/feedback_phase_a1_hotfix_saga.md rule #2 — Phase A.1 burned
~3 hotfixes mis-diagnosing this as something else.
Rules:
- All
DatCollection.Get<T>()calls run on the render thread. LandblockStreamerruns synchronously (see header comment, lines 17–32 ofsrc/AcDream.App/Streaming/LandblockStreamer.cs). The worker-thread design was reverted; the channel-based outbox is retained so the moment a thread-safe wrapper exists we can swap loaders back to async without touching call sites.- Don't claim "DRW is thread-safe per its docs." There are no such docs. The README says nothing about concurrency.
- The async surface area (
*Asyncmethods) is for I/O, not concurrent reads — calling them from two threads is still unsafe.
Caching: DatCollectionOptions caching mode is OnDemand. Don't
flip to None without re-benchmarking — terrain mesh build hits the
same LandBlock repeatedly.
6. How to upgrade
- Bump
Version="2.1.7"insrc/AcDream.Core/AcDream.Core.csprojandsrc/AcDream.Cli/AcDream.Cli.csproj(and anytools/*using the NuGet package, e.g.TextureDump). - Pull
references/DatReaderWriter/to the matching commit so the vendored copy stays in sync (used by tools thatProjectReferenceit directly + as a search target for "what's in DRW today"). dotnet restore && dotnet buildfrom repo root.- Run the full test suite (
dotnet test). Watch for compile breaks on type renames (DRW has historically renamedDBObjtypes between minor versions). - Smoke-test live: launch against the local ACE server and walk Holtburg → Foundry. Watch for half-loaded landblocks (missing heights / null surfaces) — that's the canonical "thread-safety regression slipped in" signature.
Risks per area:
- DBObj field renames — DRW source-generates packing; field renames break every consumer. Grep before bumping.
- Enum value reordering —
MotionCommand,Sound,StipplingTypeare enum-by-name in our code, so adds are safe but removals will break. - BSP type changes —
BSPNodefamily is consumed inBSPQuery.cs(collision). Any signature change there is a high-risk diff that needs the conformance sweep re-run.
7. NuGet vs vendored
- Use NuGet (
Chorizite.DatReaderWriterpackage) for everything shipped insrc/and anytools/that doesn't need DRW internals. This is the default. CurrentlyAcDream.Core,AcDream.Cli, andtools/TextureDump. - Vendor (
ProjectReferencetoreferences/DatReaderWriter/…) when:- The tool needs a DRW type or method that isn't
publicin the NuGet build (some helpers areinternal). - You're prototyping a DRW patch you intend to upstream — edit in place, build, validate, then PR upstream.
- Diagnostic / probe tools (
WeatherSetupProbe,WeatherEnumerator,SkyObjectInspect,dump-keymap,PesChainAudit) where iteration speed against DRW source matters more than ship discipline.
- The tool needs a DRW type or method that isn't
- Never mix within one assembly. A project either NuGet-references
DRW or
ProjectReferences the vendored copy. Both produces type- identity errors at the boundary. - Production code stays on NuGet. When a vendored tool proves a DRW change is needed, upstream it, wait for a release, and bump §6.