6.6 KiB
6.6 KiB
date, type, tags, permalink
| date | type | tags | permalink | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-06-02 | session |
|
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,StatModifierghost buffer,UpgradePickupserver-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);SharedStorageContainertag;StorageSpawner;StorageOpRequest+StorageOpbyte consts;StorageMath(deposit-merge / withdraw-clamp-drop). - New (
ProjectM.Server/HomeBase):SharedStorageSpawnSystem(one-shot, spawns the container ghost atCellToWorld(cell));StorageOpReceiveSystem(server RPC apply viaStorageMath, singleton container, outside the predicted loop). - New (
ProjectM.Client/HomeBase):StorageOpSendSystem(managedSystemBase; E/Q keyboard edge + editor-onlyDeposit/Withdrawstatics →StorageOpRequestRPC to the server connection). - New (
ProjectM.Authoring/HomeBase):BaseAnchorAuthoring(bakesBaseAnchorfrom the GameObject position + plot gizmo);SharedStorageContainerAuthoring;StorageSpawnerAuthoring. - Modified:
GoInGameServerSystem— spawn ring re-rooted onBaseGridMath.PlotCenter(BaseAnchor)with aTryGetSingletonfallback.Packages/manifest.json— stale 6.6-era pins reconciled to the resolved 6.4.7 lock. - Assets:
Storage.prefab(duplicated fromUpgradePickup.prefab→ swapped toSharedStorageContainerAuthoring, kept the ownerless-interpolatedGhostAuthoringComponent).Gameplay.unitysubscene — addedBaseAnchor(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):
BaseAnchorbaked 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 deterministicBaseGridMathgrid (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_coderuns as a method body — nousingdirectives 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(perStatModifier) 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
FireInputEvent 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.runInBackgroundonly helps in Play mode). Operator restarted the editor; it recovered clean. Avoid pilingrefresh_unitycalls onto a blocked main thread; wait or ask to focus/restart. scope=all forcerefresh is heavy — fine on a fresh editor, but it (plus an unfocused throttle) likely contributed to the hang. Preferscope=scriptsfor code-only changes.
Open / deferred
- Option C (base/expedition subscene split + async
SceneSystemstreaming) — 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.