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:
+55
-7
@@ -282,14 +282,11 @@ public sealed class GameServer
|
||||
{
|
||||
var result = heartbeat.Result;
|
||||
heartbeat = null;
|
||||
if (result.Status == 401 || result.Status == 403)
|
||||
{
|
||||
Log("License key was rejected by the master server - shutting down.");
|
||||
running = false;
|
||||
}
|
||||
else if (result.Status == 0) Log("Master server not reachable - server is not listed right now.");
|
||||
else ReportReachability(result);
|
||||
OnLicenseResult(result.Status);
|
||||
if (result.Status == 0 || result.Status >= 500) Log("Master server not reachable - server is not listed right now.");
|
||||
else if (result.Status >= 200 && result.Status < 300) ReportReachability(result);
|
||||
}
|
||||
CheckLicenseGrace();
|
||||
if (!offline && now >= nextHeartbeat && heartbeat == null)
|
||||
{
|
||||
nextHeartbeat = now + HeartbeatSeconds;
|
||||
@@ -1067,6 +1064,7 @@ public sealed class GameServer
|
||||
players = JoinedCount,
|
||||
maxPlayers = config.MaxPlayers,
|
||||
state = "running",
|
||||
licenseWarning = LicenseWarning,
|
||||
uptimeSeconds = (long)clock.Elapsed.TotalSeconds,
|
||||
port = config.Port,
|
||||
isPublic = config.Public,
|
||||
@@ -1116,6 +1114,56 @@ public sealed class GameServer
|
||||
public bool ExitRequested { get; private set; }
|
||||
string stoppedBy; // gesetzt = gezielt gestoppt (Spieler bekommen den Grund statt "verbinde neu")
|
||||
|
||||
// ---------------------------------------------------------------- Lizenz: Kulanzzeit, wenn der Master ausfällt
|
||||
|
||||
/// <summary>Wann der Master den Schlüssel zuletzt bestätigt hat (setzt der Host beim Start).</summary>
|
||||
public DateTime LicenseConfirmedUtc { get; set; } = DateTime.UtcNow;
|
||||
/// <summary>Gesetzt, wenn der Server wegen fehlender Lizenzbestätigung gestoppt wurde.</summary>
|
||||
public string LicenseStopReason { get; private set; }
|
||||
/// <summary>Hinweis fürs Panel, solange der Master den Schlüssel nicht bestätigt (null = alles gut).</summary>
|
||||
public string LicenseWarning { get; private set; }
|
||||
DateTime nextLicenseWarning = DateTime.MinValue, lastLicenseCacheWrite = DateTime.MinValue;
|
||||
|
||||
TimeSpan LicenseGrace => TimeSpan.FromHours(Math.Clamp(config.LicenseGraceHours, 1, 24 * 30));
|
||||
|
||||
/// <summary>Antwort des Masters auf den Heartbeat auswerten - nie sofort stoppen.</summary>
|
||||
void OnLicenseResult(int status)
|
||||
{
|
||||
if (status >= 200 && status < 300)
|
||||
{
|
||||
if (LicenseWarning != null) Log("The master server confirms the license key again - all good.");
|
||||
LicenseWarning = null;
|
||||
LicenseConfirmedUtc = DateTime.UtcNow;
|
||||
if (DateTime.UtcNow - lastLicenseCacheWrite > TimeSpan.FromMinutes(10))
|
||||
{
|
||||
lastLicenseCacheWrite = DateTime.UtcNow;
|
||||
var cached = LicenseCache.Load(DataDir, config.LicenseKey);
|
||||
LicenseCache.Save(DataDir, config.LicenseKey, cached?.Owner ?? ownerName, cached?.Label, LicenseConfirmedUtc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool rejected = status == 401 || status == 403;
|
||||
var since = DateTime.UtcNow - LicenseConfirmedUtc;
|
||||
var left = LicenseGrace - since;
|
||||
LicenseWarning = (rejected ? "The master server rejects the license key" : "The master server is not reachable") +
|
||||
$" (last confirmed {LicenseCache.Span(since)} ago) - the server keeps running for {LicenseCache.Span(left)}";
|
||||
if (DateTime.UtcNow >= nextLicenseWarning)
|
||||
{
|
||||
nextLicenseWarning = DateTime.UtcNow.AddMinutes(15);
|
||||
Log("WARNING: " + LicenseWarning + (rejected ? ". Check your key at " + MasterServer.Url + "/keys" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Kulanzzeit abgelaufen? Dann den Gameserver stoppen (das Panel bleibt, der Host versucht es weiter).</summary>
|
||||
void CheckLicenseGrace()
|
||||
{
|
||||
if (LicenseStopReason != null || DateTime.UtcNow - LicenseConfirmedUtc <= LicenseGrace) return;
|
||||
LicenseStopReason = $"The license key could not be confirmed by the master server for {LicenseCache.Span(LicenseGrace)}";
|
||||
Log(LicenseStopReason + " - stopping the game server. It starts again automatically as soon as the master confirms the key.");
|
||||
stoppedBy = "the license check";
|
||||
running = false;
|
||||
}
|
||||
|
||||
public void RequestExit()
|
||||
{
|
||||
ExitRequested = true;
|
||||
|
||||
Reference in New Issue
Block a user