Relay vehicle controls; on shutdown tell players to reconnect (restart) instead of a final disconnect

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 16:51:48 +02:00
co-authored by Claude Opus 5.5
parent 0f62c17c90
commit 53cda0cbc2
3 changed files with 8 additions and 4 deletions
+4 -3
View File
@@ -162,8 +162,9 @@ public sealed class GameServer
foreach (var c in clients.Values) foreach (var c in clients.Values)
{ {
// Grund mitschicken: die Spieler sehen ihn im Hauptmenü // Meist ein Neustart (Update, docker compose up): die Spieler verbinden sich automatisch neu, sobald der
if (c.State == ClientState.Joined) c.Conn.Send(MsgType.Reject, Wire.Build(w => w.Write("The server was shut down."))); // Server wieder da ist. Bleibt er aus, bekommen sie nach ein paar Versuchen einen Hinweis im Hauptmenü.
if (c.State == ClientState.Joined) c.Conn.Send(MsgType.Reconnect, Wire.Build(w => w.Write("The server is restarting - reconnecting...")));
c.Conn.CloseAfterSend("Server shutting down"); c.Conn.CloseAfterSend("Server shutting down");
} }
// Kurz warten, bis die Spieler die Meldung gelesen haben (sauber trennen statt hart abbrechen) // Kurz warten, bis die Spieler die Meldung gelesen haben (sauber trennen statt hart abbrechen)
@@ -291,7 +292,7 @@ public sealed class GameServer
t is MsgType.Player or MsgType.ObjStates or MsgType.ObjOwn or MsgType.ObjRelease or MsgType.FsmEvent t is MsgType.Player or MsgType.ObjStates or MsgType.ObjOwn or MsgType.ObjRelease or MsgType.FsmEvent
or MsgType.VehicleFx or MsgType.WorldTime or MsgType.MoneyDelta or MsgType.DoorState or MsgType.Chat or MsgType.VarSync or MsgType.VehicleFx or MsgType.WorldTime or MsgType.MoneyDelta or MsgType.DoorState or MsgType.Chat or MsgType.VarSync
or MsgType.SpawnItem or MsgType.ItemAppeared or MsgType.ObjDestroy or MsgType.VehicleDamage or MsgType.SpawnItem or MsgType.ItemAppeared or MsgType.ObjDestroy or MsgType.VehicleDamage
or MsgType.TrafficStates or MsgType.WorldVars or MsgType.HingeStates; or MsgType.TrafficStates or MsgType.WorldVars or MsgType.HingeStates or MsgType.VehicleControls;
/// <summary>Uhrzeit/Wetter gibt der Spieler mit der kleinsten ID vor (so rechnen es auch die Clients).</summary> /// <summary>Uhrzeit/Wetter gibt der Spieler mit der kleinsten ID vor (so rechnen es auch die Clients).</summary>
int WorldAuthorityId => clients.Values.Where(c => c.State == ClientState.Joined).Select(c => c.Id).DefaultIfEmpty(-1).Min(); int WorldAuthorityId => clients.Values.Where(c => c.State == ClientState.Joined).Select(c => c.Id).DefaultIfEmpty(-1).Min();
+2 -1
View File
@@ -46,7 +46,8 @@ public enum MsgType : byte
VehicleDamage = 39, // Unfallschaden (verformte Karosserie): int Mesh-Schlüssel, int Eckpunkte, int n, je [int Index, 3 float] VehicleDamage = 39, // Unfallschaden (verformte Karosserie): int Mesh-Schlüssel, int Eckpunkte, int n, je [int Index, 3 float]
TrafficStates = 40, // Verkehr, nur von der Welt-Autorität: int n, je [int Schlüssel, 3 float Pos, 4 float Rot, 3 float Geschw.] TrafficStates = 40, // Verkehr, nur von der Welt-Autorität: int n, je [int Schlüssel, 3 float Pos, 4 float Rot, 3 float Geschw.]
WorldVars = 41, // Jobs/Laden: int n, je [int Schlüssel, byte Typ, Wert] WorldVars = 41, // Jobs/Laden: int n, je [int Schlüssel, byte Typ, Wert]
HingeStates = 42 // Autotüren/Haube/Kofferraum: int n, je [int Schlüssel, 3 float Pos, 4 float Rot relativ zum Auto] HingeStates = 42, // Autotüren/Haube/Kofferraum: int n, je [int Schlüssel, 3 float Pos, 4 float Rot relativ zum Auto]
VehicleControls = 43 // Zündung/Motor/Licht ...: int n, je [int FSM-Schlüssel, string Zustand]
} }
public readonly record struct Message(MsgType Type, byte[] Payload) public readonly record struct Message(MsgType Type, byte[] Payload)
+2
View File
@@ -128,6 +128,8 @@ try
Check(await bob.WaitFor(MsgType.WorldVars) != null, "Job/shop values are relayed"); Check(await bob.WaitFor(MsgType.WorldVars) != null, "Job/shop values are relayed");
alice.Send(MsgType.HingeStates, Wire.Build(w => { w.Write(0); })); alice.Send(MsgType.HingeStates, Wire.Build(w => { w.Write(0); }));
Check(await bob.WaitFor(MsgType.HingeStates) != null, "Car door angles are relayed"); Check(await bob.WaitFor(MsgType.HingeStates) != null, "Car door angles are relayed");
alice.Send(MsgType.VehicleControls, Wire.Build(w => { w.Write(1); w.Write(123); w.Write("Lights"); }));
Check(await bob.WaitFor(MsgType.VehicleControls) != null, "Vehicle controls (ignition, lights ...) are relayed");
byte[] trafficPayload = Wire.Build(w => { w.Write(0); }); byte[] trafficPayload = Wire.Build(w => { w.Write(0); });
bob.Send(MsgType.TrafficStates, trafficPayload); bob.Send(MsgType.TrafficStates, trafficPayload);
Check(await alice.WaitFor(MsgType.TrafficStates, 800) == null, "Traffic from a player who is not the world authority is ignored"); Check(await alice.WaitFor(MsgType.TrafficStates, 800) == null, "Traffic from a player who is not the world authority is ignored");