Files
Project-M/Docs/Vault/07_Sessions/2026/2026-06-02_M5_HomeBase_BaseLayer.md
T
2026-06-02 18:28:23 -07:00

6.6 KiB
Raw Blame History

date, type, tags, permalink
date type tags permalink
2026-06-02 session
session
dots
netcode
home-base
rpc
ghost
m5
m4
gamevault/07-sessions/2026/2026-06-02-m5-homebase-baselayer

Session 2026-06-02 — M5: home-base base-layer + shared storage

Goal

Close out M4 and take the next milestone via /dots-dev. Operator closed M4 (multi-client co-op validated via Unity Multiplayer Play Mode — two controllable characters in-scene; full handshake not exercised end-to-end, Relay still deferred). For M5, clarified what "home base" means for this game and built the operator-chosen Option B: a fixed Base Anchor + planar build-grid coordinate space + spawn re-root, plus one shared storage container ghost (deposit/withdraw via RPC). Locked in DR-008_M5_HomeBase_BaseLayer_Storage.

Process

  • Scoping research workflow (4 parallel readers → synthesis): design/genre meaning, DOTS/Netcode subscene+streaming feasibility, codebase scene state, persistence/world-arch → a grounded "home base" definition + 3 scoped options (A/B/C). Operator picked B; grid 1.0u × 32² planar; storage = (itemId, count); persistence = in-session only (no disk); one shared base; manifest reconcile folded in.
  • Design/verify workflow (3 parallel → I synthesized the plan): extracted exact in-repo templates (RPC GoInGameRequest, StatModifier ghost buffer, UpgradePickup server-spawned interpolated ghost, ring spawn, baked-singleton bakers); context7 was unreachable so Netcode 1.13.2 shapes were verified by reflection on the installed assemblies + the project's own proven usages; locked the deterministic grid math.
  • Implementation (sequential via MCP create_script/apply_text_edits — single editor, domain-reload-ordered; NOT a parallel swarm): components+math → tests → server systems+spawn re-root → client send → authoring → prefab/subscene wiring → runtime validation.

Done

  • New (ProjectM.Simulation/HomeBase): BaseAnchor; BaseGridMath (WorldToCell/CellToWorld/IsCellInPlot/IsPointInPlot/ClampCell/PlotCenter — corner-origin, center-returning, half-open, floor); StorageEntry ([GhostField] buffer, ownerless); SharedStorageContainer tag; StorageSpawner; StorageOpRequest + StorageOp byte consts; StorageMath (deposit-merge / withdraw-clamp-drop).
  • New (ProjectM.Server/HomeBase): SharedStorageSpawnSystem (one-shot, spawns the container ghost at CellToWorld(cell)); StorageOpReceiveSystem (server RPC apply via StorageMath, singleton container, outside the predicted loop).
  • New (ProjectM.Client/HomeBase): StorageOpSendSystem (managed SystemBase; E/Q keyboard edge + editor-only Deposit/Withdraw statics → StorageOpRequest RPC to the server connection).
  • New (ProjectM.Authoring/HomeBase): BaseAnchorAuthoring (bakes BaseAnchor from the GameObject position + plot gizmo); SharedStorageContainerAuthoring; StorageSpawnerAuthoring.
  • Modified: GoInGameServerSystem — spawn ring re-rooted on BaseGridMath.PlotCenter(BaseAnchor) with a TryGetSingleton fallback. Packages/manifest.json — stale 6.6-era pins reconciled to the resolved 6.4.7 lock.
  • Assets: Storage.prefab (duplicated from UpgradePickup.prefab → swapped to SharedStorageContainerAuthoring, kept the ownerless-interpolated GhostAuthoringComponent). Gameplay.unity subscene — added BaseAnchor (at (0,1,0)) + StorageSpawner (→ Storage.prefab, cell (19,19)) authoring GameObjects.
  • Tests: BaseGridMathTests (7) + StorageMathTests (8). EditMode 62/62.

Validation

  • EditMode 62/62 green (+15 vs M5b's 47).
  • Runtime (single in-editor client, 6.4.7): BaseAnchor baked identically into both worlds; player spawn re-rooted onto the anchor (spawned at (2.5,1,0)); storage ghost server-spawned + replicated to the client at (3.5,1,3.5) = CellToWorld(19,19); deposit (1×5, 2×3) then withdraw (decrement + clamp + drop-empty)server == client buffer every time, driven through the real RPC→server→replication path; RPCs survived the tick-batching artifact. Console clean of code/Burst/ghost/RPC errors — only the known unfocused-editor tick-batching warning.

Decisions

  • DR-008_M5_HomeBase_BaseLayer_Storage — home base = baked ghost-free BaseAnchor + locked deterministic BaseGridMath grid (M6 builds on it) + spawn re-root + one ownerless-interpolated shared-storage ghost mutated by a server-authoritative RPC (singleton-resolved, byte op, outside the predicted loop). Streaming + disk persistence deferred.

Diagnosis notes (for future me)

  • execute_code runs as a method body — no using directives allowed (they parse as statements → "Identifier expected"); fully-qualify all types (Unity.Entities.World, ProjectM.Simulation.BaseAnchor, …).
  • Ownerless interpolated ghost ≠ owner-predicted ghost for buffer replication: a server-spawned ownerless chest needs no OwnerSendType/GhostOwner[GhostField] alone replicates server mutations to all. OwnerSendType.All (per StatModifier) is only for the predicting owner to recompute.
  • RPC > predicted InputEvent for one-off shared-state actions: reliable delivery meant deposit/withdraw landed even while the editor tick-batched (the M2 one-shot Fire InputEvent drops under batching).
  • The editor hung mid-session (unresponsive bridge: queued commands accepted, pings unanswered) while unfocused — Edit-mode throttles to near-idle when the window lacks OS focus (Application.runInBackground only helps in Play mode). Operator restarted the editor; it recovered clean. Avoid piling refresh_unity calls onto a blocked main thread; wait or ask to focus/restart.
  • scope=all force refresh is heavy — fine on a fresh editor, but it (plus an unfocused throttle) likely contributed to the hang. Prefer scope=scripts for code-only changes.

Open / deferred

  • Option C (base/expedition subscene split + async SceneSystem streaming) — own world-architecture milestone; M6/M7 don't need it.
  • Disk persistence — nothing to save until M6 produces structures; thin host-only slice afterward.
  • Storage polish — proximity gate (container has HitRadius), real item/UI model, multi-writer ordering beyond first-come.
  • Multi-client storage — validate two clients see identical shared state (pairs with the deferred M5b multi-client interpolation + M4 two-build tests).

Next

Begin M6 — server-authoritative grid build placement via RPC, reusing BaseGridMath (legality + snap) and the runtime-ghost-into-base-cell spawn path from this slice.