diff --git a/KCClient.cs b/KCClient.cs index 75ba8ec..9e530c6 100644 --- a/KCClient.cs +++ b/KCClient.cs @@ -39,6 +39,8 @@ namespace KCM Main.helper.Log("Client disconnected event start"); try { + Main.ResetMultiplayerState("Client disconnected"); + if (e.Message != null) { Main.helper.Log(e.Message.ToString()); diff --git a/LoadSaveOverrides/MultiplayerSaveContainer.cs b/LoadSaveOverrides/MultiplayerSaveContainer.cs index 918f003..7697411 100644 --- a/LoadSaveOverrides/MultiplayerSaveContainer.cs +++ b/LoadSaveOverrides/MultiplayerSaveContainer.cs @@ -27,12 +27,36 @@ namespace KCM.LoadSaveOverrides //this.PlayerSaveData = new PlayerSaveDataOverride().Pack(Player.inst); foreach (var player in Main.kCPlayers.Values) { - Main.helper.Log($"Attempting to pack data for: " + player.name + $"({player.steamId})"); - Main.helper.Log($"{player.inst.ToString()} {player.inst?.gameObject.name}"); - this.players.Add(player.steamId, new Player.PlayerSaveData().Pack(player.inst)); - kingdomNames.Add(player.steamId, player.kingdomName); + try + { + if (player == null) + continue; - Main.helper.Log($"{players[player.steamId] == null}"); + if (string.IsNullOrWhiteSpace(player.steamId)) + { + Main.helper.Log($"Skipping save for player with missing steamId (name={player.name ?? \"\"})"); + continue; + } + + if (player.inst == null) + { + Main.helper.Log($"Skipping save for player {player.name ?? \"\"} ({player.steamId}) because Player.inst is null"); + continue; + } + + Main.helper.Log($"Attempting to pack data for: {player.name} ({player.steamId})"); + Main.helper.Log($"Player object: {player.inst} {player.inst.gameObject?.name}"); + + this.players[player.steamId] = new Player.PlayerSaveData().Pack(player.inst); + kingdomNames[player.steamId] = player.kingdomName ?? " "; + + Main.helper.Log($"{players[player.steamId] == null}"); + } + catch (Exception ex) + { + Main.helper.Log($"Error packing player data for save (steamId={player?.steamId ?? \"\"}, name={player?.name ?? \"\"})"); + Main.helper.Log(ex.ToString()); + } } this.WorldSaveData = new World.WorldSaveData().Pack(World.inst); diff --git a/Main.cs b/Main.cs index 34d0a9c..3652e09 100644 --- a/Main.cs +++ b/Main.cs @@ -56,6 +56,76 @@ namespace KCM public static Dictionary clientSteamIds = new Dictionary(); private static readonly Dictionary lastTeamIdLookupLogMs = new Dictionary(); + private static int resetInProgress = 0; + + public static void ResetMultiplayerState(string reason = null) + { + if (Interlocked.Exchange(ref resetInProgress, 1) == 1) + return; + + try + { + if (!string.IsNullOrEmpty(reason)) + helper?.Log($"ResetMultiplayerState: {reason}"); + + try { StateObserver.ClearAll(); } catch { } + try { SaveTransferPacket.ResetTransferState(); } catch { } + + try + { + LoadSaveLoadHook.memoryStreamHook = false; + LoadSaveLoadHook.saveBytes = new byte[0]; + LoadSaveLoadHook.saveContainer = null; + } + catch { } + + try { LoadSaveLoadAtPathHook.saveData = new byte[0]; } catch { } + + try { LobbyManager.loadingSave = false; } catch { } + + try + { + foreach (var player in kCPlayers.Values) + { + if (player?.gameObject != null && player.gameObject.name != null && player.gameObject.name.Contains("Client Player")) + UnityEngine.Object.Destroy(player.gameObject); + } + } + catch { } + + try { LobbyHandler.ClearChatEntries(); } catch { } + try { LobbyHandler.ClearPlayerList(); } catch { } + + try + { + kCPlayers.Clear(); + clientSteamIds.Clear(); + } + catch { } + + try { lastTeamIdLookupLogMs.Clear(); } catch { } + + try + { + if (KCClient.client != null && KCClient.client.IsConnected) + KCClient.client.Disconnect(); + } + catch { } + + try + { + if (KCServer.IsRunning) + KCServer.server.Stop(); + } + catch { } + + try { ServerBrowser.registerServer = false; } catch { } + } + finally + { + Interlocked.Exchange(ref resetInProgress, 0); + } + } public static KCPlayer GetPlayerByClientID(ushort clientId) { @@ -310,6 +380,9 @@ namespace KCM if (newState != MainMenuMode.State.Uninitialized) Main.menuState = (MenuState)newState; + + if ((MenuState)newState == MenuState.Menu && (KCClient.client.IsConnected || KCServer.IsRunning)) + ResetMultiplayerState("Returned to main menu"); } } @@ -1221,11 +1294,18 @@ namespace KCM { BinaryFormatter bf = new BinaryFormatter(); bf.Binder = new MultiplayerSaveDeserializationBinder(); - saveData = File.ReadAllBytes(path); Stream file = new FileStream(path, FileMode.Open); try { - MultiplayerSaveContainer loadData = (MultiplayerSaveContainer)bf.Deserialize(file); + object deserialized = bf.Deserialize(file); + MultiplayerSaveContainer loadData = deserialized as MultiplayerSaveContainer; + if (loadData == null) + { + Main.helper.Log("Selected save is not a MultiplayerSaveContainer; falling back to vanilla load."); + return true; + } + + saveData = File.ReadAllBytes(path); loadData.Unpack(null); Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent()); } @@ -1335,19 +1415,33 @@ namespace KCM { Directory.CreateDirectory(LoadSave.GetSaveDir()); Guid guid = Guid.NewGuid(); - string path = (pathOverride != "") ? pathOverride : (LoadSave.GetSaveDir() + "/" + guid); + bool hasOverride = !string.IsNullOrWhiteSpace(pathOverride); + string path = hasOverride ? pathOverride : Path.Combine(LoadSave.GetSaveDir(), guid.ToString()); + if (hasOverride && !Path.IsPathRooted(path)) + path = Path.Combine(LoadSave.GetSaveDir(), pathOverride); Directory.CreateDirectory(path); Thread thread; try { thread = new Thread(new ParameterizedThreadStart(OutToFile)); - MultiplayerSaveContainer packedData = new MultiplayerSaveContainer().Pack(null); + MultiplayerSaveContainer packedData; + try + { + packedData = new MultiplayerSaveContainer().Pack(null); + } + catch (Exception ex) + { + Main.helper.Log("Failed to pack multiplayer save data; falling back to vanilla save."); + Main.helper.Log(ex.ToString()); + __result = null; + return true; + } Broadcast.OnSaveEvent.Broadcast(new OnSaveEvent()); thread.Start(new OutData { LoadSaveContainer = packedData, - Path = path + "/world" + Path = Path.Combine(path, "world") }); } catch (Exception e) @@ -1379,7 +1473,7 @@ namespace KCM try { - World.inst.TakeScreenshot(path + "/cover", new Func(World.inst.Func_CaptureWorldShot), onCompleteCallback); + World.inst.TakeScreenshot(Path.Combine(path, "cover"), new Func(World.inst.Func_CaptureWorldShot), onCompleteCallback); } catch (Exception e3) { diff --git a/Packets/Lobby/SaveTransferPacket.cs b/Packets/Lobby/SaveTransferPacket.cs index dc595d0..114fc8d 100644 --- a/Packets/Lobby/SaveTransferPacket.cs +++ b/Packets/Lobby/SaveTransferPacket.cs @@ -18,6 +18,14 @@ namespace KCM.Packets.Lobby public static bool loadingSave = false; public static int received = 0; + public static void ResetTransferState() + { + loadingSave = false; + received = 0; + saveData = new byte[1]; + chunksReceived = new bool[1]; + } + public int chunkId { get; set; } public int chunkSize { get; set; } diff --git a/RiptideSteamTransport/LobbyManager.cs b/RiptideSteamTransport/LobbyManager.cs index ca9ca72..21e6b69 100644 --- a/RiptideSteamTransport/LobbyManager.cs +++ b/RiptideSteamTransport/LobbyManager.cs @@ -154,23 +154,7 @@ namespace Riptide.Demos.Steam.PlayerHosted //NetworkManager.Singleton.StopServer(); //NetworkManager.Singleton.DisconnectClient(); SteamMatchmaking.LeaveLobby(lobbyId); - - if (KCClient.client.IsConnected) - KCClient.client.Disconnect(); - - StateObserver.ClearAll(); - - Main.helper.Log("clear players"); - Main.kCPlayers.Clear(); - Main.clientSteamIds.Clear(); - LobbyHandler.ClearPlayerList(); - LobbyHandler.ClearChatEntries(); - Main.helper.Log("end clear players"); - - if (KCServer.IsRunning) - KCServer.server.Stop(); - - + Main.ResetMultiplayerState("LeaveLobby"); Main.TransitionTo(MenuState.ServerBrowser); ServerBrowser.registerServer = false;