acdream/docs
Erik 0ccbb4e52c fix(interaction): port retail's wielded-item pickup rejection (Slice 4 F1)
Slice 4 made a remote character's wielded weapon selectable, which made the
pickup chain reachable end to end for the first time: SelectionPickUp on
another player's weapon captured identity, passed ValidatePickupTarget (which
checked only the Stuck flag and the small-item mask, and a MeleeWeapon clears
both), installed a real non-autonomous approach through
PlayerInteractionMovementSink, and then sent a pickup request the server
rejects. Retail does none of that.

ItemHolder::AttemptToPlaceInContainer @ 0x00588140 runs
AttemptToPlaceInContainer_IsItemLegal @ 0x005870C0 first, at 0x00588173 --
ahead of container legality, auto-merge, the container walk, and the only
CM_Inventory::Event_PutItemInContainer emitter
(ACCWeenieObject::UIAttemptPutInContainer @ 0x0058D680). IsItemLegal's arm at
0x005872B7 rejects `!ACCWeenieObject::IsOwnedByPlayer(item) &&
item->pwd._location != 0` with one local
ECM_UI::SendNotice_DisplayStringInfo(0x1a, ...), and
CPlayerSystem::PlaceInBackpack @ 0x0055D8C0 then withdraws the waiting slot it
had published (SetWaitingState(obj, 0) + SendNotice_EndPendingInPlayer at
0x0055D918). No request, no movement. acdream had never ported that arm; it
was harmless while wielded children were unpickable and stopped being harmless
at f6db964f.

The notice is data_7e2228, "The %s is being wielded by someone else!" -- WITH
the exclamation mark. IsItemLegal's six strings occupy one contiguous literal
block, 0x007e21f0 through 0x007e234c, one per arm in reverse code order, and
the two neighbours already ported here (0x007e227c "The %s cannot be picked
up!" at 0x00587264, 0x007e22b4 "You cannot pick up creatures!" at 0x005871f4)
pin it. The punctuation-free 0x007cd350 variant belongs to the wield/wear
block and is emitted from a different function at 0x00560aef.

pwd._location is the PublicWeenieDesc CurrentWieldedLocation field
(acclient.h:37175), which acdream projects as
ClientObject.CurrentlyEquippedLocation, and ACCWeenieObject::IsOwnedByPlayer
@ 0x0058D160 is IsOwnedByObject(this, player_id) -- already ported as
ClientObjectTable.IsOwnedByObject @ 0x0058CEB0 and reached here through the
existing ItemInteractionController.IsOwnedByPlayer. The arm reads pwd._location
verbatim rather than adding a WielderId belt-and-braces test, because retail's
predicate is the thing being ported.

The player's OWN wielded item is IsOwnedByPlayer, so retail passes it and takes
a different route. ACCWeenieObject::DeterminePositionState @ 0x0058BE70 gives
it PositionState.WIELDED (acclient.h:6802) rather than IN_3D_VIEW, and
UIAttemptPutInContainer records IR_PICK_UP only for IN_3D_VIEW, treating
WIELDED and IN_CONTAINER alike as a plain IR_PUT_IN_CONTAINER transfer. So an
own-wielded item is unwielded in place: the request goes out immediately with
no approach, joining the existing current-ground-object shortcut. The shortcut
carries an ownership conjunct so it can never outrun the 0x005872B7 gate.

TryGetApproach now refuses attached children outright, for the same
IN_3D_VIEW reason. An Attached projection's bookkeeping WorldEntity.Position
carries the PARENT's composed root (EquippedChildRenderController
.ApplyParentWorldPose), not the child frame CPhysicsObj::UpdateChild @
0x00512D50 composes, so an approach built from it walked toward the wielder.
Slice 4 de-parented the marker anchor but left this one parent-derived; no
approach can anchor on a wielder now.

The pick predicates are deliberately untouched. Picking, selecting, examining,
lighting-pulse identity, and the vivid-marker anchor on a remote's wielded
weapon all behave exactly as Slice 4 shipped them -- retail's sr_Select and
sr_Examine branches of RecvNotice_SmartBoxObjectFound @ 0x004E5AD0 never
consult IsItemLegal. The gate is the transaction, not the pick.

f6db964f's message asserted the slice introduced no deviation and owed no
retail-divergence-register row. That was wrong: the unported 0x005872B7 arm
was a deviation it made reachable. This commit ports the arm in full, matches
retail on the own-wielded path, and removes the parent-derived approach
anchor, so the record is corrected here and no register row is owed.

Gates: dotnet build green; AcDream.App.Tests 3,960 passed / 3 skipped;
complete Release solution 9,792 passed / 5 skipped;
tools\run-connected-world-lifecycle-gate.ps1 -SkipBuild RESULT=PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 19:09:38 +02:00
..
architecture docs(net): N6 accepted - Opus review PASS; five owed register rows filed 2026-07-29 17:32:59 +02:00
audit docs: update audit — Sprint 1 items verified (sequence counters + scenery LCG) 2026-04-13 13:51:39 +02:00
plans fix(interaction): port retail's wielded-item pickup rejection (Slice 4 F1) 2026-07-29 19:09:38 +02:00
reference docs(reference): preserve local ACE command catalog 2026-07-27 00:03:58 +02:00
reports docs: the overnight consolidation report, open questions first 2026-07-29 03:54:43 +02:00
research docs: VTank requirements research - the plugin-automation milestone model 2026-07-29 16:29:49 +02:00
superpowers fix(world): remove non-retail portal exit fade 2026-07-15 23:20:52 +02:00
bugs.md docs: reconcile project status and navigation 2026-07-20 13:00:41 +02:00
ISSUES.md docs: Campaign N CLOSED - user-accepted; #260 closed; #262 filed 2026-07-29 17:48:45 +02:00
README.md docs: reconcile project state and refresh README 2026-07-27 12:53:31 +02:00

acdream documentation map

This page is the entry point for project documentation. It distinguishes current sources of truth from implementation history so an old plan or issue banner cannot silently override the current program state.

Current snapshot — 2026-07-27

  • Milestone state: M3, “Cast a spell,” landed 2026-07-21. M4, “Live in the world,” is active.
  • M4 gameplay program: resume the pre-M4 world-interaction completion program. Favorite-spell overflow, status Use/Assess, and the complete assessment surface are user-accepted. Equipped-child picking and vendor browse/transactions remain Slices 46.
  • Structural/runtime state: all eight GameWindow decomposition slices, Modern Runtime Slices AJ, and the connected visual/lifecycle gates are complete. GameWindow is a 1,622-line composition/callback shell. AcDream.Runtime.GameRuntime owns canonical session, entity/object, gameplay, movement, physics, projectile, environment, and portal state; graphical and no-window hosts borrow the same owner graph.
  • Headless state: Slice K is complete. AcDream.Headless is a presentation-free Windows/Linux host with deterministic commands/events, shared immutable content, multi-session isolation, reconnect, resource telemetry, and 1/5/10/30-session gates. The final two-account native-Linux soak completed ten minutes, logged out through ACE, and converged every ownership ledger.
  • Linux graphical state: Slice L0 and the L1 implementation checkpoint are complete at 66f114b2 and 11501d52. Native Windows passes the active modern-GL/audio/window smoke. WSLg X11/Wayland correctly reject their missing GL_ARB_bindless_texture. Physical-Linux validation and L2L6 are explicitly deferred; resume at the supported AMD/NVIDIA L1 gate.
  • Completed gameplay gates: R6 locomotion/collision/projectile/teleport/ radar, two-client portal-out/materialization, indoor prepared collision, loot ordering, local/remote ground drops, and selection-marker lifetime.
  • Separate visual verification: issue #225, the shared-alpha lifestone/particle result; its connected resource-lifetime and performance routes pass.
  • Carried behaviour debt: issue #153 (far teleport onto an unstreamed edge), #116 (narrowed slide response), #235 (capped/RDP jump cadence), and the active temporary-stopgap rows in the divergence register.
  • Divergence audit: 189 active rows — IA 18, AD 38, AP 91, TS 38, and UN 4 — plus retained struck/retired historical rows such as TS-37.
  • Latest automated baseline: the Release build succeeds with the 17 test-project warnings tracked by issue #228; 8,826 tests pass and five are intentionally skipped. App passes 3,763 / 3 skips. The L1 Windows supported smoke and WSLg X11/Wayland negative-capability reports all end with zero window/GL/input/audio ownership.

Sources of truth

Read these in this order when deciding what to do next:

  1. plans/2026-05-12-milestones.md — the active playable outcome, freeze boundaries, and visual gates.
  2. plans/2026-04-11-roadmap.md — strategic phase ledger: shipped, active, deferred, and future work.
  3. ISSUES.md — tactical defects and small follow-ups. The status inside an issue is authoritative; physical order is not.
  4. architecture/retail-divergence-register.md — every known place runtime behavior can differ from retail.
  5. architecture/acdream-architecture.md and architecture/code-structure.md — ownership, dependency, update-thread, and extraction rules.
  6. architecture/worldbuilder-inventory.md — rendering/DAT code already owned in-tree versus mechanisms still ours to port.

If these disagree, milestones control the current outcome, the roadmap controls work ordering, the issue status controls the individual defect, and the architecture documents control implementation shape. Reconcile the stale document in the same change; do not leave both claims standing.

Research and implementation records

  • research/named-retail/ is the primary retail oracle: named pseudo-C, headers, symbols, and types from the Sept 2013 build.
  • research/decompiled/ is the older Ghidra fallback.
  • research/ contains focused pseudocode, traces, fixtures, and gate reports. A dated research note records evidence; it does not become a new roadmap.
  • superpowers/specs/ and superpowers/plans/ are per-slice design and execution records. Completed plans remain historical.
  • audit/ contains completion and conformance audits.
  • reference/ace-commands.md preserves the local ACE server's complete in-game command catalog and points to the authoritative per-command help surface.

Durable memory

  • ../claude-memory/MEMORY.md indexes the live subsystem memories and the render/physics digests. Read a domain digest before changing that subsystem, especially its DO-NOT-RETRY table.
  • ../memory/ contains stable engineering references such as the modern rendering pipeline, two-tier streaming, and toolchain notes.

Memory accelerates recall; it does not outrank the canonical documents above. When current truth changes, update the relevant canonical document and distill only the durable lesson into memory.

Historical and deprecated documents

  • bugs.md is the April 2026 bug snapshot. It is preserved for archaeology and is not an active ledger.
  • Dated plans and specs describe the decision at that time. Their completion wording is historical unless the current milestone/roadmap explicitly links the item as active.
  • Old R1→R8 architecture sequencing is superseded. Current execution comes from the milestones and strategic roadmap.

Documentation maintenance rules

  • Update milestone, roadmap, issue, divergence, architecture, and memory claims in the same commit when a shipped change affects them.
  • Keep one issue ID per defect. Narrow an issue in place; do not reuse another issue's number as a shorthand.
  • Mark automated, connected, and visual gates separately. An automated pass is not a visual acceptance, and an RDP throughput sample is not a local-display visual comparison.
  • Preserve research history, but remove stale “current/next” claims from living documents once the state advances.