License grace period: keep running when the master is down or rejects the key (LicenseGraceHours, default 72 h), start with the last confirmed license, auto-retry; panel restart button (port/panel settings without program restart, login kept); tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+63
-9
@@ -19,6 +19,7 @@ public sealed class ServerHost
|
||||
static readonly TimeSpan CrashWindow = TimeSpan.FromMinutes(10);
|
||||
static readonly TimeSpan CrashRestartDelay = TimeSpan.FromSeconds(10);
|
||||
static readonly TimeSpan MasterRetryDelay = TimeSpan.FromSeconds(30);
|
||||
static readonly TimeSpan LicenseRetryDelay = TimeSpan.FromMinutes(5);
|
||||
|
||||
readonly string dataDir, configPath;
|
||||
readonly bool offline;
|
||||
@@ -87,6 +88,30 @@ public sealed class ServerHost
|
||||
return "Stopping the server - the panel keeps running";
|
||||
}
|
||||
|
||||
/// <summary>server.json für einen Panel-Neustart lesen (Umgebungsvariablen gelten weiter).</summary>
|
||||
public ServerConfig LoadConfigForRestart()
|
||||
{
|
||||
if (offline) return Config;
|
||||
try
|
||||
{
|
||||
var fresh = ServerConfig.LoadOrCreate(configPath, out _);
|
||||
fresh.ApplyEnvironment();
|
||||
return fresh;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
/// <summary>Panel mit neuen Einstellungen neu starten; geänderter Spiel-Port: Gameserver neu starten bzw. Port neu binden.</summary>
|
||||
public void RestartPanel(ServerConfig fresh, string by)
|
||||
{
|
||||
bool portChanged = fresh.Port != listenerPort;
|
||||
Config = fresh;
|
||||
Panel?.RestartWeb(fresh);
|
||||
if (!portChanged) return;
|
||||
if (Server != null && State == HostState.Running) RequestRestart(by); // bindet beim Start den neuen Port
|
||||
else Listen(fresh.Port);
|
||||
}
|
||||
|
||||
public string RequestRestart(string by)
|
||||
{
|
||||
var server = Server;
|
||||
@@ -146,6 +171,14 @@ public sealed class ServerHost
|
||||
Server = null;
|
||||
|
||||
if (server.ExitRequested) break;
|
||||
if (server.LicenseStopReason != null)
|
||||
{
|
||||
// Lizenz zu lange nicht bestätigt: regelmäßig neu versuchen - startet von selbst, sobald der Master wieder bestätigt
|
||||
SetState(HostState.Error, server.LicenseStopReason);
|
||||
startRequested = true;
|
||||
startAt = DateTime.UtcNow + LicenseRetryDelay;
|
||||
continue;
|
||||
}
|
||||
if (server.RestartRequested)
|
||||
{
|
||||
GameServer.Log("Restarting the server ...");
|
||||
@@ -220,23 +253,43 @@ public sealed class ServerHost
|
||||
}
|
||||
|
||||
string owner = "Test";
|
||||
DateTime confirmed = DateTime.UtcNow;
|
||||
if (!offline)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Config.LicenseKey))
|
||||
return Fail("No license key set - get one at " + MasterServer.Url + "/keys and enter it in the panel (Settings) or server.json");
|
||||
GameServer.Log($"Checking license key at {MasterServer.Url} ...");
|
||||
var key = new MasterClient(MasterServer.Url).ValidateKeyAsync(Config.LicenseKey).GetAwaiter().GetResult();
|
||||
if (key.Unreachable)
|
||||
if (key.Valid)
|
||||
{
|
||||
// Master gerade nicht erreichbar: gleich nochmal versuchen, bis der Server gestoppt wird
|
||||
startRequested = true;
|
||||
startAt = DateTime.UtcNow + MasterRetryDelay;
|
||||
return Fail($"Master server not reachable ({key.Error}) - retrying in {MasterRetryDelay.TotalSeconds:0} s");
|
||||
GameServer.Log($"License key OK ({key.Label}, owner {key.Owner})");
|
||||
owner = key.Owner;
|
||||
LicenseCache.Save(dataDir, Config.LicenseKey, key.Owner, key.Label, confirmed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Master nicht erreichbar oder lehnt ab: mit der letzten Bestätigung starten, solange sie frisch genug ist
|
||||
var grace = TimeSpan.FromHours(Math.Clamp(Config.LicenseGraceHours, 1, 24 * 30));
|
||||
var cached = LicenseCache.Load(dataDir, Config.LicenseKey);
|
||||
string problem = key.Unreachable ? $"Master server not reachable ({key.Error})" : "License key rejected: " + (key.Error ?? "unknown key");
|
||||
if (cached != null && DateTime.UtcNow - cached.LastConfirmedUtc < grace)
|
||||
{
|
||||
var left = grace - (DateTime.UtcNow - cached.LastConfirmedUtc);
|
||||
GameServer.Log($"WARNING: {problem} - starting with the license confirmed {LicenseCache.Span(DateTime.UtcNow - cached.LastConfirmedUtc)} ago " +
|
||||
$"(owner {cached.Owner}). The server keeps running for {LicenseCache.Span(left)} unless the master confirms it again.");
|
||||
owner = cached.Owner ?? "?";
|
||||
confirmed = cached.LastConfirmedUtc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nicht dauerhaft aufgeben: der Master kann gleich wieder da sein bzw. der Schlüssel wieder gelten
|
||||
var retry = key.Unreachable ? MasterRetryDelay : LicenseRetryDelay;
|
||||
startRequested = true;
|
||||
startAt = DateTime.UtcNow + retry;
|
||||
return Fail(problem + (key.Unreachable ? "" : " - manage your keys at " + MasterServer.Url + "/keys") +
|
||||
$" - retrying in {LicenseCache.Span(retry)}");
|
||||
}
|
||||
}
|
||||
if (!key.Valid)
|
||||
return Fail("License key invalid: " + (key.Error ?? "unknown key") + " - manage your keys at " + MasterServer.Url + "/keys");
|
||||
GameServer.Log($"License key OK ({key.Label}, owner {key.Owner})");
|
||||
owner = key.Owner;
|
||||
}
|
||||
|
||||
GameServer server;
|
||||
@@ -251,6 +304,7 @@ public sealed class ServerHost
|
||||
: "No save game yet - upload one as ZIP in the panel (World & Backups) or copy it to data/save");
|
||||
server.Panel = Panel;
|
||||
server.Host = this;
|
||||
server.LicenseConfirmedUtc = confirmed;
|
||||
ConfigureServer?.Invoke(server);
|
||||
if (Config.Port != listenerPort) Listen(Config.Port); // Port in den Einstellungen geändert
|
||||
return server;
|
||||
|
||||
Reference in New Issue
Block a user