Update wire protocol to version 6 and enhance timetable functionality
ci / server (push) Failing after 3m53s
ci / client (push) Successful in 15s

- Bumped the wire protocol version to 6, reflecting changes in the communication structure.
- Expanded the timetable API with new endpoints for fetching and managing lesson schedules, including `GET /api/schools/{id}/timetable` and `POST /api/schools/{id}/timetable/pin`.
- Updated the protocol documentation to include detailed descriptions of the new timetable features and message structures.
- Enhanced the client-side implementation to support the new timetable functionalities, including lesson pinning and unpinning.
- Revised server-side logic to handle timetable operations and ensure proper integration with existing school management features.
- Added tests to validate the new timetable functionalities and ensure robustness in handling lesson data.
This commit is contained in:
Leonid Pershin
2026-08-19 10:05:05 +03:00
parent 2011d12b1d
commit cad3068bac
30 changed files with 1621 additions and 43 deletions
@@ -12,6 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\HSchool.Content\HSchool.Content.csproj" />
<ProjectReference Include="..\HSchool.People\HSchool.People.csproj" />
<ProjectReference Include="..\HSchool.Schedule\HSchool.Schedule.csproj" />
</ItemGroup>
</Project>
+17
View File
@@ -1,6 +1,7 @@
using Arch.Core;
using HSchool.Content;
using HSchool.People;
using HSchool.Schedule;
namespace HSchool.Simulation;
@@ -76,6 +77,12 @@ public sealed class School : IDisposable
/// <summary>Name pack used to generate this school's people. Needed again on 1 September.</summary>
public string? NameSetId { get; private set; }
/// <summary>Last built table. Null until the worker installs people.</summary>
public Timetable? Timetable { get; private set; }
/// <summary>True after yearly intake until the worker rebuilds around remaining locks.</summary>
public bool TimetableDirty { get; private set; }
/// <summary>
/// Installs a roster that already matches the map. Spawns entities; does not write to disk.
/// </summary>
@@ -104,6 +111,15 @@ public sealed class School : IDisposable
Roster = roster;
Applicants = applicants;
RosterSpawner.Replace(World, roster);
TimetableDirty = true;
}
public void SetTimetable(Timetable timetable)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentNullException.ThrowIfNull(timetable);
Timetable = timetable;
TimetableDirty = false;
}
/// <summary>Runs one fixed step of the school: calendar, yearly intake, applicant refresh, then need decay.</summary>
@@ -145,6 +161,7 @@ public sealed class School : IDisposable
if (changed)
{
RosterSpawner.Replace(World, Roster);
TimetableDirty = true;
}
return changed;
@@ -0,0 +1,31 @@
using HSchool.Content;
using HSchool.People;
using HSchool.Schedule;
namespace HSchool.Simulation;
/// <summary>Turns a school's roster into planner input and back. The worker calls this, not the tick.</summary>
public static class SchoolTimetables
{
public static Timetable Build(
DefCatalog catalog,
MapLayout map,
Roster roster,
IReadOnlyList<LessonPlacement>? locked,
int weekDays)
{
ArgumentNullException.ThrowIfNull(catalog);
ArgumentNullException.ThrowIfNull(map);
ArgumentNullException.ThrowIfNull(roster);
var classes = roster.Classes
.Select(item => new PlannerClass(item.Id, item.Year, item.Letter, item.RoomId, item.PupilIds.Count))
.ToArray();
var teachers = roster.People
.Where(person => person.IsStaff && person.Subjects.Count > 0)
.Select(person => new PlannerTeacher(person.Id, person.Subjects))
.ToArray();
return TimetablePlanner.Build(catalog, map, classes, teachers, locked, weekDays);
}
}