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
@@ -246,7 +246,120 @@ public class ProtocolCodecTests
Assert.Equal("f0.c0", read.People[0].Id);
Assert.Equal("classroom-101", read.People[0].NodeId);
Assert.Equal(PresenceState.Here, read.People[0].State);
Assert.Empty(read.People[0].TalkMemberIds);
Assert.Equal("", read.People[0].TalkTopicId);
Assert.Equal(PresenceState.Walking, read.People[1].State);
Assert.Empty(read.People[1].TalkMemberIds);
}
[Fact]
public void Presence_PersonWithoutCircle_WritesEmptyMemberListAndEmptyTopic()
{
var message = new ServerPresenceMessage(
1,
[],
[new PresencePerson("f0.c0", "corridor-1", PresenceState.Here)]);
var buffer = new byte[ProtocolCodec.PresenceSize(message)];
var length = ProtocolCodec.WritePresence(buffer, message);
Assert.Equal((byte)MessageType.ServerPresence, buffer[0]);
Assert.Equal(1, BitConverter.ToInt32(buffer.AsSpan(1, 4)));
Assert.Equal((ushort)0, BitConverter.ToUInt16(buffer.AsSpan(5, 2)));
Assert.Equal((ushort)1, BitConverter.ToUInt16(buffer.AsSpan(7, 2)));
var offset = 9;
offset = AssertWireString(buffer, offset, "f0.c0");
offset = AssertWireString(buffer, offset, "corridor-1");
Assert.Equal(PresenceState.Here, buffer[offset]);
offset += 1;
Assert.Equal(0, buffer[offset]);
offset += 1;
offset = AssertWireString(buffer, offset, "");
Assert.Equal(length, offset);
Assert.Equal(length, ProtocolCodec.PresenceSize(message));
var read = ProtocolCodec.ReadPresence(buffer.AsSpan(0, length));
Assert.Empty(read.People[0].TalkMemberIds);
Assert.Equal("", read.People[0].TalkTopicId);
Assert.Equal(message.People[0].Id, read.People[0].Id);
Assert.Equal(message.People[0].NodeId, read.People[0].NodeId);
Assert.Equal(message.People[0].State, read.People[0].State);
}
[Fact]
public void Presence_PersonWithCircle_RoundTripsMemberIdsAndTopic()
{
var members = new[] { "f0.c0", "f0.c1" };
var message = new ServerPresenceMessage(
1,
[new PresenceNode("corridor-1", 2)],
[new PresencePerson("f0.c0", "corridor-1", PresenceState.Here, members, "TopicSport")]);
var buffer = new byte[ProtocolCodec.PresenceSize(message)];
var length = ProtocolCodec.WritePresence(buffer, message);
Assert.Equal((byte)MessageType.ServerPresence, buffer[0]);
Assert.Equal(1, BitConverter.ToInt32(buffer.AsSpan(1, 4)));
Assert.Equal((ushort)1, BitConverter.ToUInt16(buffer.AsSpan(5, 2)));
var offset = 7;
offset = AssertWireString(buffer, offset, "corridor-1");
Assert.Equal((ushort)2, BitConverter.ToUInt16(buffer.AsSpan(offset, 2)));
offset += 2;
Assert.Equal(0, buffer[offset]);
offset += 1;
Assert.Equal((ushort)1, BitConverter.ToUInt16(buffer.AsSpan(offset, 2)));
offset += 2;
offset = AssertWireString(buffer, offset, "f0.c0");
offset = AssertWireString(buffer, offset, "corridor-1");
Assert.Equal(PresenceState.Here, buffer[offset]);
offset += 1;
Assert.Equal(2, buffer[offset]);
offset += 1;
offset = AssertWireString(buffer, offset, "f0.c0");
offset = AssertWireString(buffer, offset, "f0.c1");
offset = AssertWireString(buffer, offset, "TopicSport");
Assert.Equal(length, offset);
Assert.Equal(length, ProtocolCodec.PresenceSize(message));
var read = ProtocolCodec.ReadPresence(buffer.AsSpan(0, length));
Assert.Equal(["f0.c0", "f0.c1"], read.People[0].TalkMemberIds);
Assert.Equal("TopicSport", read.People[0].TalkTopicId);
Assert.Equal(message.SchoolId, read.SchoolId);
Assert.Equal(message.People[0].Id, read.People[0].Id);
}
[Fact]
public void Presence_DoesNotContainPersonDisplayName()
{
var message = new ServerPresenceMessage(
1,
[],
[new PresencePerson(
"f0.c0",
"corridor-1",
PresenceState.Here,
["f0.c0", "f0.c1"],
"TopicSport")]);
var buffer = new byte[ProtocolCodec.PresenceSize(message)];
ProtocolCodec.WritePresence(buffer, message);
var text = System.Text.Encoding.UTF8.GetString(buffer);
Assert.DoesNotContain("Мария", text);
Assert.DoesNotContain("Иванова", text);
Assert.DoesNotContain("Маша", text);
Assert.Contains("f0.c0", text);
Assert.Contains("TopicSport", text);
}
private static int AssertWireString(byte[] buffer, int offset, string expected)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(expected);
Assert.Equal((ushort)bytes.Length, BitConverter.ToUInt16(buffer.AsSpan(offset, 2)));
Assert.Equal(bytes, buffer.AsSpan(offset + 2, bytes.Length).ToArray());
return offset + 2 + bytes.Length;
}
[Fact]