Files
h-school/src/HSchool.Protocol/Messages.cs
T

149 lines
5.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace HSchool.Protocol;
/// <summary>
/// First frame from the client. <paramref name="Locale"/> is <see cref="ProtocolConstants.LocaleRussian"/>
/// or <see cref="ProtocolConstants.LocaleEnglish"/> — the same language the catalog HTTP API uses.
/// </summary>
public readonly record struct ClientHelloMessage(byte ProtocolVersion, byte Locale);
/// <summary>Round-trip probe; the server mirrors <paramref name="ClientTimeMs"/> back untouched.</summary>
public readonly record struct ClientPingMessage(long ClientTimeMs);
/// <summary>Asks for clock updates of one school. Starts its calendar running.</summary>
public readonly record struct ClientOpenSchoolMessage(int SchoolId);
/// <summary>
/// Play or pause the open school. Running and speed are separate messages on purpose: a button
/// that also resent the other field would clobber it with whatever the client last saw.
/// </summary>
public readonly record struct ClientSetRunningMessage(bool Running);
/// <summary>Change the speed of the open school without touching whether it runs.</summary>
public readonly record struct ClientSetSpeedMessage(byte SpeedIndex);
/// <summary>Sent once per connection, before anything else.</summary>
public readonly record struct ServerWelcomeMessage(byte ProtocolVersion, byte TickRate, byte MaxSchools);
/// <summary>Answer to <see cref="ClientPingMessage"/>, carrying the current server tick.</summary>
public readonly record struct ServerPongMessage(long ClientTimeMs, uint ServerTick);
/// <summary>
/// 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.
/// <paramref name="TemperatureTenths"/> is outdoor °C × 10. <paramref name="Precipitation"/> is
/// <see cref="PrecipitationKind"/>.
/// </summary>
public readonly record struct ServerClockMessage(
int SchoolId,
long GameTimeUnixMs,
bool Running,
byte SpeedIndex,
bool SkipAllowed = false,
long SkipTargetUnixMs = 0,
short TemperatureTenths = 0,
byte Precipitation = 0);
/// <summary>Outdoor precipitation on the clock frame. Below 0 °C the same weather roll is snow.</summary>
public static class PrecipitationKind
{
public const byte None = 0;
public const byte Rain = 1;
public const byte Snow = 2;
}
/// <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);
/// <summary>
/// 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. Live occupancy lives on <see cref="ServerPresenceMessage"/>.
/// </summary>
public sealed record MapSnapshotNode(
byte Kind,
string Id,
string ParentId,
string Name,
ushort PupilSlots,
IReadOnlyList<MapSnapshotItem> Items,
IReadOnlyList<string> Positions);
/// <summary>One stacked thing in a room. <paramref name="Count"/> is 1255.</summary>
public sealed record MapSnapshotItem(string Name, byte Count);
/// <summary>
/// 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>Closes one notice by id. Info is not stored; pausing dismiss is phase 65.</summary>
public readonly record struct ClientDismissNoticeMessage(uint Id);
/// <summary>Wire values for <see cref="ServerNoticeMessage.Severity"/>.</summary>
public static class NoticeSeverity
{
public const byte Info = 0;
public const byte Warning = 1;
public const byte Error = 2;
}
/// <summary>
/// One school event for open clients. <paramref name="PersonId"/> is 0 when nobody is in frame.
/// Info toasts are not replayed on OpenSchool.
/// </summary>
public readonly record struct ServerNoticeMessage(
uint Id,
string DefName,
byte Severity,
bool Pause,
uint TtlMs,
uint PersonId = 0);
/// <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. Talk members and
/// topic are ids; an empty list means the person is not in a circle.
/// </summary>
public sealed record PresencePerson(
string Id,
string NodeId,
byte State,
IReadOnlyList<string> TalkMemberIds,
string TalkTopicId)
{
public PresencePerson(string id, string nodeId, byte state)
: this(id, nodeId, 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);