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 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,8 @@ Server und Mod müssen beide 2.7 sein (Server: `git pull && docker compose up -d
|
|||||||
ist das Hauptmenü (mit eigenem QUIT)
|
ist das Hauptmenü (mit eigenem QUIT)
|
||||||
- Server-Profil neu: JOIN oben, Spielerliste rechts, Namensfeld ohne Steam in eigenem Kasten
|
- Server-Profil neu: JOIN oben, Spielerliste rechts, Namensfeld ohne Steam in eigenem Kasten
|
||||||
- Admin-ITEMS: Werkzeuge und Laden-Artikel (Ratschenkasten, Batterie, Motoröl, Kühlmittel, Bremsflüssigkeit ...)
|
- Admin-ITEMS: Werkzeuge und Laden-Artikel (Ratschenkasten, Batterie, Motoröl, Kühlmittel, Bremsflüssigkeit ...)
|
||||||
|
- Nach dem Trennen (Kick, Ban, Shutdown, Timeout ...) zeigt das Hauptmenü den Grund
|
||||||
|
- `reset-world` / `reload-save` auf dem Server: alle Spieler bekommen eine Meldung und verbinden sich automatisch neu
|
||||||
|
|
||||||
**Behoben**
|
**Behoben**
|
||||||
- Auto fiel beim Fahren eines anderen Spielers auseinander (angebaute Teile rissen an ihren Gelenken ab)
|
- Auto fiel beim Fahren eines anderen Spielers auseinander (angebaute Teile rissen an ihren Gelenken ab)
|
||||||
|
|||||||
+30
-2
@@ -72,7 +72,8 @@ namespace MSCO
|
|||||||
EnsureStyles();
|
EnsureStyles();
|
||||||
bool pendingJoin = mp.PendingJoinHost != null && !mp.IsInSession;
|
bool pendingJoin = mp.PendingJoinHost != null && !mp.IsInSession;
|
||||||
bool versionWarning = mp.PendingVersionWarning != null && !mp.IsInSession;
|
bool versionWarning = mp.PendingVersionWarning != null && !mp.IsInSession;
|
||||||
if (pendingJoin || versionWarning) minimized = false; // JOIN-Link / Warnung: Fenster zeigen
|
bool disconnected = mp.DisconnectNotice != null && !mp.IsInSession;
|
||||||
|
if (pendingJoin || versionWarning || disconnected) minimized = false; // JOIN-Link / Warnung / Trennung: Fenster zeigen
|
||||||
if (minimized) { DrawMinimized(mp); return; }
|
if (minimized) { DrawMinimized(mp); return; }
|
||||||
|
|
||||||
var window = new Rect((Screen.width - Width) / 2f, (Screen.height - Height) / 2f, Width, Height);
|
var window = new Rect((Screen.width - Width) / 2f, (Screen.height - Height) / 2f, Width, Height);
|
||||||
@@ -84,8 +85,10 @@ namespace MSCO
|
|||||||
var content = new Rect(window.x + SidebarWidth + 32, window.y + 28,
|
var content = new Rect(window.x + SidebarWidth + 32, window.y + 28,
|
||||||
Width - SidebarWidth - 64, Height - StatusHeight - 52);
|
Width - SidebarWidth - 64, Height - StatusHeight - 52);
|
||||||
GUILayout.BeginArea(content);
|
GUILayout.BeginArea(content);
|
||||||
if (versionWarning) DrawVersionWarning(mp);
|
if (disconnected) DrawDisconnectNotice(mp);
|
||||||
|
else if (versionWarning) DrawVersionWarning(mp);
|
||||||
else if (pendingJoin) DrawJoinConfirm(mp);
|
else if (pendingJoin) DrawJoinConfirm(mp);
|
||||||
|
else if (mp.IsReconnecting) DrawReconnecting(mp);
|
||||||
else if (mp.IsInSession) DrawSession(mp);
|
else if (mp.IsInSession) DrawSession(mp);
|
||||||
else if (page == Page.P2P) DrawP2P(mp);
|
else if (page == Page.P2P) DrawP2P(mp);
|
||||||
else DrawServerBrowser(mp);
|
else DrawServerBrowser(mp);
|
||||||
@@ -483,6 +486,31 @@ namespace MSCO
|
|||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Nach dem Trennen (Kick, Ban, Shutdown, Timeout ...): Grund anzeigen.</summary>
|
||||||
|
void DrawDisconnectNotice(MultiplayerManager mp)
|
||||||
|
{
|
||||||
|
GUILayout.Label("Disconnected", titleStyle);
|
||||||
|
GUILayout.Space(8);
|
||||||
|
GUILayout.BeginVertical(rowBox);
|
||||||
|
GUILayout.Space(6);
|
||||||
|
GUILayout.Label("REASON", captionStyle);
|
||||||
|
GUILayout.Label(mp.DisconnectNotice, rowNameStyle);
|
||||||
|
GUILayout.Space(6);
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
GUILayout.Space(8);
|
||||||
|
GUILayout.Label("Your own save game has been restored.", textStyle);
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
if (GUILayout.Button("OK", primaryStyle, GUILayout.Height(42), GUILayout.Width(170))) mp.ClearDisconnectNotice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawReconnecting(MultiplayerManager mp)
|
||||||
|
{
|
||||||
|
GUILayout.Label("Reconnecting", titleStyle);
|
||||||
|
GUILayout.Space(8);
|
||||||
|
GUILayout.Label(mp.ReconnectMessage, rowNameStyle);
|
||||||
|
GUILayout.Label("You will be connected to the server again in a moment.", textStyle);
|
||||||
|
}
|
||||||
|
|
||||||
void DrawSession(MultiplayerManager mp)
|
void DrawSession(MultiplayerManager mp)
|
||||||
{
|
{
|
||||||
GUILayout.Label(mp.IsHost ? "Hosting" : "Connected", titleStyle);
|
GUILayout.Label(mp.IsHost ? "Hosting" : "Connected", titleStyle);
|
||||||
|
|||||||
+68
-1
@@ -541,8 +541,18 @@ namespace MSCO
|
|||||||
SetStatus("");
|
SetStatus("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Letzter Server (für das automatische Neu-Verbinden nach reset-world)
|
||||||
|
string lastServerHost, lastServerName;
|
||||||
|
int lastServerPort, lastServerMax;
|
||||||
|
string reconnectMessage; // gesetzt, wenn der Server uns neu verbinden lässt
|
||||||
|
float reconnectAt;
|
||||||
|
|
||||||
void ConnectToServer(string host, int port, string displayName, int maxPlayers)
|
void ConnectToServer(string host, int port, string displayName, int maxPlayers)
|
||||||
{
|
{
|
||||||
|
lastServerHost = host;
|
||||||
|
lastServerPort = port;
|
||||||
|
lastServerName = displayName;
|
||||||
|
lastServerMax = maxPlayers;
|
||||||
Join(host, port);
|
Join(host, port);
|
||||||
if (session == null) return;
|
if (session == null) return;
|
||||||
connectingToServer = true;
|
connectingToServer = true;
|
||||||
@@ -758,6 +768,18 @@ namespace MSCO
|
|||||||
UpdatePresence();
|
UpdatePresence();
|
||||||
|
|
||||||
steam.Tick();
|
steam.Tick();
|
||||||
|
|
||||||
|
// Nach reset-world o.ä.: im Hauptmenü automatisch wieder mit demselben Server verbinden
|
||||||
|
if (reconnectMessage != null && session == null && MenuHelper.InMenu && Time.realtimeSinceStartup >= reconnectAt)
|
||||||
|
{
|
||||||
|
string message = reconnectMessage;
|
||||||
|
reconnectMessage = null;
|
||||||
|
if (lastServerHost != null)
|
||||||
|
{
|
||||||
|
SetStatus(message);
|
||||||
|
JoinServer(lastServerHost, lastServerPort, lastServerName, lastServerMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (pendingLobby.m_SteamID != 0 && MenuHelper.InMenu && session == null && steam.Init())
|
if (pendingLobby.m_SteamID != 0 && MenuHelper.InMenu && session == null && steam.Init())
|
||||||
{
|
{
|
||||||
Steamworks.CSteamID lobbyToJoin = pendingLobby;
|
Steamworks.CSteamID lobbyToJoin = pendingLobby;
|
||||||
@@ -832,8 +854,39 @@ namespace MSCO
|
|||||||
SetStatus(p.Name + " left (" + reason + ")");
|
SetStatus(p.Name + " left (" + reason + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string serverReason; // Grund, den der Server/Host beim Trennen mitgeschickt hat
|
||||||
|
|
||||||
|
/// <summary>Warum man zuletzt getrennt wurde - wird im Hauptmenü angezeigt, bis man OK drückt.</summary>
|
||||||
|
public string DisconnectNotice { get; private set; }
|
||||||
|
public void ClearDisconnectNotice() { DisconnectNotice = null; }
|
||||||
|
public bool IsReconnecting { get { return reconnectMessage != null && session == null; } }
|
||||||
|
public string ReconnectMessage { get { return reconnectMessage; } }
|
||||||
|
|
||||||
|
/// <summary>Technische Gründe in verständliche Sätze übersetzen.</summary>
|
||||||
|
static string FriendlyReason(string reason)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(reason)) return "The connection was closed.";
|
||||||
|
string r = reason.ToLower();
|
||||||
|
if (r.Contains("timed out") || r.Contains("timeout")) return "Connection lost - the server did not respond anymore (timeout).";
|
||||||
|
if (r.Contains("network error") || r.Contains("forcibly closed") || r.Contains("connection reset"))
|
||||||
|
return "Connection lost (" + reason + ").";
|
||||||
|
if (r == "connection closed" || r.Contains("closed")) return "The server closed the connection.";
|
||||||
|
if (r.Contains("host left")) return "The host left the game.";
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
void OnSessionEnded(string reason)
|
void OnSessionEnded(string reason)
|
||||||
{
|
{
|
||||||
|
// Hinweis fürs Hauptmenü - außer man hat selbst auf DISCONNECT gedrückt oder verbindet gleich neu
|
||||||
|
if (reconnectMessage != null)
|
||||||
|
{
|
||||||
|
DisconnectNotice = null;
|
||||||
|
reconnectAt = Time.realtimeSinceStartup + 3f; // erst ins Menü, dann neu verbinden
|
||||||
|
}
|
||||||
|
else if (reason != "Disconnected")
|
||||||
|
DisconnectNotice = serverReason ?? FriendlyReason(reason);
|
||||||
|
serverReason = null;
|
||||||
|
|
||||||
nextPresenceUpdate = 0f; // Discord sofort auf "In the Menus"/Singleplayer umstellen
|
nextPresenceUpdate = 0f; // Discord sofort auf "In the Menus"/Singleplayer umstellen
|
||||||
bool wasClientInGame = session != null && !session.IsHost && MenuHelper.InGame;
|
bool wasClientInGame = session != null && !session.IsHost && MenuHelper.InGame;
|
||||||
|
|
||||||
@@ -1003,9 +1056,13 @@ namespace MSCO
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MsgType.Reject:
|
case MsgType.Reject:
|
||||||
session.Close((dedicated || connectingToServer ? "Rejected by server: " : "Rejected by host: ") + r.ReadString());
|
{
|
||||||
|
string why = r.ReadString();
|
||||||
|
serverReason = why; // für den Hinweis im Hauptmenü (Kick, Ban, Server voll, Shutdown ...)
|
||||||
|
session.Close((dedicated || connectingToServer ? "Rejected by server: " : "Rejected by host: ") + why);
|
||||||
OnSessionEnded(session.CloseReason);
|
OnSessionEnded(session.CloseReason);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case MsgType.WorldSnapshot:
|
case MsgType.WorldSnapshot:
|
||||||
// Wird angewendet, sobald das Spiel geladen und alle Objekte erfasst sind
|
// Wird angewendet, sobald das Spiel geladen und alle Objekte erfasst sind
|
||||||
@@ -1059,6 +1116,16 @@ namespace MSCO
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MsgType.Reconnect:
|
||||||
|
{
|
||||||
|
// Server hat die Welt zurückgesetzt o.ä.: Meldung zeigen, ins Menü, dann automatisch neu verbinden
|
||||||
|
reconnectMessage = r.ReadString();
|
||||||
|
overlays.AddChat("", reconnectMessage, true);
|
||||||
|
session.Close("Reconnecting");
|
||||||
|
OnSessionEnded(session.CloseReason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case MsgType.AdminStatus:
|
case MsgType.AdminStatus:
|
||||||
isAdmin = r.ReadBoolean();
|
isAdmin = r.ReadBoolean();
|
||||||
if (isAdmin) overlays.Log("You are an admin on this server - type /admin in chat or 'admin' here.");
|
if (isAdmin) overlays.Log("You are an admin on this server - type /admin in chat or 'admin' here.");
|
||||||
|
|||||||
+2
-1
@@ -40,7 +40,8 @@ namespace MSCO
|
|||||||
VarSync = 29, // Fahrzeugzustand: int n, je [int key, byte typ, wert]
|
VarSync = 29, // Fahrzeugzustand: int n, je [int key, byte typ, wert]
|
||||||
Teleport = 30, // Admin holt einen Spieler zu sich: 3 float Position
|
Teleport = 30, // Admin holt einen Spieler zu sich: 3 float Position
|
||||||
Pong = 31, // Antwort auf Ping mit demselben Zeitstempel (Laufzeit messen)
|
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; ins Menü und neu verbinden (z.B. Welt zurückgesetzt)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Message
|
public struct Message
|
||||||
|
|||||||
Reference in New Issue
Block a user