From 08844b18ac6bc5dafdb93d58ba5e06699f32ffde Mon Sep 17 00:00:00 2001 From: MrMaho Date: Sun, 27 Sep 2026 10:46:45 +0200 Subject: [PATCH] Show why you were disconnected in the main menu; reset-world / reload-save make all players reconnect automatically Co-Authored-By: Claude Opus 5.5 --- GameServer.cs | 26 ++++++++++++++++++++++++-- Protocol.cs | 3 ++- README.md | 4 ++-- Tests/Program.cs | 9 +++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/GameServer.cs b/GameServer.cs index f40c65b..f9f1ce7 100644 --- a/GameServer.cs +++ b/GameServer.cs @@ -138,7 +138,12 @@ public sealed class GameServer Thread.Sleep(2); } - foreach (var c in clients.Values) c.Conn.Close("Server shutting down"); + foreach (var c in clients.Values) + { + // Grund mitschicken: die Spieler sehen ihn im Hauptmenü + if (c.State == ClientState.Joined) c.Conn.Send(MsgType.Reject, Wire.Build(w => w.Write("The server was shut down."))); + c.Conn.Close("Server shutting down"); + } listener.Stop(); SaveWorld(); if (!offline) @@ -457,6 +462,22 @@ public sealed class GameServer } /// Chatnachricht vom Server an alle (Absender-ID 0 = "Server"). + /// + /// Alle Spieler neu verbinden lassen (nach reset-world / reload-save): sie sehen die Meldung, gehen kurz ins Menü + /// und laden dann den neuen Stand. Sonst liefen sie mit der alten Welt weiter. + /// + void ReconnectAll(string message) + { + int count = 0; + foreach (var c in clients.Values.Where(x => x.State == ClientState.Joined).ToList()) + { + c.Conn.Send(MsgType.Reconnect, Wire.Build(w => w.Write(message))); + c.Conn.Close("Reconnect after world change"); + count++; + } + if (count > 0) Log($"{count} player(s) will reconnect"); + } + string Unban(string id, string by) { id = (id ?? "").Trim(); @@ -576,9 +597,10 @@ public sealed class GameServer world.Reset(); SaveWorld(); Log("World state reset - new players get the plain save game again."); + ReconnectAll("The world is being reset - reconnecting..."); break; case "reload-save": - LoadSave(); + if (LoadSave()) ReconnectAll("The server loaded a new save game - reconnecting..."); break; case "stop": case "quit": diff --git a/Protocol.cs b/Protocol.cs index 40fb757..4e6843b 100644 --- a/Protocol.cs +++ b/Protocol.cs @@ -36,7 +36,8 @@ public enum MsgType : byte VarSync = 29, // Fahrzeugzustand: int n, je [int key, byte typ, wert] Teleport = 30, // Admin holt einen Spieler zu sich: 3 float Position Pong = 31, // Antwort auf Ping mit demselben Zeitstempel (Laufzeit messen) - SpawnItem = 32 // Admin spawnt ein Item: string Item-Name, 3 float Position, 4 float Drehung + SpawnItem = 32, // Admin spawnt ein Item: string Item-Name, 3 float Position, 4 float Drehung + Reconnect = 33 // Server -> Client: string Meldung; Client geht ins Menü und verbindet sich neu (z.B. Welt zurückgesetzt) } public readonly record struct Message(MsgType Type, byte[] Payload) diff --git a/README.md b/README.md index d611d7c..2efd6d7 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,8 @@ Fehlende Einstellungen ergänzt der Server beim Start automatisch. Umgebungsvari | `say ` | Chatnachricht an alle | | `admins`, `op `, `deop <...>` | Admins anzeigen / geben / nehmen | | `save` | Weltzustand jetzt speichern | -| `reset-world` | alle Änderungen seit dem Spielstand verwerfen | -| `reload-save` | Spielstand-Dateien neu einlesen | +| `reset-world` | alle Änderungen seit dem Spielstand verwerfen – verbundene Spieler bekommen eine Meldung und verbinden sich automatisch neu | +| `reload-save` | Spielstand-Dateien neu einlesen – verbundene Spieler verbinden sich automatisch neu | | `stop` | speichern und beenden | Im Spiel öffnen Admins mit `/admin` (Chat, Taste T) oder `admin` (F7-Konsole) das Admin-Menü: Kick/Ban, diff --git a/Tests/Program.cs b/Tests/Program.cs index fdc6696..50be6a8 100644 --- a/Tests/Program.cs +++ b/Tests/Program.cs @@ -153,6 +153,15 @@ try Check(unbanned.Welcome != null, "After unban the player can join again"); unbanned.Close(); watcher.Close(); + await Task.Delay(300); + + Console.WriteLine("== reset-world makes everyone reconnect"); + var r1 = await TestClient.Join("Resetter", 0, null); + server.EnqueueCommand("reset-world"); + var rec = await r1.WaitFor(MsgType.Reconnect); + Check(rec != null && new BinaryReader(new MemoryStream(rec)).ReadString().Contains("reset"), "Players get the 'world is being reset' reconnect message"); + await Task.Delay(300); + Check(r1.IsClosedByServer(), "Their connection is closed so they load the fresh world"); } catch (Exception e) {