Show director summons as info toasts and presence queue labels.
Corridor waiters need a summon-phase byte on presence (v11); EventDef DirectorSummoned is info-only so clocks keep running.
This commit is contained in:
@@ -33,6 +33,15 @@ public class EventDefTests
|
||||
Assert.Equal(EventActions.GenerateImage, catalog.Events["GenerationFailed"].Action);
|
||||
Assert.Equal("Начало дня", catalog.Label("ru", catalog.Events["DayStarted"]));
|
||||
Assert.Equal("The day has started", catalog.Label("en", catalog.Events["DayStarted"]));
|
||||
|
||||
Assert.True(catalog.Events.ContainsKey("DirectorSummoned"));
|
||||
Assert.Equal(EventSeverities.Info, catalog.Events["DirectorSummoned"].Severity);
|
||||
Assert.False(catalog.Events["DirectorSummoned"].Pause);
|
||||
Assert.Equal(8000, catalog.Events["DirectorSummoned"].TtlMs);
|
||||
Assert.Equal(EventTriggers.DirectorSummon, catalog.Events["DirectorSummoned"].Trigger);
|
||||
Assert.Equal(EventActions.None, catalog.Events["DirectorSummoned"].Action);
|
||||
Assert.Equal("Ученика вызвали к директору", catalog.Label("ru", catalog.Events["DirectorSummoned"]));
|
||||
Assert.Equal("A pupil was summoned to the principal", catalog.Label("en", catalog.Events["DirectorSummoned"]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -285,8 +285,10 @@ public class ProtocolCodecTests
|
||||
Assert.Equal(PresenceState.Here, read.People[0].State);
|
||||
Assert.Empty(read.People[0].TalkMemberIds);
|
||||
Assert.Equal("", read.People[0].TalkTopicId);
|
||||
Assert.Equal(PresenceSummonPhase.None, read.People[0].SummonPhase);
|
||||
Assert.Equal(PresenceState.Walking, read.People[1].State);
|
||||
Assert.Empty(read.People[1].TalkMemberIds);
|
||||
Assert.Equal(PresenceSummonPhase.None, read.People[1].SummonPhase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -313,12 +315,15 @@ public class ProtocolCodecTests
|
||||
Assert.Equal(0, buffer[offset]);
|
||||
offset += 1;
|
||||
offset = AssertWireString(buffer, offset, "");
|
||||
Assert.Equal(PresenceSummonPhase.None, buffer[offset]);
|
||||
offset += 1;
|
||||
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(PresenceSummonPhase.None, read.People[0].SummonPhase);
|
||||
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);
|
||||
@@ -357,16 +362,52 @@ public class ProtocolCodecTests
|
||||
offset = AssertWireString(buffer, offset, "f0.c0");
|
||||
offset = AssertWireString(buffer, offset, "f0.c1");
|
||||
offset = AssertWireString(buffer, offset, "TopicSport");
|
||||
Assert.Equal(PresenceSummonPhase.None, buffer[offset]);
|
||||
offset += 1;
|
||||
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(PresenceSummonPhase.None, read.People[0].SummonPhase);
|
||||
Assert.Equal(message.SchoolId, read.SchoolId);
|
||||
Assert.Equal(message.People[0].Id, read.People[0].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Presence_SummonPhase_RoundTripsAndMatchesByteLayout()
|
||||
{
|
||||
var message = new ServerPresenceMessage(
|
||||
1,
|
||||
[],
|
||||
[new PresencePerson(
|
||||
"f0.c0",
|
||||
"corridor-1",
|
||||
PresenceState.Here,
|
||||
[],
|
||||
"",
|
||||
PresenceSummonPhase.Waiting)]);
|
||||
var buffer = new byte[ProtocolCodec.PresenceSize(message)];
|
||||
|
||||
var length = ProtocolCodec.WritePresence(buffer, message);
|
||||
|
||||
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(PresenceSummonPhase.Waiting, buffer[offset]);
|
||||
offset += 1;
|
||||
Assert.Equal(length, offset);
|
||||
|
||||
var read = ProtocolCodec.ReadPresence(buffer.AsSpan(0, length));
|
||||
Assert.Equal(PresenceSummonPhase.Waiting, Assert.Single(read.People).SummonPhase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Presence_DoesNotContainPersonDisplayName()
|
||||
{
|
||||
|
||||
@@ -95,6 +95,18 @@ public class NoticeBoardTests
|
||||
Assert.Equal("avatar", sticky.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectorSummonedInfo_IsNotStickyInSave()
|
||||
{
|
||||
var board = new NoticeBoard(maxSticky: 8);
|
||||
|
||||
Assert.True(board.TryPost(DirectorSummoned(), personKey: "a0.p0", kind: "", out var info));
|
||||
Assert.Equal("DirectorSummoned", info.DefName);
|
||||
Assert.False(info.Pause);
|
||||
Assert.Empty(board.Sticky);
|
||||
Assert.DoesNotContain(board.ToSave(), row => row.DefName == "DirectorSummoned");
|
||||
}
|
||||
|
||||
private static EventDef Info() => new()
|
||||
{
|
||||
DefName = "DayStarted",
|
||||
@@ -105,6 +117,16 @@ public class NoticeBoardTests
|
||||
Action = EventActions.None,
|
||||
};
|
||||
|
||||
private static EventDef DirectorSummoned() => new()
|
||||
{
|
||||
DefName = "DirectorSummoned",
|
||||
Severity = EventSeverities.Info,
|
||||
Pause = false,
|
||||
TtlMs = 8000,
|
||||
Trigger = EventTriggers.DirectorSummon,
|
||||
Action = EventActions.None,
|
||||
};
|
||||
|
||||
private static EventDef Pausing() => new()
|
||||
{
|
||||
DefName = "GenerationFailed",
|
||||
|
||||
@@ -112,7 +112,16 @@ public class DirectorSummonTests
|
||||
name => name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.DoesNotContain(
|
||||
typeof(School).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public),
|
||||
method => method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
|
||||
method => method.Name.StartsWith("Set", StringComparison.Ordinal)
|
||||
&& method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.DoesNotContain(
|
||||
typeof(School).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public),
|
||||
method => method.Name.StartsWith("Send", StringComparison.Ordinal)
|
||||
&& method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.DoesNotContain(
|
||||
typeof(School).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public),
|
||||
method => method.Name.StartsWith("Enqueue", StringComparison.Ordinal)
|
||||
&& method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -123,6 +132,67 @@ public class DirectorSummonTests
|
||||
Assert.Equal("corridor-1", DirectorSummons.WaitNodeId(map));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QualifyingSummon_RaisesDirectorSummonWorldEvent()
|
||||
{
|
||||
var (school, first, _, _) = TwoPupilsPrincipalAndTeacherOnBreak();
|
||||
using (school)
|
||||
{
|
||||
school.DrainWorldEvents();
|
||||
Assert.True(DirectorSummonSystem.TryEnqueue(school, first.Id, chance: 1f));
|
||||
|
||||
var facts = school.DrainWorldEvents();
|
||||
var fact = Assert.Single(facts, row => row.Trigger.Equals(EventTriggers.DirectorSummon, StringComparison.Ordinal));
|
||||
Assert.Equal(first.Id, fact.PersonKey);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueueAdvancesWithoutOpenClient()
|
||||
{
|
||||
var (school, first, second, _) = TwoPupilsPrincipalAndTeacherOnBreak();
|
||||
using (school)
|
||||
{
|
||||
Assert.True(DirectorSummonSystem.TryEnqueue(school, first.Id, chance: 1f));
|
||||
Assert.True(DirectorSummonSystem.TryEnqueue(school, second.Id, chance: 1f));
|
||||
DirectorSummonSystem.Apply(school);
|
||||
|
||||
var head = school.DirectorSummons.OrderBy(row => row.Order).First();
|
||||
var next = school.DirectorSummons.OrderBy(row => row.Order).Skip(1).First();
|
||||
PlaceAt(school, head.PersonId, "principals-office");
|
||||
DirectorSummonSystem.Apply(school);
|
||||
Assert.True(ActivitySystem.IsPrincipalHearing(ActivityOf(school, head.PersonId)));
|
||||
|
||||
DirectorSummonSystem.CompleteHearing(school, head.PersonId);
|
||||
DirectorSummonSystem.Apply(school);
|
||||
|
||||
Assert.False(DirectorSummonSystem.IsSummoned(school, head.PersonId));
|
||||
Assert.True(DirectorSummonSystem.IsSummoned(school, next.PersonId));
|
||||
PlaceAt(school, next.PersonId, "corridor-1");
|
||||
Assert.Equal(DirectorSummons.PresenceWaiting, school.DirectorSummonPresencePhase(next.PersonId));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SummonPresencePhase_WaitingInCorridor_HearingInOffice()
|
||||
{
|
||||
var (school, first, second, _) = TwoPupilsPrincipalAndTeacherOnBreak();
|
||||
using (school)
|
||||
{
|
||||
Assert.True(DirectorSummonSystem.TryEnqueue(school, first.Id, chance: 1f));
|
||||
Assert.True(DirectorSummonSystem.TryEnqueue(school, second.Id, chance: 1f));
|
||||
DirectorSummonSystem.Apply(school);
|
||||
|
||||
var ordered = school.DirectorSummons.OrderBy(row => row.Order).ToArray();
|
||||
PlaceAt(school, ordered[0].PersonId, "principals-office");
|
||||
PlaceAt(school, ordered[1].PersonId, "corridor-1");
|
||||
DirectorSummonSystem.Apply(school);
|
||||
|
||||
Assert.Equal(DirectorSummons.PresenceHearing, school.DirectorSummonPresencePhase(ordered[0].PersonId));
|
||||
Assert.Equal(DirectorSummons.PresenceWaiting, school.DirectorSummonPresencePhase(ordered[1].PersonId));
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> QueueOrderFromFight(int seed)
|
||||
{
|
||||
var (school, first, second, _) = TwoPupilsPrincipalAndTeacherOnBreak(seed);
|
||||
|
||||
Reference in New Issue
Block a user