namespace HSchool.Protocol;
///
/// First frame from the client. is
/// or — the same language the catalog HTTP API uses.
///
public readonly record struct ClientHelloMessage(byte ProtocolVersion, byte Locale);
/// Round-trip probe; the server mirrors back untouched.
public readonly record struct ClientPingMessage(long ClientTimeMs);
/// Asks for clock updates of one school. Starts its calendar running.
public readonly record struct ClientOpenSchoolMessage(int SchoolId);
///
/// 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.
///
public readonly record struct ClientSetRunningMessage(bool Running);
/// Change the speed of the open school without touching whether it runs.
public readonly record struct ClientSetSpeedMessage(byte SpeedIndex);
/// Sent once per connection, before anything else.
public readonly record struct ServerWelcomeMessage(byte ProtocolVersion, byte TickRate, byte MaxSchools);
/// Answer to , carrying the current server tick.
public readonly record struct ServerPongMessage(long ClientTimeMs, uint ServerTick);
///
/// State of the open school's calendar, sent every tick.
/// is the in-game date as milliseconds since the Unix epoch,
/// interpreted as UTC — the game calendar has no time zone.
/// is the server's verdict; the client must not recompute it.
/// is 0 when skip is refused.
/// is outdoor °C × 10. is
/// .
///
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);
/// Outdoor precipitation on the clock frame. Below 0 °C the same weather roll is snow.
public static class PrecipitationKind
{
public const byte None = 0;
public const byte Rain = 1;
public const byte Snow = 2;
}
/// The open school no longer exists (deleted from another tab); the client returns to the menu.
public readonly record struct ServerSchoolGoneMessage(int SchoolId);
///
/// Tree node in a map snapshot. Kind is 0 territory, 1 building, 2 floor, 3 room.
/// is empty for the yard.
/// is how many pupils can take a lesson here — summed from things
/// on the server, not by the client. Live occupancy lives on .
///
public sealed record MapSnapshotNode(
byte Kind,
string Id,
string ParentId,
string Name,
ushort PupilSlots,
IReadOnlyList Items,
IReadOnlyList Positions);
/// One stacked thing in a room. is 1–255.
public sealed record MapSnapshotItem(string Name, byte Count);
///
/// 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.
///
public sealed record ServerMapSnapshotMessage(int SchoolId, IReadOnlyList Nodes);
/// Jump empty nights, weekends and holidays. The server re-checks the conditions.
public readonly record struct ClientSkipEmptyMessage;
/// Closes one notice by id. Info is not stored; pausing dismiss is phase 65.
public readonly record struct ClientDismissNoticeMessage(uint Id);
/// Wire values for .
public static class NoticeSeverity
{
public const byte Info = 0;
public const byte Warning = 1;
public const byte Error = 2;
}
///
/// One school event for open clients. is 0 when nobody is in frame.
/// Info toasts are not replayed on OpenSchool.
///
public readonly record struct ServerNoticeMessage(
uint Id,
string DefName,
byte Severity,
bool Pause,
uint TtlMs,
uint PersonId = 0);
/// Where people are: 1 in a node, 2 walking through it. Off campus is omitted.
public static class PresenceState
{
public const byte Here = 1;
public const byte Walking = 2;
}
/// One occupied (or currently taught) map node in a presence frame.
public sealed record PresenceNode(
string Id,
ushort Count,
string ActivitySubject = "",
string ActivityClass = "");
///
/// 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.
///
public sealed record PresencePerson(
string Id,
string NodeId,
byte State,
IReadOnlyList TalkMemberIds,
string TalkTopicId)
{
public PresencePerson(string id, string nodeId, byte state)
: this(id, nodeId, state, [], "")
{
}
}
///
/// 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.
///
public sealed record ServerPresenceMessage(
int SchoolId,
IReadOnlyList Nodes,
IReadOnlyList People);