diff --git a/docs/research/2026-06-18-item-object-model-handoff.md b/docs/research/2026-06-18-item-object-model-handoff.md new file mode 100644 index 00000000..720d9f8d --- /dev/null +++ b/docs/research/2026-06-18-item-object-model-handoff.md @@ -0,0 +1,93 @@ +# Handoff — the client object/item data model (next phase, post-D.5.2) + +**Date:** 2026-06-18 +**From:** the D.5.2 stateful-icon session (icon system SHIPPED + visually confirmed on a +live Coldeve server). This handoff frames the NEXT phase: the real item/object data model. +**Status of this work:** branch `claude/hopeful-maxwell-214a12` (kept, not merged). D.5.2 is +complete: `52306d9..fb288ad`. + +--- + +## 0. Why this phase exists (the root cause we uncovered) + +Visual-verifying D.5.2 on a live server (character **Barris** on Coldeve) showed **4 of 6 +hotbar items render no icon**. The diagnostic (`icon-dump.txt`, since removed) proved the +cause: those items are **`NOT-ENRICHED`** — `ItemRepository.GetItem(guid)` returns null +because their `CreateObject` was **dropped**. + +The mechanism is acdream's **scaffold item model**: +- `EnrichItem` is **enrich-existing-only**: it updates an item ONLY if it was already seeded + as a stub (from `PlayerDescription` at login). A `CreateObject` for an item with no + pre-existing stub is silently discarded (the toolbar handoff called this out: + *"new-item ingestion is the inventory phase"*). +- So only items in the login seed set get icons; everything else (most pack contents) falls + on the floor. The 2 that showed (Energy Crystal, Revenant's Scythe) are wielded items the + server announces up front. + +This is **NOT a D.5.2 bug** (the icon composite is correct for every item that reaches it — +confirmed: Energy Crystal's Magical gradient tint + the no-mana scroll's black edges both +match retail). It is the **item/object data model** being a placeholder. + +## 1. The retail model to port (the oracle) + +Retail has **one master object table** — `ClientObjMaintSystem` — and **`CreateObject` is the +canonical create/update for every object** (item, creature, player). The UI never owns item +data: a hotbar slot, an inventory cell, a paperdoll slot, a vendor cell all hold a `guid` and +resolve it live via `ClientObjMaintSystem::GetWeenieObject(guid)`. (Confirmed in our spine +research: *"the cell never holds item data — it holds an itemID and resolves it live."*) + +acdream **inverted** this: login snapshot = source of truth, `CreateObject` = second-class +enrich. The fix is to flip it: **`CreateObject` is the authoritative ingestion**; +`PlayerDescription` / `ViewContents (0x0196)` / shortcuts become **references + supplementary +data**, not the primary seed. Every object the server tells us about is tracked; the UI +resolves by guid. + +## 2. THE crux design question (settle this FIRST in the brainstorm) + +acdream currently has **two object tracks**: +- the **WorldEntity** system (3D creatures / players / world items, fed by `CreateObject` → + `GameWindow.OnLiveEntitySpawned` → `WorldEntity`), and +- the **ItemRepository** (inventory items, `src/AcDream.Core/Items/`). + +Retail unifies these under one `ClientObjMaintSystem` (every object is an `ACCWeenieObject`). +**Decision to make:** unify acdream into ONE object table (retail shape), or keep the two +tracks with a shared ingestion seam? This choice drives everything downstream (inventory, +equipment/paperdoll, vendor, trade all resolve items from whatever table wins). Think it +through up front — don't discover it halfway in. + +## 3. Sources that feed the model (the ingestion surface to design around) + +| Wire message | Role | +|---|---| +| `CreateObject (0xF745)` | **canonical** object create/update (full weenie: icon/name/type/stack/container/wielder/effects/…) | +| `DeleteObject (0xF747)` | remove | +| `PlayerDescription (0x0013)` | login snapshot: inventory + equipped + shortcuts (references; some props) | +| `ViewContents (0x0196)` | a container's `{guid, slot}` list when opened | +| move events `0x0019/1A/1B`, `0x0022/23`, `0x019A` | re-parent (container/wield/3D) | +| `Public/PrivateUpdateProperty* (0x02CD–0x02DA)` | per-property live updates (D.5.2 wired `0x02CE` UiEffects → icon) | +| `InventoryServerSaveFailed (0x00A0)` | speculative-move rollback | + +## 4. Grounding research (already written — read before the brainstorm) + +- `docs/research/2026-06-16-inventory-deep-dive.md` — inventory panel + the wire catalog +- `docs/research/2026-06-16-ui-item-slot-icon-dragdrop-spine-deep-dive.md` — `ClientObjMaintSystem`, + `ServerSaysMoveItem`, the resolve-by-guid model +- `docs/research/deepdives/r06-items-inventory.md` — the item/container property model +- `docs/research/2026-06-16-ui-panels-synthesis.md` — core-panels build order (item-model is + the prerequisite for the inventory panel) +- `claude-memory/project_d2b_retail_ui.md` — D.2b/D.5.1/D.5.2 state +- `claude-memory/feedback_weenie_vs_static.md` — items are server weenies, not dat-baked + +## 5. Recommended approach + +Full process (the user values it): **brainstorm → spec → plan → subagent implementation.** +Open the brainstorm on **the unify-vs-separate question (§2) first**, then the ingestion +lifecycle (§3), then how the UI (toolbar/inventory/paperdoll) binds by guid. This is the +foundation the remaining D.5 core panels sit on — get it solid. + +NOTE the user's standing constraint for this phase: *"No quick fixes — needs to be +architecturally solid and thought through."* Do not band-aid `EnrichItem` to add new items; +design the model properly. + +**MEMORY.md index line:** +- [Handoff: client object/item data model (2026-06-18)](research/2026-06-18-item-object-model-handoff.md) — next phase after D.5.2. Root cause of the live-Coldeve "4/6 hotbar items missing": acdream's item model is enrich-existing-only (drops CreateObjects without a pre-seeded stub). Fix = port retail's `ClientObjMaintSystem` (CreateObject = canonical ingestion; UI resolves by guid). CRUX to settle first: unify the WorldEntity + ItemRepository tracks into one object table, or keep separate w/ shared ingestion? Grounding research + ingestion surface listed. User constraint: architecturally solid, no quick fixes.