Enhance school creation and map management features by updating the API to support mod packs and map layouts. Introduce a new map snapshot protocol for efficient data handling during school sessions. Revise documentation to reflect these changes, including updates to the protocol and architecture documents. Improve UI components for mod selection and map editing, ensuring a better user experience. Update tests to validate new functionalities and ensure robustness.
This commit is contained in:
+78
-13
@@ -1,10 +1,12 @@
|
||||
# Wire protocol v3
|
||||
# Wire protocol v4
|
||||
|
||||
The client talks to the server two ways:
|
||||
|
||||
- **HTTP/JSON** for the main menu — listing, creating and deleting schools. Those are
|
||||
request/response by nature, so they are plain REST.
|
||||
- **A binary WebSocket at `/ws/game`** for the school calendar, which changes 20 times a second.
|
||||
- **HTTP/JSON** for the main menu — listing, creating and deleting schools, listing mods and
|
||||
loading a catalog for the create editor. Those are request/response by nature, so they are
|
||||
plain REST.
|
||||
- **A binary WebSocket at `/ws/game`** for the school calendar (20 Hz) and the one-shot map
|
||||
snapshot sent when a school is opened.
|
||||
|
||||
This document covers both. One protocol message per WebSocket frame, no framing header beyond the
|
||||
message id. **All multi-byte numbers are little-endian.**
|
||||
@@ -48,15 +50,48 @@ Optional `?lang=en` draws from the English word list (`Northern Academy`); any o
|
||||
none, stays Russian. The client sends the active UI language. Names the player types are not
|
||||
translated — they are saved as written.
|
||||
|
||||
### `GET /api/mods`
|
||||
|
||||
Folders under the server's `mods/` directory. `core` is always first and `required: true`; other
|
||||
packs can be switched off in the create dialog.
|
||||
|
||||
```json
|
||||
{ "mods": [{ "id": "core", "required": true }] }
|
||||
```
|
||||
|
||||
### `GET /api/catalog?lang=ru|en&mods=addon1,addon2`
|
||||
|
||||
Placeable (non-abstract) types plus labels in `lang`, and the last-wins `maps/default.jsonc` for
|
||||
`core` plus the listed extras. The server always prepends `core`. `mods` is a comma-separated
|
||||
list of extra pack ids; omit it for vanilla. Unknown extras return `400` `unknown-mod`.
|
||||
|
||||
`lang` is the same value Hello carries — not `Accept-Language`. Anything other than `en` is
|
||||
Russian.
|
||||
|
||||
### `POST /api/schools`
|
||||
|
||||
Body: `{ "name": "Гимназия №14", "startDate": "2012-04-03T06:00:00Z" }`
|
||||
Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Гимназия №14",
|
||||
"startDate": "2012-04-03T06:00:00Z",
|
||||
"modIds": [],
|
||||
"map": null
|
||||
}
|
||||
```
|
||||
|
||||
`modIds` are extras; the server always prepends `core`. Omit `map` (or send `null`) to use that
|
||||
pack set's default layout. A supplied map is validated as a connected yard-and-rooms graph.
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
| `201` | Created; body is the school. |
|
||||
| `400` `invalid-name` | Blank, or longer than 40 characters. |
|
||||
| `400` `invalid-start-date` | Outside 1900–2999. |
|
||||
| `400` `invalid-map` | Missing yard, no rooms, unknown def, or a disconnected graph. |
|
||||
| `400` `unknown-mod` | An extra pack id is missing under `mods/`. |
|
||||
| `400` `invalid-catalog` | The selected packs could not be loaded. |
|
||||
| `409` `school-limit-reached` | `maxSchools` schools already exist. |
|
||||
|
||||
Failures are RFC 7807 problem details with an extra `code` field — that is what the UI switches on.
|
||||
@@ -83,17 +118,20 @@ frame is obvious at a glance.
|
||||
| `0x82` | S → C | Pong |
|
||||
| `0x83` | S → C | Clock |
|
||||
| `0x84` | S → C | SchoolGone |
|
||||
| `0x85` | S → C | MapSnapshot |
|
||||
|
||||
## Client → server
|
||||
|
||||
### `0x01` Hello — 2 bytes
|
||||
### `0x01` Hello — 3 bytes
|
||||
|
||||
Must be the first frame; the server drops the connection if it does not arrive within 5 seconds.
|
||||
The locale byte is the same language the catalog HTTP API takes as `?lang=`.
|
||||
|
||||
| Offset | Type | Field |
|
||||
| --- | --- | --- |
|
||||
| 0 | `u8` | `0x01` |
|
||||
| 1 | `u8` | protocol version |
|
||||
| 2 | `u8` | locale: `0` Russian, `1` English; any other value is treated as Russian |
|
||||
|
||||
### `0x02` Ping — 9 bytes
|
||||
|
||||
@@ -104,8 +142,8 @@ Must be the first frame; the server drops the connection if it does not arrive w
|
||||
|
||||
### `0x03` OpenSchool — 5 bytes
|
||||
|
||||
Starts watching a school: clock frames for it begin to arrive. It does not start the calendar —
|
||||
every school runs on its own from the moment it is created.
|
||||
Starts watching a school: a map snapshot in the Hello locale arrives once, then clock frames.
|
||||
It does not start the calendar — every school runs on its own from the moment it is created.
|
||||
|
||||
| Offset | Type | Field |
|
||||
| --- | --- | --- |
|
||||
@@ -182,16 +220,43 @@ client returns to the menu.
|
||||
| 0 | `u8` | `0x84` |
|
||||
| 1 | `i32` | school id |
|
||||
|
||||
### `0x85` MapSnapshot — variable
|
||||
|
||||
Sent once when a school is opened (and again on reconnect OpenSchool). Not every tick, not on
|
||||
tree clicks. Labels are in the Hello locale. People and in-place activities are omitted — the
|
||||
client keeps those sections empty.
|
||||
|
||||
Strings are `u16` byte length + UTF-8. Empty string is a zero length.
|
||||
|
||||
| Offset | Type | Field |
|
||||
| --- | --- | --- |
|
||||
| 0 | `u8` | `0x85` |
|
||||
| 1 | `i32` | school id |
|
||||
| 5 | `u16` | node count |
|
||||
| 7… | | nodes |
|
||||
|
||||
Each node:
|
||||
|
||||
| Type | Field |
|
||||
| --- | --- |
|
||||
| `u8` | kind: `0` territory, `1` building, `2` floor, `3` room |
|
||||
| string | instance id |
|
||||
| string | parent id (empty for the yard) |
|
||||
| string | display name |
|
||||
| `u8` | item count, then that many strings |
|
||||
| `u8` | position count, then that many strings |
|
||||
|
||||
## Guarantees and limits
|
||||
|
||||
- Frames larger than 8 KiB are refused with close status `1009 MessageTooBig`.
|
||||
- A malformed frame closes the connection with `1007 InvalidPayloadData`.
|
||||
- Unknown message ids are ignored rather than fatal, so new ids can be added without breaking
|
||||
older clients within the same protocol version.
|
||||
- Clock delivery is lossy under back pressure: each connection buffers 32 frames and drops the
|
||||
oldest, because a stale clock is worthless once a newer one exists.
|
||||
- Clock delivery is lossy under back pressure: each connection buffers 32 clock frames and drops
|
||||
the oldest, because a stale clock is worthless once a newer one exists.
|
||||
- The map snapshot uses a separate reliable queue so ticks cannot crowd it out.
|
||||
|
||||
## Not in v3 yet
|
||||
## Not in v4 yet
|
||||
|
||||
Saving schools to disk (they live in server memory), authentication, and any game state beyond the
|
||||
calendar — the school's ECS world is created but still empty.
|
||||
Authentication, Sit orders, an event log, and `OpenLocation` on the server — the tree is filtered
|
||||
on the client from the snapshot. The school's ECS world is created but still empty.
|
||||
|
||||
Reference in New Issue
Block a user