Update wire protocol to version 7 and enhance presence management features
- Bumped the wire protocol version to 7, reflecting significant changes in the communication structure. - Introduced a new presence message type for real-time occupancy updates, including node activity and individual presence states. - Updated the API to include a directory endpoint for fetching short id→name mappings, improving client-side name resolution. - Revised the map snapshot structure to be static, with people and current lessons now handled through the presence stream. - Enhanced client-side handling of presence updates, including UI adjustments to display live occupancy and activity. - Updated documentation to reflect the new protocol features and changes in presence management. - Added tests to validate the new presence functionalities and ensure robust handling of real-time data.
This commit is contained in:
@@ -31,12 +31,16 @@ public readonly record struct ServerPongMessage(long ClientTimeMs, uint ServerTi
|
||||
/// State of the open school's calendar, sent every tick.
|
||||
/// <paramref name="GameTimeUnixMs"/> is the in-game date as milliseconds since the Unix epoch,
|
||||
/// interpreted as UTC — the game calendar has no time zone.
|
||||
/// <paramref name="SkipAllowed"/> is the server's verdict; the client must not recompute it.
|
||||
/// <paramref name="SkipTargetUnixMs"/> is 0 when skip is refused.
|
||||
/// </summary>
|
||||
public readonly record struct ServerClockMessage(
|
||||
int SchoolId,
|
||||
long GameTimeUnixMs,
|
||||
bool Running,
|
||||
byte SpeedIndex);
|
||||
byte SpeedIndex,
|
||||
bool SkipAllowed = false,
|
||||
long SkipTargetUnixMs = 0);
|
||||
|
||||
/// <summary>The open school no longer exists (deleted from another tab); the client returns to the menu.</summary>
|
||||
public readonly record struct ServerSchoolGoneMessage(int SchoolId);
|
||||
@@ -45,9 +49,7 @@ public readonly record struct ServerSchoolGoneMessage(int SchoolId);
|
||||
/// Tree node in a map snapshot. Kind is <c>0</c> territory, <c>1</c> building, <c>2</c> floor, <c>3</c> room.
|
||||
/// <paramref name="ParentId"/> is empty for the yard.
|
||||
/// <paramref name="PupilSlots"/> is how many pupils can take a lesson here — summed from things
|
||||
/// on the server, not by the client.
|
||||
/// <paramref name="ActivitySubject"/> and <paramref name="ActivityClass"/> are empty when the
|
||||
/// room is free. <paramref name="Characters"/> are the people the timetable puts there right now.
|
||||
/// on the server, not by the client. Live occupancy lives on <see cref="ServerPresenceMessage"/>.
|
||||
/// </summary>
|
||||
public sealed record MapSnapshotNode(
|
||||
byte Kind,
|
||||
@@ -56,19 +58,42 @@ public sealed record MapSnapshotNode(
|
||||
string Name,
|
||||
ushort PupilSlots,
|
||||
IReadOnlyList<MapSnapshotItem> Items,
|
||||
IReadOnlyList<string> Positions,
|
||||
string ActivitySubject = "",
|
||||
string ActivityClass = "",
|
||||
IReadOnlyList<string>? Characters = null)
|
||||
{
|
||||
public IReadOnlyList<string> Present => Characters ?? [];
|
||||
}
|
||||
IReadOnlyList<string> Positions);
|
||||
|
||||
/// <summary>One stacked thing in a room. <paramref name="Count"/> is 1–255.</summary>
|
||||
public sealed record MapSnapshotItem(string Name, byte Count);
|
||||
|
||||
/// <summary>
|
||||
/// One school's map, labelled in the Hello locale. Sent when that school is opened and again
|
||||
/// when the current lesson slot changes. Occupancy is computed from the timetable and the clock.
|
||||
/// One school's map, labelled in the Hello locale. Sent once when that school is opened.
|
||||
/// Structure only — people and the current lesson ride the presence stream.
|
||||
/// </summary>
|
||||
public sealed record ServerMapSnapshotMessage(int SchoolId, IReadOnlyList<MapSnapshotNode> Nodes);
|
||||
|
||||
/// <summary>Jump empty nights, weekends and holidays. The server re-checks the conditions.</summary>
|
||||
public readonly record struct ClientSkipEmptyMessage;
|
||||
|
||||
/// <summary>Where people are: 1 in a node, 2 walking through it. Off campus is omitted.</summary>
|
||||
public static class PresenceState
|
||||
{
|
||||
public const byte Here = 1;
|
||||
public const byte Walking = 2;
|
||||
}
|
||||
|
||||
/// <summary>One occupied (or currently taught) map node in a presence frame.</summary>
|
||||
public sealed record PresenceNode(
|
||||
string Id,
|
||||
ushort Count,
|
||||
string ActivitySubject = "",
|
||||
string ActivityClass = "");
|
||||
|
||||
/// <summary>One on-campus person. Names are resolved over HTTP, not on this frame.</summary>
|
||||
public sealed record PresencePerson(string Id, string NodeId, byte State);
|
||||
|
||||
/// <summary>
|
||||
/// Live occupancy of an open school, about twice a second. Counts and people cover the whole
|
||||
/// map; the client filters to the selected tree node.
|
||||
/// </summary>
|
||||
public sealed record ServerPresenceMessage(
|
||||
int SchoolId,
|
||||
IReadOnlyList<PresenceNode> Nodes,
|
||||
IReadOnlyList<PresencePerson> People);
|
||||
|
||||
Reference in New Issue
Block a user