Give each school its own people seed so composition no longer follows create order.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -55,6 +55,7 @@ internal static class SchoolEndpoints
|
||||
request.Map,
|
||||
request.NameSetId,
|
||||
request.NativeLanguage,
|
||||
request.Seed,
|
||||
NewCompletion<SchoolCreationOutcome>());
|
||||
commands.Enqueue(command);
|
||||
|
||||
@@ -490,12 +491,13 @@ internal sealed record CreateSchoolRequest(
|
||||
IReadOnlyList<string>? ModIds,
|
||||
MapLayout? Map,
|
||||
string? NameSetId,
|
||||
string? NativeLanguage);
|
||||
string? NativeLanguage,
|
||||
int? Seed);
|
||||
|
||||
internal sealed record SchoolResponse(int Id, string Name, DateTime GameTime, bool Running, byte SpeedIndex)
|
||||
internal sealed record SchoolResponse(int Id, string Name, DateTime GameTime, bool Running, byte SpeedIndex, int Seed)
|
||||
{
|
||||
public static SchoolResponse From(SchoolState school) =>
|
||||
new(school.Id, school.Name, school.GameTime, school.Running, school.SpeedIndex);
|
||||
new(school.Id, school.Name, school.GameTime, school.Running, school.SpeedIndex, school.Seed);
|
||||
}
|
||||
|
||||
/// <summary>Everything the main menu needs in one request.</summary>
|
||||
|
||||
@@ -18,6 +18,7 @@ internal abstract record GameCommand
|
||||
MapLayout? Map,
|
||||
string? NameSetId,
|
||||
string? NativeLanguage,
|
||||
int? Seed,
|
||||
TaskCompletionSource<SchoolCreationOutcome> Result) : GameCommand;
|
||||
|
||||
internal sealed record DeleteSchool(int SchoolId, TaskCompletionSource<bool> Result) : GameCommand;
|
||||
|
||||
@@ -342,9 +342,10 @@ internal sealed class GameLoopService(
|
||||
|
||||
var id = _nextId++;
|
||||
store.WriteNextId(_nextId);
|
||||
var nativeLanguage = NativeLanguages.Pick(names, id, command.NativeLanguage, rollIfOmitted: true);
|
||||
var seed = command.Seed ?? Random.Shared.Next();
|
||||
var nativeLanguage = NativeLanguages.Pick(names, seed, command.NativeLanguage, rollIfOmitted: true);
|
||||
|
||||
var worker = SpawnWorker(id, normalized, command.StartDate, running: true, ClockSpeed.DefaultIndex, isNew: true, packIds, command.Map, nameSetId, nativeLanguage);
|
||||
var worker = SpawnWorker(id, normalized, command.StartDate, running: true, ClockSpeed.DefaultIndex, isNew: true, packIds, command.Map, nameSetId, nativeLanguage, seed);
|
||||
Track(worker);
|
||||
worker.Start();
|
||||
|
||||
@@ -366,7 +367,7 @@ internal sealed class GameLoopService(
|
||||
throw;
|
||||
}
|
||||
|
||||
logger.LogInformation("School {SchoolId} \"{Name}\" created.", id, normalized);
|
||||
logger.LogInformation("School {SchoolId} \"{Name}\" created with seed {Seed}.", id, normalized, seed);
|
||||
command.Result.TrySetResult(new SchoolCreationOutcome(worker.Snapshot, SchoolCreationError.None));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -513,6 +514,7 @@ internal sealed class GameLoopService(
|
||||
save.Map,
|
||||
save.NameSetId,
|
||||
save.NativeLanguage,
|
||||
createSeed: null,
|
||||
save.Presence);
|
||||
worker.Start();
|
||||
|
||||
@@ -562,6 +564,7 @@ internal sealed class GameLoopService(
|
||||
MapLayout? map,
|
||||
string? nameSetId,
|
||||
string? nativeLanguage,
|
||||
int? createSeed = null,
|
||||
IReadOnlyList<PresenceSnapshot>? presence = null) =>
|
||||
new(
|
||||
id,
|
||||
@@ -574,6 +577,7 @@ internal sealed class GameLoopService(
|
||||
map,
|
||||
nameSetId,
|
||||
nativeLanguage,
|
||||
createSeed,
|
||||
presence,
|
||||
_options,
|
||||
clients,
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace HSchool.Server.Game;
|
||||
/// Immutable copy of a school, safe to hand to request threads. The live <c>School</c> object
|
||||
/// never leaves its worker thread.
|
||||
/// </summary>
|
||||
internal sealed record SchoolState(int Id, string Name, DateTime GameTime, bool Running, byte SpeedIndex);
|
||||
internal sealed record SchoolState(int Id, string Name, DateTime GameTime, bool Running, byte SpeedIndex, int Seed);
|
||||
|
||||
/// <summary>Everything the main menu needs in one read.</summary>
|
||||
internal sealed record SchoolsState(int MaxSchools, IReadOnlyList<SchoolState> Schools);
|
||||
|
||||
@@ -34,6 +34,7 @@ internal sealed class SchoolWorker
|
||||
private readonly MapLayout? _savedMap;
|
||||
private readonly string? _nameSetId;
|
||||
private string? _nativeLanguage;
|
||||
private readonly int? _createSeed;
|
||||
private readonly IReadOnlyList<PresenceSnapshot>? _savedPresence;
|
||||
private readonly Action<int> _onFailed;
|
||||
|
||||
@@ -67,6 +68,7 @@ internal sealed class SchoolWorker
|
||||
MapLayout? savedMap,
|
||||
string? nameSetId,
|
||||
string? nativeLanguage,
|
||||
int? createSeed,
|
||||
IReadOnlyList<PresenceSnapshot>? savedPresence,
|
||||
SimulationOptions options,
|
||||
ClientRegistry clients,
|
||||
@@ -86,6 +88,7 @@ internal sealed class SchoolWorker
|
||||
_savedMap = savedMap;
|
||||
_nameSetId = nameSetId;
|
||||
_nativeLanguage = nativeLanguage;
|
||||
_createSeed = createSeed;
|
||||
_savedPresence = savedPresence;
|
||||
_options = options;
|
||||
_clients = clients;
|
||||
@@ -94,7 +97,7 @@ internal sealed class SchoolWorker
|
||||
_mods = mods;
|
||||
_onFailed = onFailed;
|
||||
_logger = logger;
|
||||
_snapshot = new SchoolState(id, name, time, running, (byte)speedIndex);
|
||||
_snapshot = new SchoolState(id, name, time, running, (byte)speedIndex, createSeed ?? 0);
|
||||
}
|
||||
|
||||
public int Id => _id;
|
||||
@@ -577,7 +580,8 @@ internal sealed class SchoolWorker
|
||||
school.Name,
|
||||
school.Clock.Time,
|
||||
school.Clock.IsRunning,
|
||||
(byte)school.Clock.SpeedIndex));
|
||||
(byte)school.Clock.SpeedIndex,
|
||||
school.PeopleSeed));
|
||||
Volatile.Write(ref _rosterSnapshot, school.Roster);
|
||||
Volatile.Write(ref _applicantSnapshot, school.Applicants);
|
||||
Volatile.Write(ref _timetableSnapshot, school.Timetable);
|
||||
@@ -856,7 +860,12 @@ internal sealed class SchoolWorker
|
||||
|
||||
if (_isNew)
|
||||
{
|
||||
seed = school.Id;
|
||||
if (_createSeed is not int createSeed)
|
||||
{
|
||||
throw new InvalidOperationException($"School {_id} was created without a people seed.");
|
||||
}
|
||||
|
||||
seed = createSeed;
|
||||
native = ResolveNative(catalog, nameSetId, seed, _nativeLanguage, generating: true);
|
||||
_nativeLanguage = native;
|
||||
roster = RosterGenerator.Generate(catalog, map, seed, nameSetId, school.Clock.Time, native);
|
||||
|
||||
Reference in New Issue
Block a user