Update wire protocol to version 6 and enhance timetable functionality
ci / server (push) Failing after 3m53s
ci / client (push) Successful in 15s

- 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:
Leonid Pershin
2026-08-19 10:05:05 +03:00
parent 2011d12b1d
commit cad3068bac
30 changed files with 1621 additions and 43 deletions
+61 -3
View File
@@ -129,6 +129,18 @@ public static class ProtocolCodec
{
size += StringSize(position);
}
size += sizeof(byte);
if (HasActivity(node))
{
size += StringSize(node.ActivitySubject) + StringSize(node.ActivityClass);
}
size += sizeof(byte);
foreach (var person in node.Present)
{
size += StringSize(person);
}
}
return size;
@@ -148,9 +160,9 @@ public static class ProtocolCodec
foreach (var node in message.Nodes)
{
if (node.Items.Count > byte.MaxValue || node.Positions.Count > byte.MaxValue)
if (node.Items.Count > byte.MaxValue || node.Positions.Count > byte.MaxValue || node.Present.Count > byte.MaxValue)
{
throw new ProtocolException($"Map node '{node.Id}' has too many items or positions for a u8 count.");
throw new ProtocolException($"Map node '{node.Id}' has too many items, positions or people for a u8 count.");
}
writer.WriteByte(node.Kind);
@@ -170,6 +182,23 @@ public static class ProtocolCodec
{
writer.WriteString(position);
}
if (HasActivity(node))
{
writer.WriteByte(1);
writer.WriteString(node.ActivitySubject);
writer.WriteString(node.ActivityClass);
}
else
{
writer.WriteByte(0);
}
writer.WriteByte((byte)node.Present.Count);
foreach (var person in node.Present)
{
writer.WriteString(person);
}
}
return writer.Position;
@@ -283,12 +312,41 @@ public static class ProtocolCodec
positions[position] = reader.ReadString();
}
nodes[i] = new MapSnapshotNode(kind, id, parentId, name, pupilSlots, items, positions);
var hasActivity = reader.ReadByte() != 0;
var activitySubject = "";
var activityClass = "";
if (hasActivity)
{
activitySubject = reader.ReadString();
activityClass = reader.ReadString();
}
var characterCount = reader.ReadByte();
var characters = new string[characterCount];
for (var person = 0; person < characterCount; person++)
{
characters[person] = reader.ReadString();
}
nodes[i] = new MapSnapshotNode(
kind,
id,
parentId,
name,
pupilSlots,
items,
positions,
activitySubject,
activityClass,
characters);
}
return new ServerMapSnapshotMessage(schoolId, nodes);
}
private static bool HasActivity(MapSnapshotNode node) =>
node.ActivitySubject.Length > 0 || node.ActivityClass.Length > 0;
/// <summary>Matches <see cref="PacketWriter.WriteString"/>: a <c>u16</c> length plus UTF-8.</summary>
private static int StringSize(string value) => sizeof(ushort) + Encoding.UTF8.GetByteCount(value);