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 ECS, fixed 20 Hz tick |
| Transport | raw WebSocket, custom binary protocol |
| Client | TypeScript, PixiJS 8, Vite |
| Orchestration | .NET Aspire 13 |
| Tests | xUnit v3, Vitest, Aspire.Hosting.Testing |
Prerequisites
- .NET SDK 10 (
global.jsonpins the 10.0.1xx band) - Node.js 22.12 or newer
- Optional: the Aspire CLI (
dotnet tool install -g aspire.cli) if you preferaspire run
Run everything
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
dotnet run --project src/HSchool.Server
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
dotnet test
tests/HSchool.Protocol.Tests— wire-format round-trips and byte layouts.tests/HSchool.Simulation.Tests— ECS behaviour againstGameWorld, 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.
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.