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:
@@ -11,17 +11,17 @@
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Расписание пересобирается при найме, увольнении и изменении назначений — **не по тику**
|
||||
- [ ] Хранится в сейве рядом с составом; пишется при изменении, не по таймеру. Закреплённые
|
||||
- [x] Расписание пересобирается при найме, увольнении и изменении назначений — **не по тику**
|
||||
- [x] Хранится в сейве рядом с составом; пишется при изменении, не по таймеру. Закреплённые
|
||||
игроком уроки сохраняются вместе с ним
|
||||
- [ ] Воркер публикует расписание снимком, как ростер
|
||||
- [ ] «Кто где сейчас» **вычисляется** из расписания и часов, а не хранится вторым состоянием
|
||||
- [ ] Команда закрепления и снятия закрепления урока — через мейлбокс воркера
|
||||
- [ ] `GET /api/schools/{id}/timetable` — расписание класса и расписание человека; читает
|
||||
- [x] Воркер публикует расписание снимком, как ростер
|
||||
- [x] «Кто где сейчас» **вычисляется** из расписания и часов, а не хранится вторым состоянием
|
||||
- [x] Команда закрепления и снятия закрепления урока — через мейлбокс воркера
|
||||
- [x] `GET /api/schools/{id}/timetable` — расписание класса и расписание человека; читает
|
||||
опубликованный снимок
|
||||
- [ ] Снимок карты отдаёт по узлу, что там сейчас идёт и у кого; протокол и `docs/protocol.md`
|
||||
- [x] Снимок карты отдаёт по узлу, что там сейчас идёт и у кого; протокол и `docs/protocol.md`
|
||||
правятся тем же коммитом, версия бумпится
|
||||
- [ ] Панель локации получает данные для «Сейчас» и «Персонажей»
|
||||
- [x] Панель локации получает данные для «Сейчас» и «Персонажей»
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
|
||||
@@ -66,5 +66,5 @@
|
||||
| --- | --- | --- |
|
||||
| [14. Каркас дня и каникулы](14-school-calendar.md) | ✅ | Звонки, длина недели, каникулы |
|
||||
| [15. Планировщик](15-timetable-planner.md) | ✅ | Раскладка часов по слотам, четыре запрета |
|
||||
| [16. Расписание в школе](16-timetable-in-school.md) | ⬜ | Сейв, снимок, «кто где сейчас» |
|
||||
| [16. Расписание в школе](16-timetable-in-school.md) | ✅ | Сейв, снимок, «кто где сейчас» |
|
||||
| [17. Расписание на экране](17-timetable-screen.md) | ⬜ | Скобки в дереве, сетка класса, расписание учителя |
|
||||
|
||||
+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