using HSchool.Content; using HSchool.People; using HSchool.Protocol; using HSchool.Schedule; using HSchool.Simulation; namespace HSchool.Server.Game; /// /// Builds a presence frame from stored places plus the current lesson. Occupancy is state; /// the lesson labels are still derived from the timetable so an empty classroom during a period /// still shows what should be happening there. /// internal static class PresenceFrame { public static ServerPresenceMessage Build(School school, int weekDays, string locale) { var people = new List(); var counts = new Dictionary(StringComparer.Ordinal); foreach (var row in school.CapturePresence()) { if (row.NodeId is null) { continue; } var walking = row.Path.Count > 0 || row.RemainingMinutes > 0; var circle = school.TalkCircleOf(row.PersonId); var summonPhase = school.DirectorSummonPresencePhase(row.PersonId); people.Add(new PresencePerson( row.PersonId, row.NodeId, walking ? PresenceState.Walking : PresenceState.Here, circle?.MemberIds ?? [], circle?.TopicId ?? "", summonPhase)); counts[row.NodeId] = counts.GetValueOrDefault(row.NodeId) + 1; } people.Sort((left, right) => StringComparer.Ordinal.Compare(left.Id, right.Id)); var activity = ActivityByRoom(school, weekDays, locale); var nodeIds = counts.Keys .Concat(activity.Keys) .Distinct(StringComparer.Ordinal) .OrderBy(id => id, StringComparer.Ordinal); var nodes = new List(); foreach (var id in nodeIds) { var count = (ushort)Math.Min(counts.GetValueOrDefault(id), ushort.MaxValue); var subject = ""; var classLabel = ""; if (activity.TryGetValue(id, out var labels)) { subject = labels.Subject; classLabel = labels.Class; } nodes.Add(new PresenceNode(id, count, subject, classLabel)); } return new ServerPresenceMessage(school.Id, nodes, people); } private static Dictionary ActivityByRoom( School school, int weekDays, string locale) { var labels = new Dictionary(StringComparer.Ordinal); if (school.Timetable is null || school.Roster is null || school.Catalog is null) { return labels; } var occurring = TimetableClock.OccurringAt( school.Timetable, school.Catalog, school.Clock.Time, weekDays); if (occurring.Count == 0) { return labels; } var catalog = school.Catalog; var classes = school.Roster.Classes.ToDictionary(item => item.Id, StringComparer.Ordinal); foreach (var lesson in occurring) { classes.TryGetValue(lesson.ClassId, out var schoolClass); var subjectLabel = catalog.Subjects.TryGetValue(lesson.Subject, out var subject) ? catalog.Label(locale, subject) : lesson.Subject; var classLabel = schoolClass is null ? lesson.ClassId : $"{schoolClass.Year}{schoolClass.Letter}"; labels[lesson.RoomId] = (subjectLabel, classLabel); } return labels; } }