Add networked Fighter3D minigame
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
# Fighter3D Networked MiniGame Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Build a first playable server-authoritative Fighter3D networked mini game in the existing hot-update mini game framework.
|
||||
|
||||
**Architecture:** Follow the existing RockPaperScissors mini game layout. Put deterministic combat rules and snapshot codec in `Fighter3D.Core`, room authority in `Fighter3D.Server`, and Unity-only UI/input in `Fighter3D.Client`.
|
||||
|
||||
**Tech Stack:** C# 9, .NET `netstandard2.1` hot-update projects, xUnit server tests, Unity runtime APIs only inside the client hot-update assembly.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Do not hand-edit Unity `.meta`, `.prefab`, `.unity`, `.mat`, `.asset`, `.controller`, or `.anim` files.
|
||||
- Core must not reference `UnityEngine`.
|
||||
- Client must send gameplay messages only through `IGameClientCtx.Send`.
|
||||
- Client assets must load through `ctx.Assets.Load`.
|
||||
- Server is authoritative for damage, block, KO, winner, and settlement.
|
||||
- First playable scope excludes jump, crouch, command specials, supers, polished 3D assets, and replay tooling.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Core Combat Model
|
||||
|
||||
**Files:**
|
||||
- Create: `Client/Assets/MiniGames/Fighter3D/scripts~/Core/FighterTypes.cs`
|
||||
- Create: `Client/Assets/MiniGames/Fighter3D/scripts~/Core/FighterLogic.cs`
|
||||
- Create: `Server/games-src/Fighter3D/Fighter3D.Core/Fighter3D.Core.csproj`
|
||||
- Modify: `Server/Server.Host.Tests/Server.Host.Tests.csproj`
|
||||
- Test: `Server/Server.Host.Tests/Fighter3DLogicTests.cs`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `Fighter3D.Core.FighterLogic.CreateInitial(RoomConfig, IRandom) : FighterState`
|
||||
- Produces: `Fighter3D.Core.FighterLogic.Step(FighterState, FighterInput, IRandom) : StepResult<FighterState, FighterEvent>`
|
||||
- Produces: `Fighter3D.Core.FighterLogic.Encode(FighterState) : byte[]`
|
||||
- Produces: `Fighter3D.Core.FighterLogic.Decode(byte[]) : FighterState`
|
||||
|
||||
- [ ] **Step 1: Write failing core tests**
|
||||
|
||||
Create tests that assert initial state, movement clamp, light hit damage/stun, guard block, timeout winner, and codec round-trip.
|
||||
|
||||
- [ ] **Step 2: Run tests and verify red**
|
||||
|
||||
Run: `dotnet test Server/Server.Host.Tests/Server.Host.Tests.csproj --filter Fighter3DLogicTests`
|
||||
|
||||
Expected: compile failure because `Fighter3D.Core` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement minimal core**
|
||||
|
||||
Implement two fighters, fixed 60 Hz step accumulation, stage bounds, facing, horizontal movement, light/heavy attacks, attack active collision by range, guard-away block, hit/block stun, HP, KO, timeout, and packet codec.
|
||||
|
||||
- [ ] **Step 4: Run tests and verify green**
|
||||
|
||||
Run: `dotnet test Server/Server.Host.Tests/Server.Host.Tests.csproj --filter Fighter3DLogicTests`
|
||||
|
||||
Expected: all Fighter3D core tests pass.
|
||||
|
||||
### Task 2: Server Room Authority
|
||||
|
||||
**Files:**
|
||||
- Create: `Server/games-src/Fighter3D/Fighter3D.Server/Fighter3D.Server.csproj`
|
||||
- Create: `Server/games-src/Fighter3D/Fighter3D.Server/FighterServerRoom.cs`
|
||||
- Modify: `Server/Server.Host.Tests/Server.Host.Tests.csproj`
|
||||
- Test: `Server/Server.Host.Tests/Fighter3DServerRoomTests.cs`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `FighterLogic`, `FighterInput`, `FighterState`
|
||||
- Produces: `Fighter3D.Server.FighterServerRoom : IGameServerRoom`
|
||||
- Produces: `FighterServerRoom.InputOpcode = 1`
|
||||
- Produces: `FighterServerRoom.SnapshotOpcode = 100`
|
||||
|
||||
- [ ] **Step 1: Write failing room tests**
|
||||
|
||||
Create tests that start a two-player room, assert initial broadcast, send attack input from player 1, tick until KO, and assert `EndRoom` winner is player 1.
|
||||
|
||||
- [ ] **Step 2: Run tests and verify red**
|
||||
|
||||
Run: `dotnet test Server/Server.Host.Tests/Server.Host.Tests.csproj --filter Fighter3DServerRoomTests`
|
||||
|
||||
Expected: compile failure because `Fighter3D.Server` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement minimal room**
|
||||
|
||||
Map `PlayerInfo.PlayerId` to seats, decode input messages, feed Core, broadcast snapshots each room tick, and call `EndRoom(RoomEndResult)` exactly once when Core is finished.
|
||||
|
||||
- [ ] **Step 4: Run tests and verify green**
|
||||
|
||||
Run: `dotnet test Server/Server.Host.Tests/Server.Host.Tests.csproj --filter Fighter3DServerRoomTests`
|
||||
|
||||
Expected: all Fighter3D server room tests pass.
|
||||
|
||||
### Task 3: Client Hot-Update Shell
|
||||
|
||||
**Files:**
|
||||
- Create: `Client/Assets/MiniGames/Fighter3D/scripts~/Client/FighterGameClient.cs`
|
||||
- Create: `Client/HotUpdateGames/Fighter3D.Client/Fighter3D.Client.csproj`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `FighterLogic.Decode(byte[])`
|
||||
- Produces: `Fighter3D.Client.FighterGameClient : IGameClient`
|
||||
|
||||
- [ ] **Step 1: Implement client shell**
|
||||
|
||||
Load `Assets/MiniGames/Fighter3D/res/UI/Prefab/UI_Fighter3D.prefab` through `ctx.Assets.Load`, fallback to a generated Canvas if it is missing, bind no-prefab UI labels, send input frames, and update HUD from snapshots.
|
||||
|
||||
- [ ] **Step 2: Build client project**
|
||||
|
||||
Run: `dotnet build Client/HotUpdateGames/Fighter3D.Client/Fighter3D.Client.csproj -c Release`
|
||||
|
||||
Expected: build succeeds.
|
||||
|
||||
### Task 4: MiniGame Publish Metadata
|
||||
|
||||
**Files:**
|
||||
- Create: `Client/Assets/MiniGames/Fighter3D/README.md`
|
||||
- Create: `Client/Assets/MiniGames/Fighter3D/publish.json`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `gameId = "fighter3d"`
|
||||
- Produces: `clientEntryType = "Fighter3D.Client.FighterGameClient"`
|
||||
- Produces: `serverEntryType = "Fighter3D.Server.FighterServerRoom"`
|
||||
|
||||
- [ ] **Step 1: Add publish metadata**
|
||||
|
||||
Create `publish.json` matching the existing RPS fields with `playerCount` 2 and `tickRateHz` 20.
|
||||
|
||||
- [ ] **Step 2: Build server and client projects**
|
||||
|
||||
Run: `dotnet build Server/games-src/Fighter3D/Fighter3D.Server/Fighter3D.Server.csproj -c Release`
|
||||
|
||||
Run: `dotnet build Client/HotUpdateGames/Fighter3D.Client/Fighter3D.Client.csproj -c Release`
|
||||
|
||||
Expected: both builds succeed.
|
||||
|
||||
### Task 5: Verification
|
||||
|
||||
**Files:**
|
||||
- No new files.
|
||||
|
||||
**Interfaces:**
|
||||
- Verifies all earlier outputs.
|
||||
|
||||
- [ ] **Step 1: Run focused tests**
|
||||
|
||||
Run: `dotnet test Server/Server.Host.Tests/Server.Host.Tests.csproj --filter Fighter3D`
|
||||
|
||||
Expected: all Fighter3D tests pass.
|
||||
|
||||
- [ ] **Step 2: Run full server host tests if focused tests pass**
|
||||
|
||||
Run: `dotnet test Server/Server.Host.Tests/Server.Host.Tests.csproj`
|
||||
|
||||
Expected: pass, or document unrelated existing failures.
|
||||
|
||||
- [ ] **Step 3: Check working tree**
|
||||
|
||||
Run: `git status --short`
|
||||
|
||||
Expected: only intended Fighter3D files plus pre-existing unrelated user changes.
|
||||
Reference in New Issue
Block a user