# h-school Base for a multiplayer browser game: an authoritative .NET server simulating the world with an ECS, a PixiJS client that renders snapshots, and .NET Aspire tying them together for local runs and integration tests. There is no game here yet — there is a world with a few obstacles, players that can walk around it, and every piece of plumbing needed to build a game on top. ## Stack | Layer | Choice | | --- | --- | | Server | .NET 10, ASP.NET Core | | Simulation | [Arch](https://github.com/genaray/Arch) ECS, fixed 20 Hz tick | | Transport | raw WebSocket, custom binary protocol | | Client | TypeScript, [PixiJS 8](https://pixijs.com/), Vite | | Orchestration | .NET Aspire 13 | | Tests | xUnit v3, Vitest, `Aspire.Hosting.Testing` | ## Prerequisites - [.NET SDK 10](https://dotnet.microsoft.com/download) (`global.json` pins the 10.0.1xx band) - [Node.js](https://nodejs.org/) 22.12 or newer - Optional: the Aspire CLI (`dotnet tool install -g aspire.cli`) if you prefer `aspire run` ## Run everything ```bash dotnet run --project src/HSchool.AppHost ``` On Windows `run-aspire.cmd` does the same and can be double-clicked; it checks that the .NET SDK and Node are on PATH first and passes any arguments through (`run-aspire.cmd --launch-profile http`). The Aspire dashboard opens with two resources: `server` (ASP.NET Core) and `client` (Vite dev server). Aspire assigns the client a random port on every run, so take its URL from the dashboard rather than guessing. Open it and use **WASD** or the arrow keys to move — the HUD shows connection state, server tick, round-trip time and entity count. Open the same URL in a second tab to see another player: both are simulated by the one server. ## Run the pieces separately ```bash dotnet run --project src/HSchool.Server ``` ```bash npm --prefix src/HSchool.Client run dev ``` Without Aspire the client falls back to `http://localhost:5180` for its `/api` and `/ws` proxy, which matches the server's launch profile. ## Tests ```bash dotnet test ``` - `tests/HSchool.Protocol.Tests` — wire-format round-trips and byte layouts. - `tests/HSchool.Simulation.Tests` — ECS behaviour against `GameWorld`, no host involved. - `tests/HSchool.AppHost.Tests` — boots the real Aspire graph, connects a WebSocket, plays a few ticks. Runs headless (`--HSchool:Headless=true`), so no Node install is needed. ```bash npm --prefix src/HSchool.Client test ``` Vitest covers the client codec and snapshot interpolation. ## Layout ``` src/ HSchool.Protocol/ binary wire format (shared contract with the client) HSchool.Simulation/ Arch ECS world, components, systems HSchool.Server/ ASP.NET Core host, WebSocket endpoint, game loop HSchool.ServiceDefaults/ Aspire telemetry, health checks, resilience HSchool.AppHost/ Aspire orchestration HSchool.Client/ Vite + TypeScript + PixiJS renderer tests/ docs/ architecture.md how the pieces fit together protocol.md the wire format, byte by byte AGENTS.md working agreements for humans and coding agents ``` ## Configuration Simulation tunables live under the `Simulation` section of `src/HSchool.Server/appsettings.json`: | Key | Default | Meaning | | --- | --- | --- | | `TickRate` | 20 | fixed simulation steps per second | | `WorldWidth` / `WorldHeight` | 1600 × 900 | field size in simulation units | | `PlayerSpeed` | 260 | units per second | | `PlayerRadius` | 18 | player body radius | ## What is deliberately missing No authentication, no persistence, no client-side prediction, no delta compression, no rooms or matchmaking. Each of these has a natural seam described in [`docs/architecture.md`](docs/architecture.md).