WIP phase 39: school owners, my/others menu, guest restrictions.
This commit is contained in:
@@ -84,6 +84,38 @@ internal sealed class GameLoopService(
|
||||
return null;
|
||||
}
|
||||
|
||||
public SchoolState? FindSchool(int schoolId)
|
||||
{
|
||||
foreach (var worker in Volatile.Read(ref _publishedWorkers))
|
||||
{
|
||||
if (worker.Id == schoolId)
|
||||
{
|
||||
return worker.Snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
if (_incompatible.TryGetValue(schoolId, out var broken))
|
||||
{
|
||||
return broken;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int CountOwnedBy(string normalizedUserName)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var school in SchoolsState.Schools)
|
||||
{
|
||||
if (SchoolOwnership.IsOwner(school, normalizedUserName))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public Task ReloadFromDiskAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
@@ -94,8 +126,9 @@ internal sealed class GameLoopService(
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
logger.LogInformation(
|
||||
"School supervisor starting at {TickRate} Hz, up to {MaxSchools} schools, saves in {Directory}.",
|
||||
"School supervisor starting at {TickRate} Hz, up to {MaxSchoolsTotal} schools ({MaxSchools} per player), saves in {Directory}.",
|
||||
_options.TickRate,
|
||||
_options.MaxSchoolsTotal,
|
||||
_options.MaxSchools,
|
||||
store.DirectoryPath);
|
||||
|
||||
@@ -166,15 +199,15 @@ internal sealed class GameLoopService(
|
||||
break;
|
||||
|
||||
case GameCommand.SetRunning setRunning:
|
||||
RouteOpenSchool(setRunning.PlayerId, new WorkerCommand.SetRunning(setRunning.Running));
|
||||
HandleClockCommand(setRunning.PlayerId, setRunning.NormalizedUserName, new WorkerCommand.SetRunning(setRunning.Running));
|
||||
break;
|
||||
|
||||
case GameCommand.SetSpeed setSpeed:
|
||||
RouteOpenSchool(setSpeed.PlayerId, new WorkerCommand.SetSpeed(setSpeed.SpeedIndex));
|
||||
HandleClockCommand(setSpeed.PlayerId, setSpeed.NormalizedUserName, new WorkerCommand.SetSpeed(setSpeed.SpeedIndex));
|
||||
break;
|
||||
|
||||
case GameCommand.SkipEmpty skipEmpty:
|
||||
RouteOpenSchool(skipEmpty.PlayerId, new WorkerCommand.SkipEmpty());
|
||||
HandleClockCommand(skipEmpty.PlayerId, skipEmpty.NormalizedUserName, new WorkerCommand.SkipEmpty());
|
||||
break;
|
||||
|
||||
case GameCommand.ReloadSaves reload:
|
||||
@@ -326,7 +359,13 @@ internal sealed class GameLoopService(
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_workers.Count >= _options.MaxSchools)
|
||||
if (_workers.Count >= _options.MaxSchoolsTotal)
|
||||
{
|
||||
command.Result.TrySetResult(new SchoolCreationOutcome(null, SchoolCreationError.ServerFull));
|
||||
return;
|
||||
}
|
||||
|
||||
if (CountOwnedBy(command.Owner) >= _options.MaxSchools)
|
||||
{
|
||||
command.Result.TrySetResult(new SchoolCreationOutcome(null, SchoolCreationError.LimitReached));
|
||||
return;
|
||||
@@ -414,7 +453,7 @@ internal sealed class GameLoopService(
|
||||
var nativeLanguage = NativeLanguages.Pick(country.Names, seed, command.NativeLanguage, rollIfOmitted: true);
|
||||
var climatePresetId = CountryClimate.Pick(country, seed, rollIfOmitted: true);
|
||||
|
||||
var worker = SpawnWorker(id, normalized, command.StartDate, running: true, ClockSpeed.DefaultIndex, isNew: true, packIds, command.Map, countryId, climatePresetId, nativeLanguage, seed);
|
||||
var worker = SpawnWorker(id, normalized, command.StartDate, running: true, ClockSpeed.DefaultIndex, isNew: true, packIds, command.Map, countryId, climatePresetId, nativeLanguage, seed, owner: command.Owner);
|
||||
Track(worker);
|
||||
worker.Start();
|
||||
|
||||
@@ -558,6 +597,23 @@ internal sealed class GameLoopService(
|
||||
Route(schoolId, command);
|
||||
}
|
||||
|
||||
private void HandleClockCommand(uint playerId, string normalizedUserName, WorkerCommand command)
|
||||
{
|
||||
var client = clients.Find(playerId);
|
||||
if (client?.OpenSchoolId is not { } schoolId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_workers.TryGetValue(schoolId, out var worker)
|
||||
|| !SchoolOwnership.CanManage(worker.Snapshot, normalizedUserName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Route(schoolId, command);
|
||||
}
|
||||
|
||||
private async Task StartWorkersFromDiskAsync()
|
||||
{
|
||||
var saves = store.LoadAll();
|
||||
@@ -582,19 +638,19 @@ internal sealed class GameLoopService(
|
||||
}
|
||||
}
|
||||
|
||||
if (startable.Count > _options.MaxSchools)
|
||||
if (startable.Count > _options.MaxSchoolsTotal)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Found {Count} runnable school saves but the limit is {Max}; starting the first {Max}.",
|
||||
startable.Count,
|
||||
_options.MaxSchools,
|
||||
_options.MaxSchools);
|
||||
foreach (var extra in startable.Skip(_options.MaxSchools))
|
||||
_options.MaxSchoolsTotal,
|
||||
_options.MaxSchoolsTotal);
|
||||
foreach (var extra in startable.Skip(_options.MaxSchoolsTotal))
|
||||
{
|
||||
RememberIncompatible(FromSave(extra));
|
||||
}
|
||||
|
||||
startable = [.. startable.Take(_options.MaxSchools)];
|
||||
startable = [.. startable.Take(_options.MaxSchoolsTotal)];
|
||||
}
|
||||
|
||||
foreach (var save in startable)
|
||||
@@ -613,7 +669,8 @@ internal sealed class GameLoopService(
|
||||
save.NativeLanguage,
|
||||
createSeed: null,
|
||||
save.Presence,
|
||||
save.DressRules);
|
||||
save.DressRules,
|
||||
save.Owner);
|
||||
worker.Start();
|
||||
|
||||
try
|
||||
@@ -669,7 +726,8 @@ internal sealed class GameLoopService(
|
||||
string? nativeLanguage,
|
||||
int? createSeed = null,
|
||||
IReadOnlyList<PresenceSnapshot>? presence = null,
|
||||
SchoolDressRules? dressRules = null) =>
|
||||
SchoolDressRules? dressRules = null,
|
||||
string? owner = null) =>
|
||||
new(
|
||||
id,
|
||||
name,
|
||||
@@ -685,6 +743,7 @@ internal sealed class GameLoopService(
|
||||
createSeed,
|
||||
presence,
|
||||
dressRules,
|
||||
owner,
|
||||
_options,
|
||||
clients,
|
||||
metrics,
|
||||
@@ -760,6 +819,7 @@ internal sealed class GameLoopService(
|
||||
(byte)Math.Clamp(save.SpeedIndex, 0, 255),
|
||||
save.ModIds ?? [],
|
||||
SeedOf(save.Id),
|
||||
Owner: string.IsNullOrWhiteSpace(save.Owner) ? null : save.Owner,
|
||||
Incompatible: true);
|
||||
|
||||
private int SeedOf(int schoolId)
|
||||
|
||||
Reference in New Issue
Block a user