Co-Op Layer
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
tags:
|
||||
- roadmap
|
||||
- backlog
|
||||
updated: 2026-05-29
|
||||
updated: 2026-06-01
|
||||
permalink: gamevault/06-roadmap/backlog
|
||||
---
|
||||
|
||||
@@ -15,7 +15,7 @@ Unordered pool of candidate work. Promote to a [[Milestones|milestone]] when com
|
||||
- [ ] **Re-validate the M1 play-tick on a stable Unity 6.x** — live runtime blocked on the 6.6 alpha ([[DR-002_Unity66_Alpha_Netcode_Transport]]); optionally reproduce with the `networked-cube` sample to file a bug.
|
||||
- [ ] Replace template `SampleScene` with a dedicated bootstrap scene + gameplay subscene.
|
||||
- [ ] Optional template cleanup: remove `com.unity.visualscripting`, `Assets/TutorialInfo/`, `Assets/Readme.asset` (delete each asset **with** its `.meta`).
|
||||
- [ ] Decide **relay provider** (default Unity Relay) before M4 (co-op).
|
||||
- [x] Decide **relay provider** before M4 — resolved: **Direct IP/LAN now, Unity Relay later** ([[DR-005_M4_Connection_Model_Direct_IP]], [[2026-06-01_M4_LAN_CoOp_And_Classification_Fix]]).
|
||||
- [ ] Decide home-base **grid 2D vs 3D** before M6 (build/placement).
|
||||
- [ ] Decide **production replication** (predicted vs server-only) before M7 (automation).
|
||||
- [ ] **M2 follow-up — restart the editor to clear the corrupted Burst cache**, then confirm the console is clean on a warm play (no "not a known Burst entry point"). See [[2026-05-31_M2_Combat]] / [[DR-003_M2_Combat_Netcode_Architecture]].
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
tags:
|
||||
- roadmap
|
||||
- milestones
|
||||
updated: 2026-05-29
|
||||
updated: 2026-06-01
|
||||
permalink: gamevault/06-roadmap/milestones
|
||||
---
|
||||
|
||||
@@ -12,9 +12,9 @@ permalink: gamevault/06-roadmap/milestones
|
||||
|---|---|---|
|
||||
| **M0 — Foundation** | DOTS + Netcode stack, asmdef split, bootstrap, smoke test green | ✅ Done 2026-05-29 — [[2026-05-29_Project_Setup]] |
|
||||
| **M1 — Player slice** | Server-spawned owner-predicted player; twin-stick WASD + directional aim | ✅ Done 2026-05-31 — runtime-validated on Unity 6.4.7 (connect→spawn→owner-predicted ghost→replication; EditMode 3/3). The 6.6 failure was environment-specific, see [[DR-002_Unity66_Alpha_Netcode_Transport]] — [[2026-05-30_M1_Player_Slice]] |
|
||||
| **M2 — Combat** | Directional ability fire + deterministic soft auto-target; server-authoritative damage/health | ✅ Done 2026-05-31 — runtime-validated on 6.4.7: input→fire→**predicted projectile**→**swept hit**→server damage→`Health` `[GhostField]` replicated server→client; movement + fire confirmed live; EditMode 22/22. Predicted-projectile + server auto-target + non-Burst classifier — [[DR-003_M2_Combat_Netcode_Architecture]], [[2026-05-31_M2_Combat]]. (Projectile ghost-map errors appear only under server tick-batching from running two editors at once — close the reference editor for clean netcode.) |
|
||||
| **M2 — Combat** | Directional ability fire + deterministic soft auto-target; server-authoritative damage/health | ✅ Done 2026-05-31 — runtime-validated on 6.4.7: input→fire→**predicted projectile**→**swept hit**→server damage→`Health` `[GhostField]` replicated server→client; movement + fire confirmed live; EditMode 22/22. Predicted-projectile + server auto-target + non-Burst classifier — [[DR-003_M2_Combat_Netcode_Architecture]], [[2026-05-31_M2_Combat]]. (Projectile ghost-map errors were later root-caused to a `[ReadOnly]`-write in `ProjectileClassificationSystem` — fixed 2026-06-01, see [[2026-06-01_M4_LAN_CoOp_And_Classification_Fix]] — NOT two-editor tick-batching as first thought.) |
|
||||
| **M3 — Data-driven abilities & modifiers** | Ability **and** character stats authored in ScriptableObjects, baked to DOTS **blob assets**; runtime **flat + % modifier** stacks (upgrades/buffs) → effective stats, server-authoritative + prediction-correct. Pattern slice: refactor the current projectile ability + 1–2 sample abilities onto the data model. | ✅ Done 2026-05-31 — runtime-validated on 6.4.7: blob DB baked into both worlds; data-driven base + replicated `StatModifier` ghost buffer → **identical effective stats on server & owner-predicted client** (held under tick-batching); data-only ability swap; real pickup grant; EditMode 38/38. Blob DB + replicated modifier buffer + every-tick effective recompute — [[DR-004_M3_DataDriven_Abilities_Modifiers]], [[2026-05-31_M3_Data_Driven_Abilities]]. |
|
||||
| **M4 — Co-op** | 2–4 players; client-hosted listen-server over Unity Relay | ⬜ |
|
||||
| **M4 — Co-op** | 2–4 players; client-hosted listen-server (Direct IP/LAN now, Unity Relay later) | 🚧 In progress 2026-06-01 — **LAN slice done + runtime-validated**: no-auto-connect `ConnectionConfig` + request-component host/join, editor auto-host + thin clients, deterministic ring spawn; 3 clients (1 real + 2 thin) connect→spawn (distinct slots)→replicate→clean disconnect; `ConnectionUI` for builds; EditMode 45/45. **Unity Relay + real two-build LAN join deferred** — [[DR-005_M4_Connection_Model_Direct_IP]], [[2026-06-01_M4_LAN_CoOp_And_Classification_Fix]]. |
|
||||
| **M5 — Home base + physics** | Persistent base subscene streaming + Unity Physics in the predicted loop | ⬜ |
|
||||
| **M6 — Build/placement** | Server-authoritative grid build placement via RPC | ⬜ |
|
||||
| **M7 — Automation** | Self-running tick-based production chains (deterministic offline catch-up) | ⬜ |
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
date: 2026-06-01
|
||||
type: session
|
||||
tags: [session, dots, netcode, m4, co-op, lan, connection, bugfix, prediction]
|
||||
permalink: gamevault/07-sessions/2026/2026-06-01-m4-lan-co-op-and-classification-fix
|
||||
---
|
||||
|
||||
# Session 2026-06-01 — M4 LAN Co-op kickoff + projectile-classification cascade fix
|
||||
|
||||
## Goal
|
||||
|
||||
Two parts: (1) **fix the runtime console error cascade** that fired "when moving around and shooting" left by the M2/M3 work, and (2) start **M4 — Co-op**. Operator-scoped this pass to **playable LAN co-op** (multi-client correctness + a Host/Join connection flow with host-IP entry so a standalone build can join over LAN); transport **Direct IP/LAN now, Unity Relay deferred**. Architecture locked in [[DR-005_M4_Connection_Model_Direct_IP]].
|
||||
|
||||
## Part 1 — Projectile-classification cascade (root-caused + fixed)
|
||||
|
||||
**The "ton of errors" was one bug.** `ProjectileClassificationSystem` declared its `PredictedGhostSpawn` buffer lookup `[ReadOnly]` (`GetBufferLookup<PredictedGhostSpawn>(true)` + `[ReadOnly]` on the job field) but the job **writes** it via `predictedSpawnList.RemoveAtSwapBack(j)` on a match. So **every projectile spawn** threw `InvalidOperationException: …[ReadOnly]… writing to it` at `ProjectileClassificationSystem.cs:177`, which aborted classification → the predicted spawn was never paired → duplicate ghost → `Found a ghost … does not have an entity connected` → `Received baseline for a ghost we do not have` → `reset their entire ack history` → `Ghost ID n already added to the spawned ghost map` → **server tick batching**. One root cause, full cascade.
|
||||
|
||||
**Fix (3 lines):** `GetBufferLookup<PredictedGhostSpawn>(false)` + drop `[ReadOnly]` on the field (matches the default `DefaultGhostSpawnClassificationSystem`, which removes matched entries from the list; `ProjectileLookup` stays `[ReadOnly]`). The deliberate non-`[BurstCompile]` decision is unchanged.
|
||||
|
||||
**This corrects the M2 misdiagnosis.** [[2026-05-31_M2_Combat]] / [[Milestones]] attributed these ghost-map errors to "server tick-batching from running two editors at once." That was wrong — the tick-batching was a *downstream symptom* of the `[ReadOnly]` write, not the cause. Validated: a single in-editor client, moving + firing, now produces **zero** of the five cascade signatures while projectiles spawn and classify (cooldown advances across fires). Residual `Server Tick Batching` warnings are the unfocused-background-editor perf artifact (sim ticks faster than it renders, 1.25–1.75 ticks/frame) — **not** Burst-cache corruption (no "not a known Burst entry point") and not the cascade; they clear when the Game view is focused / in a build.
|
||||
|
||||
## Part 2 — M4 Playable LAN Host/Join (Direct IP) — see [[DR-005_M4_Connection_Model_Direct_IP]]
|
||||
|
||||
Reused the existing per-connection spawn + `LinkedEntityGroup` auto-despawn (already N-player-ready). Added:
|
||||
|
||||
- **Simulation:** `ConnectionConfig` singleton (`ConnectionMode {None,Host,Join}` + address/port/Requested); `EditorAutoHostSystem` (`#if UNITY_EDITOR`, once per world, seeds Host(loopback) on server + Join(loopback) on client/thin worlds, self-disables); `PlayerSpawnMath.SpawnOffset` (pure ring-slot math); `PlayerSpawner` gained `SpawnRingRadius`/`RingSlots`.
|
||||
- **Server:** `ServerConnectionControlSystem` (Host → `NetworkStreamRequestListen{AnyIpv4:port}`); `GoInGameServerSystem` now applies the deterministic per-`NetworkId` ring offset to the spawn.
|
||||
- **Client:** `ClientConnectionControlSystem` (Join → `NetworkStreamRequestConnect{Parse}`, runs in client **and thin** worlds); `ConnectionUI` IMGUI Host/Join+IP panel (build entry point; hides once connected).
|
||||
- **Authoring/scene:** `PlayerSpawnerAuthoring` bakes radius/slots (default 2.5/4); `Gameplay.unity` re-baked; `NetConnectionUI` GameObject (with `ConnectionUI`) added to `SampleScene` + saved.
|
||||
- **Bootstrap:** `GameBootstrap.AutoConnectPort 7979 → 0` (connection now explicit).
|
||||
- **Asmdefs:** added `Unity.Networking.Transport` to `ProjectM.Client` + `ProjectM.Server` (needed to name `NetworkEndpoint`; transitive-via-NetCode doesn't satisfy the compiler — Unity.Transforms-class gotcha).
|
||||
|
||||
### Validation
|
||||
- **EditMode 45/45 green** (38 prior + 7 new `PlayerSpawnRingTests`); existing M1/M2/M3 suites unaffected.
|
||||
- **Single-client (make-or-break):** no-auto-connect + `EditorAutoHostSystem` + request components → in-proc client connects to loopback, gets `NetworkId`, server spawns its player at `(2.5,0)` (NetworkId-1 ring slot, not origin). Subscene re-bake confirmed (`spawner.radius=2.5 slots=4`).
|
||||
- **3-client co-op (1 real + 2 thin):** server `conns=3 inGame=3 players=3` at distinct ring slots `(2.5,0)/(0,2.5)/(-2.5,0)` — no stacking; the real `ClientWorld` sees all 3 (own owner-predicted + 2 interpolated); thin worlds connect (`conns=1` each, `players=0` locally as expected). Continuous movement replicates (server≈client with prediction lead). **Console clean of all five cascade signatures** under multi-client load + firing.
|
||||
- **Disconnect:** `NetworkStreamRequestDisconnect` on a thin connection → server + client drop to `players=2` via `LinkedEntityGroup`.
|
||||
|
||||
### Method
|
||||
context7-led API confirm (`NetworkStreamRequestListen/Connect`, `NetworkEndpoint`, `ClientServerBootstrap.Create*World`, thin-client prefs) → plan-gated → compile-checkpointed clusters with `read_console` after each. Part 1 isolated + validated first so the fix couldn't be masked by the connection refactor. MCP `script_apply_edits` (anchor) for edits, `create_script` for new files, `Write` for the full `GameBootstrap` rewrite, `execute_code` for runtime world inspection + input injection.
|
||||
|
||||
## Decisions
|
||||
- [[DR-005_M4_Connection_Model_Direct_IP]] — no auto-connect; `ConnectionConfig` + request-component control systems; editor auto-host vs build UI; deterministic ring spawn; **Direct IP/LAN now, Unity Relay deferred** (closes the [[Backlog]] relay-provider blocker for this slice).
|
||||
|
||||
## Open / deferred
|
||||
- **Unity Relay transport** — layer onto the same `ConnectionConfig` flow (relay allocation + join code feeding the endpoint); needs Unity Gaming Services. Deferred.
|
||||
- **Real two-build LAN join** — operator-side: build a ClientServer host + a Client player, run on one LAN, Join-by-IP. The in-editor path (incl. thin clients) is validated; the standalone build join is not yet exercised this session.
|
||||
- **One-shot `Fire` under tick-batching** — continuous input replicates fine, but single-shot `Fire` events can drop in the unfocused editor; focus the Game view (or a build) for reliable fire validation. Pre-existing artifact, not introduced here.
|
||||
- **`[ReadOnly]` regression lock** — a reflection-based EditMode guard would need `ProjectM.Client` in the test asmdef (pulls InputSystem/Graphics); skipped in favour of the runtime console-clean proof. Reconsider if the field regresses.
|
||||
- **Spawn-point variety** — single ring around one `SpawnPoint`; fine for 2–4. Per-team/spread layouts later.
|
||||
|
||||
## Next
|
||||
Either (a) **Unity Relay transport** to make M4 remote-playable (layer on `ConnectionConfig`), or (b) advance to **M5 — Home base + physics** per [[Milestones]]. Recommend a quick real-LAN two-build smoke test first to confirm the `ConnectionUI` join path end-to-end.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: DR-005
|
||||
title: M4 connection model — explicit ConnectionConfig + netcode request components; Direct IP/LAN now, Unity Relay deferred
|
||||
status: accepted
|
||||
date: 2026-06-01
|
||||
tags:
|
||||
- decision
|
||||
- netcode
|
||||
- connection
|
||||
- co-op
|
||||
- lan
|
||||
permalink: gamevault/07-sessions/decisions/dr-005-m4-connection-model-direct-ip
|
||||
---
|
||||
|
||||
# DR-005 — M4 Connection Model (Direct IP/LAN; Relay deferred)
|
||||
|
||||
## Context
|
||||
|
||||
M4 ([[Milestones]]) = co-op for 2–4 players. The roadmap framed it as "client-hosted listen-server over Unity Relay," and [[Backlog]] flagged **"decide relay provider"** as a blocker. The operator scoped this pass to **playable LAN co-op** with transport **Direct IP/LAN now, Unity Relay deferred**. Until now `GameBootstrap` hard-coded `AutoConnectPort = 7979` → a single in-proc client auto-connected over IPC; there was no host-vs-join choice, no multi-client path, and all players spawned on one point. The existing per-connection spawn (`GoInGameServerSystem` stamps `GhostOwner`, links the player to the connection's `LinkedEntityGroup` for auto-despawn) already generalised to N players — only connection establishment + spawn spread were missing. Settled at intake, validated against context7 (Netcode 1.13.2) + runtime. Extends [[DR-003_M2_Combat_Netcode_Architecture]].
|
||||
|
||||
## Decision
|
||||
|
||||
1. **No auto-connect.** `GameBootstrap.AutoConnectPort = 0` (was 7979); worlds are created idle and wait for an explicit listen/connect.
|
||||
2. **Connection driven by a `ConnectionConfig` singleton + per-world control systems.** `ConnectionConfig { ConnectionMode Mode; FixedString64Bytes Address; ushort Port; bool Requested }` (Simulation, created per world). `ServerConnectionControlSystem` (ServerSimulation) turns a Host request into a `NetworkStreamRequestListen { AnyIpv4:port }`; `ClientConnectionControlSystem` (ClientSimulation | ThinClientSimulation) turns a Join request into a `NetworkStreamRequestConnect { Parse(addr,port) }`. Both clear `Requested`. **Request components, NOT manual `NetworkStreamDriver.Listen/Connect`** — race-free (netcode's receive system acts once the driver store is ready) and identical across server / client / thin worlds.
|
||||
3. **Two entry points.** Player builds: `ConnectionUI` (IMGUI Host / Join+IP, in `ProjectM.Client`) writes `ConnectionConfig` into `ClientServerBootstrap.ServerWorld` / `ClientWorld`; hides once connected; Host shown only where a server world exists. Editor: `EditorAutoHostSystem` (`#if UNITY_EDITOR`, runs once per world, then self-disables) seeds Host(loopback) in the server world and Join(loopback) in the client + **every** thin-client world — reproducing the old zero-config playflow, now multi-client.
|
||||
4. **Deterministic spawn spread.** `PlayerSpawnMath.SpawnOffset(networkId, radius, slots)` (pure, in Simulation) places each connection on a ring slot keyed by `NetworkId` (slots evenly spaced; spills to concentric outer rings past `slots`); `GoInGameServerSystem` applies `SpawnPoint + offset`. Tunable via baked `PlayerSpawner.SpawnRingRadius` / `RingSlots` (default 2.5 / 4). Server-only, no RNG — unit-tested in `PlayerSpawnRingTests`.
|
||||
5. **Transport: Direct IP/LAN now; Unity Relay deferred.** Closes the [[Backlog]] "decide relay provider" blocker for this slice: the eventual remote transport is **Unity Relay**, layered later on top of the same `ConnectionConfig` flow (swap the endpoint source for a relay allocation + join code). The request-component path is transport-agnostic, so the gameplay/connection code does not change.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Thin-client connect is the load-bearing surface, and it works.** With `AutoConnectPort=0` thin worlds connect only because `ClientConnectionControlSystem` carries `ThinClientSimulation` and `EditorAutoHostSystem` seeds each thin world. Runtime-validated: 3 clients (1 real + 2 thin) → server `conns=3 players=3` at distinct ring slots `(2.5,0)/(0,2.5)/(-2.5,0)`; the real client sees all three (own owner-predicted + two interpolated); a clean disconnect (`NetworkStreamRequestDisconnect`) drops server **and** client to 2 via the `LinkedEntityGroup` path. No ghost-classification errors under multi-client load + firing.
|
||||
- **`Unity.Networking.Transport` is now a direct asmdef reference** on `ProjectM.Client` + `ProjectM.Server` (`NetworkEndpoint` lives there; transitive-via-`Unity.NetCode` visibility does NOT satisfy the compiler — same class of gotcha as `Unity.Transforms`).
|
||||
- **Editor vs build divergence is intentional and small:** auto-host in editor, manual UI in builds. In-editor the request path is still exercised (auto-host uses it), so it is not an untested code path.
|
||||
- **Relay deferral keeps this pass service-free** (no Unity Gaming Services dependency; fully validated in-session). Revisit when remote (non-LAN) play is needed: add a relay-allocation step feeding `ConnectionConfig` endpoints, then re-test connect + the connect-retry window.
|
||||
- **Connect ordering relies on transport's connect-retry:** both server-listen and client-connect are seeded ~frame 1, so a client may issue connect before the server is listening; the transport retries until the listener is up (fine over loopback/LAN). Watch this if Relay introduces longer setup latency.
|
||||
|
||||
Mirrors the server-authoritative + small-co-op (2–4, listen-server) pillars from [[Pillars]].
|
||||
Reference in New Issue
Block a user