149 lines
5.9 KiB
C#
149 lines
5.9 KiB
C#
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 1–255.</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);
|