Web panel (like txAdmin) on the game port: login/roles/users, players, kick/ban/op, bans, live console, world reset/backups/restore, settings editor, save ZIP upload/download, start/stop/restart; in-process restart loop; tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+71
-41
@@ -94,41 +94,12 @@ if (Array.IndexOf(args, "--import-save") >= 0)
|
||||
if (Directory.Exists(original)) Directory.Delete(original, true);
|
||||
}
|
||||
|
||||
// Lizenzschlüssel beim Keymaster prüfen
|
||||
if (string.IsNullOrWhiteSpace(config.LicenseKey))
|
||||
{
|
||||
GameServer.Log("No license key set. Get one at " + MasterServer.Url + "/keys and put it into server.json.");
|
||||
Pause();
|
||||
return 1;
|
||||
}
|
||||
// Automatisch mit dem Master (msco.mbm.wtf) verbinden: ist er gerade nicht erreichbar,
|
||||
// wird es mit wachsendem Abstand (max. 60 s) weiter versucht, statt aufzugeben.
|
||||
var master = new MasterClient(MasterServer.Url);
|
||||
MasterClient.KeyInfo key;
|
||||
for (int attempt = 1; ; attempt++)
|
||||
{
|
||||
GameServer.Log($"Checking license key at {MasterServer.Url} ...");
|
||||
key = await master.ValidateKeyAsync(config.LicenseKey);
|
||||
if (!key.Unreachable) break;
|
||||
int wait = Math.Min(60, 5 * attempt);
|
||||
GameServer.Log($"Master server not reachable ({key.Error}) - retrying in {wait} s (Ctrl+C to cancel)");
|
||||
await Task.Delay(TimeSpan.FromSeconds(wait));
|
||||
}
|
||||
if (!key.Valid)
|
||||
{
|
||||
GameServer.Log("License key invalid: " + (key.Error ?? "unknown key"));
|
||||
GameServer.Log("Manage your keys at " + MasterServer.Url + "/keys");
|
||||
Pause();
|
||||
return 1;
|
||||
}
|
||||
GameServer.Log($"License key OK ({key.Label}, owner {key.Owner})");
|
||||
|
||||
var server = new GameServer(config, dataDir, configPath, key.Owner);
|
||||
if (!server.LoadSave())
|
||||
{
|
||||
Pause();
|
||||
return 1;
|
||||
}
|
||||
// Konsole, "msco <befehl>" (docker exec) und Signale gehen immer an den gerade laufenden Server -
|
||||
// nach einem Neustart aus dem Panel ist das ein neuer. Das Web-Panel bleibt dabei bestehen (man bleibt angemeldet).
|
||||
GameServer server = null;
|
||||
AdminPanel panel = null;
|
||||
Action<string> send = line => server?.EnqueueCommand(line);
|
||||
Action stopRequested = () => { if (server == null) Environment.Exit(0); else server.EnqueueCommand("stop"); };
|
||||
|
||||
// Konsolenbefehle in eigenem Thread lesen, ausgeführt werden sie in der Server-Schleife.
|
||||
// Als Dienst (systemd/Docker ohne Terminal) gibt es keine Eingabe - dann einfach ohne Konsole laufen.
|
||||
@@ -137,7 +108,7 @@ var input = new Thread(() =>
|
||||
try
|
||||
{
|
||||
string line;
|
||||
while ((line = Console.ReadLine()) != null) server.EnqueueCommand(line);
|
||||
while ((line = Console.ReadLine()) != null) send(line);
|
||||
}
|
||||
catch { }
|
||||
}) { IsBackground = true };
|
||||
@@ -155,7 +126,7 @@ var commandWatcher = new Thread(() =>
|
||||
if (!File.Exists(commandFile)) continue;
|
||||
File.Move(commandFile, processing, true);
|
||||
foreach (string line in File.ReadAllLines(processing))
|
||||
if (line.Trim().Length > 0) { GameServer.Log("> " + line.Trim()); server.EnqueueCommand(line.Trim()); }
|
||||
if (line.Trim().Length > 0) { GameServer.Log("> " + line.Trim()); send(line.Trim()); }
|
||||
File.Delete(processing);
|
||||
}
|
||||
catch { }
|
||||
@@ -164,11 +135,70 @@ var commandWatcher = new Thread(() =>
|
||||
commandWatcher.Start();
|
||||
|
||||
// Sauber beenden (Weltzustand speichern) bei Strg+C, systemctl stop / docker stop (SIGTERM) und SIGQUIT
|
||||
Console.CancelKeyPress += (_, e) => { e.Cancel = true; server.EnqueueCommand("stop"); };
|
||||
using var sigterm = PosixSignalRegistration.Create(PosixSignal.SIGTERM, ctx => { ctx.Cancel = true; server.EnqueueCommand("stop"); });
|
||||
using var sigquit = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, ctx => { ctx.Cancel = true; server.EnqueueCommand("stop"); });
|
||||
Console.CancelKeyPress += (_, e) => { e.Cancel = true; stopRequested(); };
|
||||
using var sigterm = PosixSignalRegistration.Create(PosixSignal.SIGTERM, ctx => { ctx.Cancel = true; stopRequested(); });
|
||||
using var sigquit = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, ctx => { ctx.Cancel = true; stopRequested(); });
|
||||
|
||||
server.Run();
|
||||
for (bool restarting = false; ; restarting = true)
|
||||
{
|
||||
if (restarting)
|
||||
{
|
||||
// Neustart: server.json neu lesen (im Panel geänderte Einstellungen), Lizenz und Spielstand neu prüfen
|
||||
try { config = ServerConfig.LoadOrCreate(configPath, out _); }
|
||||
catch (Exception e) { GameServer.Log("Cannot read " + configPath + " (" + e.Message + ") - keeping the previous settings"); }
|
||||
config.ApplyEnvironment();
|
||||
}
|
||||
|
||||
// Lizenzschlüssel beim Keymaster prüfen
|
||||
if (string.IsNullOrWhiteSpace(config.LicenseKey))
|
||||
{
|
||||
GameServer.Log("No license key set. Get one at " + MasterServer.Url + "/keys and put it into server.json.");
|
||||
Pause();
|
||||
return 1;
|
||||
}
|
||||
// Automatisch mit dem Master (msco.mbm.wtf) verbinden: ist er gerade nicht erreichbar,
|
||||
// wird es mit wachsendem Abstand (max. 60 s) weiter versucht, statt aufzugeben.
|
||||
var master = new MasterClient(MasterServer.Url);
|
||||
MasterClient.KeyInfo key;
|
||||
for (int attempt = 1; ; attempt++)
|
||||
{
|
||||
GameServer.Log($"Checking license key at {MasterServer.Url} ...");
|
||||
key = await master.ValidateKeyAsync(config.LicenseKey);
|
||||
if (!key.Unreachable) break;
|
||||
int wait = Math.Min(60, 5 * attempt);
|
||||
GameServer.Log($"Master server not reachable ({key.Error}) - retrying in {wait} s (Ctrl+C to cancel)");
|
||||
await Task.Delay(TimeSpan.FromSeconds(wait));
|
||||
}
|
||||
if (!key.Valid)
|
||||
{
|
||||
GameServer.Log("License key invalid: " + (key.Error ?? "unknown key"));
|
||||
GameServer.Log("Manage your keys at " + MasterServer.Url + "/keys");
|
||||
Pause();
|
||||
return 1;
|
||||
}
|
||||
GameServer.Log($"License key OK ({key.Label}, owner {key.Owner})");
|
||||
|
||||
var next = new GameServer(config, dataDir, configPath, key.Owner);
|
||||
if (!next.LoadSave())
|
||||
{
|
||||
Pause();
|
||||
return 1;
|
||||
}
|
||||
// Web-Panel (wie txAdmin) - standardmäßig auf dem Spiel-Port; bei einem Neustart dasselbe Panel weiterverwenden
|
||||
if (config.PanelEnabled || panel != null)
|
||||
{
|
||||
if (panel == null) panel = AdminPanel.Start(next, config, dataDir);
|
||||
else panel.Attach(next, config);
|
||||
next.Panel = panel;
|
||||
}
|
||||
server = next;
|
||||
|
||||
server.Run();
|
||||
if (!server.RestartRequested) break;
|
||||
server = null;
|
||||
GameServer.Log("Restarting the server ...");
|
||||
}
|
||||
panel?.Stop();
|
||||
return 0;
|
||||
|
||||
string ArgValue(string name)
|
||||
|
||||
Reference in New Issue
Block a user