Update wire protocol to version 7 and enhance presence management features
- Bumped the wire protocol version to 7, reflecting significant changes in the communication structure. - Introduced a new presence message type for real-time occupancy updates, including node activity and individual presence states. - Updated the API to include a directory endpoint for fetching short id→name mappings, improving client-side name resolution. - Revised the map snapshot structure to be static, with people and current lessons now handled through the presence stream. - Enhanced client-side handling of presence updates, including UI adjustments to display live occupancy and activity. - Updated documentation to reflect the new protocol features and changes in presence management. - Added tests to validate the new presence functionalities and ensure robust handling of real-time data.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using HSchool.Content;
|
||||
using HSchool.People;
|
||||
using HSchool.Protocol;
|
||||
using HSchool.Schedule;
|
||||
using HSchool.Simulation;
|
||||
|
||||
namespace HSchool.Server.Game;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
internal static class PresenceFrame
|
||||
{
|
||||
public static ServerPresenceMessage Build(School school, int weekDays, string locale)
|
||||
{
|
||||
var people = new List<PresencePerson>();
|
||||
var counts = new Dictionary<string, int>(StringComparer.Ordinal);
|
||||
foreach (var row in school.CapturePresence())
|
||||
{
|
||||
if (row.NodeId is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var walking = row.Path.Count > 0 || row.RemainingMinutes > 0;
|
||||
people.Add(new PresencePerson(
|
||||
row.PersonId,
|
||||
row.NodeId,
|
||||
walking ? PresenceState.Walking : PresenceState.Here));
|
||||
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<PresenceNode>();
|
||||
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<string, (string Subject, string Class)> ActivityByRoom(
|
||||
School school,
|
||||
int weekDays,
|
||||
string locale)
|
||||
{
|
||||
var labels = new Dictionary<string, (string Subject, string Class)>(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user