Bump protocol to v9 and carry talk-circle ids on the presence frame.

Names stay off the wire; a person not in a circle writes an empty member list and topic.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 10:42:11 +03:00
co-authored by Cursor
parent 52a4104f68
commit 1a21a64b4b
7 changed files with 321 additions and 10 deletions
+16 -2
View File
@@ -98,8 +98,22 @@ public sealed record PresenceNode(
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>
/// 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
+33 -1
View File
@@ -202,6 +202,14 @@ public static class ProtocolCodec
foreach (var person in message.People)
{
size += StringSize(person.Id) + StringSize(person.NodeId) + sizeof(byte);
var members = person.TalkMemberIds ?? [];
size += sizeof(byte);
foreach (var memberId in members)
{
size += StringSize(memberId);
}
size += StringSize(person.TalkTopicId ?? "");
}
return size;
@@ -242,9 +250,22 @@ public static class ProtocolCodec
writer.WriteUInt16((ushort)message.People.Count);
foreach (var person in message.People)
{
var members = person.TalkMemberIds ?? [];
if (members.Count > byte.MaxValue)
{
throw new ProtocolException($"Presence person {person.Id} has {members.Count} talk members; u8 count cannot hold it.");
}
writer.WriteString(person.Id);
writer.WriteString(person.NodeId);
writer.WriteByte(person.State);
writer.WriteByte((byte)members.Count);
foreach (var memberId in members)
{
writer.WriteString(memberId);
}
writer.WriteString(person.TalkTopicId ?? "");
}
return writer.Position;
@@ -403,7 +424,18 @@ public static class ProtocolCodec
var people = new PresencePerson[personCount];
for (var i = 0; i < personCount; i++)
{
people[i] = new PresencePerson(reader.ReadString(), reader.ReadString(), reader.ReadByte());
var id = reader.ReadString();
var nodeId = reader.ReadString();
var state = reader.ReadByte();
var memberCount = reader.ReadByte();
var members = new string[memberCount];
for (var member = 0; member < memberCount; member++)
{
members[member] = reader.ReadString();
}
var topicId = reader.ReadString();
people[i] = new PresencePerson(id, nodeId, state, members, topicId);
}
return new ServerPresenceMessage(schoolId, nodes, people);
+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 = 8;
public const byte Version = 9;
/// <summary>Upper bound for a single WebSocket frame accepted by the server.</summary>
public const int MaxMessageSize = 8 * 1024;