# Development ## Prerequisites | Tool | Version | Used for | |---|---|---| | .NET SDK | 10.0 | Server and tests | | Node.js + npm | 22 | Website | | Docker + Compose | recent | Optional, for container builds | ## Running locally Two terminals: ```bash # 1. Game server on http://localhost:5080 cd actionBlocks_server dotnet run --project src/ActionBlocks.Server --urls http://localhost:5080 # 2. Website on http://localhost:5173 cd actionBlocks_website npm install npm run dev ``` The Vite dev server proxies `/hub` (including WebSockets) to `http://localhost:5080`, so the default hub URL `/hub` just works. To point it at another server, use `DEV_SERVER_URL=http://host:port npm run dev`. To play against yourself, open the site in two browser windows. Create a lobby in one and join with the code in the other. Keyboard focus decides which window receives input. ### Handy server settings for testing `dotnet run` finds `config/game.json` in the repo root automatically. Edit it (changes apply from the next match), point `GAME_CONFIG` to a separate test file, or override single values with environment variables: ```bash Game__ItemSpawnChance=0.9 Game__CountdownSeconds=1 \ dotnet run --project src/ActionBlocks.Server --urls http://localhost:5080 ``` That gives items on almost every piece and a near-instant start, which is useful for testing items. To test one item, set the weight of all others to 0 in a test config. ## Commands | Where | Command | Does | |---|---|---| | `actionBlocks_server/` | `dotnet build` | Builds all projects | | | `dotnet test` | Runs the game logic tests | | `actionBlocks_website/` | `npm run dev` | Dev server with hot reload | | | `npm run typecheck` | TypeScript check only | | | `npm run build` | Typecheck + production build into `dist/` | | | `npm run preview` | Serves `dist/` locally | | repo root | `docker compose build` | Builds both images (server image runs the tests) | | | `docker compose config` | Validates the compose file and `.env` | ## Testing strategy - **Game rules** live in `ActionBlocks.Game` and are covered by unit tests. When you change or add a rule, add a test in `tests/ActionBlocks.Game.Tests`. Use `TestHelpers.CreatePlayer` for a deterministic player: it takes a fixed piece sequence and an item chance (0 or 1). Use `FillRow` to build board situations. - **Networking and lobby flow** has no automated tests yet. Test it manually, or with a small script client (see [Protocol → Writing a test client](protocol.md#writing-a-test-client)). - **Bots are an effective test tool.** A script client that joins a lobby and plays with a simple heuristic shows how garbage and items work under real conditions: it clears lines, collects items and uses them on you. One works like this: 1. Read your board from the snapshot. 2. For every rotation and x position of the active piece, simulate dropping it and score the result: lines cleared minus holes, height and bumpiness. 3. Send the best move's `RotateCw` / `MoveLeft` / `MoveRight` / `HardDrop` inputs. 4. Send `UseItem` whenever the inventory is not empty. - **Frontend** has no unit tests. `npm run build` (which includes the typecheck) catches type errors, and visual checks happen in the browser. ## Debugging tips - **Server logs** go to stdout. Exceptions in the tick loop are logged per lobby as `Failed to update lobby {Code}`. - **Inspect live snapshots.** In the browser devtools, open Network → WS → the `/hub` connection → Messages. Each `MatchState` frame shows the full state, and its records are separated by `\u001e`. - `GET /health` on the server tells you whether the process is up. - **Is the hub reachable?** `curl -X POST "/negotiate?negotiateVersion=1"` must return JSON listing the available transports. ## Code conventions **Server** - File-scoped namespaces, nullable reference types enabled, one main type per file. Small closely related types may share a file, e.g. all items in `Items.cs`. - `ActionBlocks.Game` must stay free of ASP.NET or serialization dependencies. Mapping to the wire format happens in `ActionBlocks.Server/Dtos`. - Game objects are not thread-safe. Only touch them inside `lock (lobby.Sync)`. - Prefer adding a value to `GameSettings`, or an implementation of an existing interface, over hard-coding numbers in logic. **Website** - Function components and hooks. No state library: server state is owned by `useGameConnection`. - Styling only through `*.module.css`, plus the CSS variables in `theme/global.css`. Don't use inline styles except for computed sizes and colors. - Reference graphics and colors only through `theme/assets.ts` and `theme/colors.ts`. - Keep `net/types.ts` in sync with the server DTOs. - UI text in the pixel font: ASCII only. - No trademarked names in user-facing text or assets.