# h-school Base for a school-management game: an authoritative .NET server that owns the saves and their in-game calendars, a TypeScript client that renders the menus, and .NET Aspire tying them together for local runs and integration tests. What exists today is the shell around a game: a main menu of schools, a creation form, and a school screen with a running game clock. The school itself is still empty. ## Stack | Layer | Choice | | --- | --- | | Server | .NET 10, ASP.NET Core | | Simulation | [Arch](https://github.com/genaray/Arch) ECS, fixed 20 Hz tick | | Transport | REST for the menu, raw WebSocket + binary protocol for the clock | | Client | TypeScript, Vite, plain DOM | | 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`). A second launch with no C# changes skips MSBuild; `run-aspire.cmd --rebuild` forces a build. 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. ## What you can do - **Main menu** — every school as a card with its name and its current in-game date and time, ticking live. Deleting one asks for confirmation first. The footer switches the UI between Russian and English; the choice is remembered in the browser. - **Create a school** — type a name or roll a random one, pick a start date (3 April 2012, 06:00 by default). At six schools the create button is disabled and says why. - **Inside a school** — the date, time and weekday of the game calendar, play/pause and the ×½ ×1 ×2 ×5 ×10 speed buttons, plus a way back to the menu. Time runs at 1 game minute per real second at ×1. **Every school runs on its own**, whether or not you are inside it — the menu cards keep counting. Only the pause button stops a school, and it stays paused (the card says so) until you press play again. Schools are written under `saves/` on the server. Restarting the process brings them back. ## 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.Content.Tests` — JSONC catalog, inheritance, patches, map connectivity. No host. - `tests/HSchool.Simulation.Tests` — the game clock, school load/create, and the school registry, no host involved. - `tests/HSchool.AppHost.Tests` — boots the real Aspire graph, drives the menu API and the WebSocket clock. Runs headless (`--HSchool:Headless=true`), so no Node install is needed. ```bash npm --prefix src/HSchool.Client test ``` Vitest covers the client codec, the calendar formatting and the RU/EN dictionaries. ## Layout ``` src/ HSchool.Protocol/ binary wire format (shared contract with the client) HSchool.Content/ JSONC defs, patches, map validation HSchool.Simulation/ schools, the game clock, the Arch ECS world HSchool.Server/ ASP.NET Core host, menu API, WebSocket, workers HSchool.Server/mods/ pack folders; `core` is always on HSchool.ServiceDefaults/ Aspire telemetry, health checks, resilience HSchool.AppHost/ Aspire orchestration HSchool.Client/ Vite + TypeScript UI tests/ docs/ architecture.md how the pieces fit together protocol.md the HTTP API and 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 | | `MaxSchools` | 6 | how many schools may exist at once | | `GameMinutesPerRealSecond` | 1 | game minutes per real second at ×1 | | `DefaultStartDate` | `2012-03-31T06:00:00` | prefilled start of a new school | | `SavesDirectory` | `saves` | per-school JSON files | | `ModsDirectory` | `mods` | pack folders; `core` is required | | `SaveIntervalSeconds` | 30 | rare clock snapshot; not every tick | | `MinSaveIntervalMilliseconds` | 1000 | shortest gap between saves caused by pause or speed | | `MonthlyPayrollCap` | 100000 | monthly payroll the player may commit; hires and assignments that would exceed it are rejected | | `SchoolWeekDays` | 5 | working days from Monday (5 is Mon–Fri; 6 adds Saturday) | | `MaxDecisionsPerTick` | 64 | presence decisions processed per tick; overflow waits | ## What is deliberately missing No authentication, no action execution, and the create dialog does not yet pick mods or edit the map — every new school gets the vanilla `core` layout. Each of these has a seam described in [`docs/architecture.md`](docs/architecture.md).