Add EventDef notices, morning and lesson bells, and info toasts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 20:46:33 +03:00
co-authored by Cursor
parent ea3f5c7bf1
commit a554738084
38 changed files with 952 additions and 22 deletions
+2
View File
@@ -15,6 +15,7 @@ public enum MessageType : byte
ClientSetRunning = 0x05,
ClientSetSpeed = 0x06,
ClientSkipEmpty = 0x07,
ClientDismissNotice = 0x08,
ServerWelcome = 0x81,
ServerPong = 0x82,
@@ -22,4 +23,5 @@ public enum MessageType : byte
ServerSchoolGone = 0x84,
ServerMapSnapshot = 0x85,
ServerPresence = 0x86,
ServerNotice = 0x87,
}
+23
View File
@@ -84,6 +84,29 @@ public sealed record ServerMapSnapshotMessage(int SchoolId, IReadOnlyList<MapSna
/// <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
{
+44
View File
@@ -70,6 +70,14 @@ public static class ProtocolCodec
return writer.Position;
}
public static int WriteDismissNotice(Span<byte> destination, in ClientDismissNoticeMessage message)
{
var writer = new PacketWriter(destination);
writer.WriteMessageType(MessageType.ClientDismissNotice);
writer.WriteUInt32(message.Id);
return writer.Position;
}
public static int WriteWelcome(Span<byte> destination, in ServerWelcomeMessage message)
{
var writer = new PacketWriter(destination);
@@ -271,6 +279,22 @@ public static class ProtocolCodec
return writer.Position;
}
public static int NoticeSize(in ServerNoticeMessage message) =>
sizeof(byte) + sizeof(uint) + StringSize(message.DefName) + sizeof(byte) + sizeof(byte) + sizeof(uint) + sizeof(uint);
public static int WriteNotice(Span<byte> destination, in ServerNoticeMessage message)
{
var writer = new PacketWriter(destination);
writer.WriteMessageType(MessageType.ServerNotice);
writer.WriteUInt32(message.Id);
writer.WriteString(message.DefName);
writer.WriteByte(message.Severity);
writer.WriteByte(message.Pause ? (byte)1 : (byte)0);
writer.WriteUInt32(message.TtlMs);
writer.WriteUInt32(message.PersonId);
return writer.Position;
}
public static MessageType PeekMessageType(ReadOnlySpan<byte> source) =>
source.IsEmpty ? MessageType.None : (MessageType)source[0];
@@ -441,6 +465,26 @@ public static class ProtocolCodec
return new ServerPresenceMessage(schoolId, nodes, people);
}
public static ClientDismissNoticeMessage ReadDismissNotice(ReadOnlySpan<byte> source)
{
var reader = new PacketReader(source);
Expect(ref reader, MessageType.ClientDismissNotice);
return new ClientDismissNoticeMessage(reader.ReadUInt32());
}
public static ServerNoticeMessage ReadNotice(ReadOnlySpan<byte> source)
{
var reader = new PacketReader(source);
Expect(ref reader, MessageType.ServerNotice);
var id = reader.ReadUInt32();
var defName = reader.ReadString();
var severity = reader.ReadByte();
var pause = reader.ReadByte() != 0;
var ttlMs = reader.ReadUInt32();
var personId = reader.ReadUInt32();
return new ServerNoticeMessage(id, defName, severity, pause, ttlMs, personId);
}
private static bool HasPresenceActivity(PresenceNode node) =>
node.ActivitySubject.Length > 0 || node.ActivityClass.Length > 0;
+1 -1
View File
@@ -4,7 +4,7 @@ namespace HSchool.Protocol;
public static class ProtocolConstants
{
/// <summary>Bumped on every breaking change to the binary layout.</summary>
public const byte Version = 9;
public const byte Version = 10;
/// <summary>Upper bound for a single WebSocket frame accepted by the server.</summary>
public const int MaxMessageSize = 8 * 1024;