Update wire protocol to version 6 and enhance timetable functionality
- Bumped the wire protocol version to 6, reflecting changes in the communication structure.
- Expanded the timetable API with new endpoints for fetching and managing lesson schedules, including `GET /api/schools/{id}/timetable` and `POST /api/schools/{id}/timetable/pin`.
- Updated the protocol documentation to include detailed descriptions of the new timetable features and message structures.
- Enhanced the client-side implementation to support the new timetable functionalities, including lesson pinning and unpinning.
- Revised server-side logic to handle timetable operations and ensure proper integration with existing school management features.
- Added tests to validate the new timetable functionalities and ensure robustness in handling lesson data.
This commit is contained in:
+72
-13
@@ -1,13 +1,13 @@
|
||||
# Wire protocol v5
|
||||
# Wire protocol v6
|
||||
|
||||
The client talks to the server two ways:
|
||||
|
||||
- **HTTP/JSON** for the main menu and the in-school people browser — listing, creating and
|
||||
deleting schools, listing mods, loading a catalog for the create editor, and reading a school's
|
||||
roster (filtered list + one-person card). 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.
|
||||
deleting schools, listing mods, loading a catalog for the create editor, reading a school's
|
||||
roster (filtered list + one-person card), staffing, and the timetable. 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 map snapshot
|
||||
sent when a school is opened and whenever the current lesson slot changes.
|
||||
|
||||
This document covers both. One protocol message per WebSocket frame, no framing header beyond the
|
||||
message id. **All multi-byte numbers are little-endian.**
|
||||
@@ -331,6 +331,62 @@ Body: `{ "subject": "Mathematics" }`. Teachers only. Same success payload as GET
|
||||
Removes one assignment. Payroll drops when the subject was not the only one. Unknown
|
||||
assignment is `404` `unknown-assignment`.
|
||||
|
||||
### `GET /api/schools/{id}/timetable`
|
||||
|
||||
The published lesson table and uncovered hours. Optional `?classId=` or `?personId=` filter
|
||||
the lessons. `?lang=ru|en` labels subjects. Reads the snapshot — it does not post to the
|
||||
worker. Unknown `{id}` is `404` `unknown-school`.
|
||||
|
||||
`day` is 0 = Monday. `period` is the 1-based lesson number from the day frame.
|
||||
|
||||
```json
|
||||
{
|
||||
"weekDays": 5,
|
||||
"lessonCount": 7,
|
||||
"lessons": [
|
||||
{
|
||||
"classId": "c5A",
|
||||
"classYear": 5,
|
||||
"classLetter": "A",
|
||||
"subject": "Mathematics",
|
||||
"subjectLabel": "Математика",
|
||||
"teacherId": "f3.p1",
|
||||
"teacherName": "Иванова Ольга Михайловна",
|
||||
"roomId": "classroom-204",
|
||||
"day": 1,
|
||||
"period": 3,
|
||||
"locked": false
|
||||
}
|
||||
],
|
||||
"uncovered": [
|
||||
{
|
||||
"classId": "c5A",
|
||||
"classYear": 5,
|
||||
"classLetter": "A",
|
||||
"subject": "Informatics",
|
||||
"subjectLabel": "Информатика",
|
||||
"hours": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/schools/{id}/timetable/pin`
|
||||
|
||||
Body: `{ "classId", "subject", "roomId", "day", "period" }`. Pins a locked lesson there and
|
||||
rebuilds the rest around it. Same success payload as GET timetable.
|
||||
|
||||
| Status | `code` | When |
|
||||
| --- | --- | --- |
|
||||
| `400` | `unknown-class` / `unknown-subject` / `unknown-room` | Not in this school. |
|
||||
| `409` | `no-teacher` | Nobody is assigned that subject. |
|
||||
| `409` | `pin-rejected` | The slot or room violates the four constraints. |
|
||||
|
||||
### `DELETE /api/schools/{id}/timetable/pin`
|
||||
|
||||
Query: `classId`, `subject`, `day`, `period`. Drops that lock and rebuilds. Unknown lock is
|
||||
`404` `unknown-lesson`.
|
||||
|
||||
## WebSocket message ids
|
||||
|
||||
Client-to-server ids live in `0x00–0x7F`, server-to-client ids in `0x80–0xFF`, so a misrouted
|
||||
@@ -452,9 +508,10 @@ client returns to the menu.
|
||||
|
||||
### `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.
|
||||
Sent when a school is opened, on reconnect OpenSchool, and again when the current lesson slot
|
||||
changes (a bell, a weekend, a holiday — not every tick, not on tree clicks). Labels are in the
|
||||
Hello locale. Occupancy is computed from the timetable and the clock; the client must not
|
||||
derive it.
|
||||
|
||||
Strings are `u16` byte length + UTF-8. Empty string is a zero length.
|
||||
|
||||
@@ -476,8 +533,10 @@ Each node:
|
||||
| `u16` | pupil slots — how many pupils can take a lesson here. Summed from things on the server. |
|
||||
| `u8` | item count, then that many records of: string name + `u8` count |
|
||||
| `u8` | position count, then that many strings |
|
||||
| `u8` | `1` if a lesson is in this room right now, then subject label + class label strings; `0` if free |
|
||||
| `u8` | character count, then that many name strings (teacher and pupils of the lesson) |
|
||||
|
||||
Item `count` is how many of that thing stand in the room (`Парта ×16` is one record, not sixteen). The client must not recompute pupil slots from items.
|
||||
Item `count` is how many of that thing stand in the room (`Парта ×16` is one record, not sixteen). The client must not recompute pupil slots from items. The location panel draws activity and characters from the last two fields.
|
||||
|
||||
## Guarantees and limits
|
||||
|
||||
@@ -491,7 +550,7 @@ Item `count` is how many of that thing stand in the room (`Парта ×16` is o
|
||||
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 v5 yet
|
||||
## Not in v6 yet
|
||||
|
||||
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.
|
||||
Authentication, Sit orders, an event log, walking, and `OpenLocation` on the server — the tree is
|
||||
filtered on the client from the snapshot.
|
||||
|
||||
Reference in New Issue
Block a user