diff --git a/.gitignore b/.gitignore index 6dcb532..652bf8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ # Logs / local debug output -output*.txt *.log -/log.txt # OS junk .DS_Store @@ -24,4 +22,5 @@ Desktop.ini **/*.pdb /.claude -/*.png \ No newline at end of file +/*.png +/*.txt \ No newline at end of file diff --git a/KCServer.cs b/KCServer.cs index 04b55d5..3177169 100644 --- a/KCServer.cs +++ b/KCServer.cs @@ -110,7 +110,15 @@ namespace KCM private void OnApplicationQuit() { if (server != null && server.IsRunning) + { + new ShowModal + { + title = "Host disconnected", + message = "The host has left the game." + }.SendToAll(); + server.Stop(); + } } private void Preload(KCModHelper helper) diff --git a/Main.cs b/Main.cs index 5cc0928..018707c 100644 --- a/Main.cs +++ b/Main.cs @@ -14,6 +14,7 @@ using KCM.Packets.Game.GameWorld; using KCM.Packets.Handlers; using KCM.Packets.Lobby; using KCM.StateManagement.BuildingState; +using KCM.Packets.State; using KCM.StateManagement.Observers; using KCM.UI; using Newtonsoft.Json; @@ -53,6 +54,7 @@ namespace KCM public static Dictionary kCPlayers = new Dictionary(); public static Dictionary clientSteamIds = new Dictionary(); + public static Dictionary pendingBuildingStatePackets = new Dictionary(); public static KCPlayer GetPlayerByClientID(ushort clientId) { @@ -81,6 +83,26 @@ namespace KCM return Player.inst; } + public static void QueuePendingBuildingState(BuildingStatePacket packet) + { + if (packet == null) + return; + + pendingBuildingStatePackets[packet.guid] = packet; + } + + public static void ApplyPendingBuildingState(Building building) + { + if (building == null) + return; + + if (pendingBuildingStatePackets.TryGetValue(building.guid, out var pending)) + { + pending.ApplyToBuilding(building); + pendingBuildingStatePackets.Remove(building.guid); + } + } + public static Player GetPlayerByBuilding(Building building) { try @@ -1226,8 +1248,22 @@ namespace KCM Stream file = new FileStream(path, FileMode.Open); try { - MultiplayerSaveContainer loadData = (MultiplayerSaveContainer)bf.Deserialize(file); - loadData.Unpack(null); + object deserialized = bf.Deserialize(file); + + if (deserialized is MultiplayerSaveContainer loadData) + { + loadData.Unpack(null); + } + else if (deserialized is LoadSaveContainer legacyLoadData) + { + Main.helper.Log($"Deserialized fallback save type ({legacyLoadData.GetType().FullName}), applying base container."); + legacyLoadData.Unpack(null); + } + else + { + throw new InvalidCastException($"Unexpected save data type: {deserialized?.GetType().FullName}"); + } + Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent()); } catch (Exception e) diff --git a/Packets/Game/GameWorld/WorldPlace.cs b/Packets/Game/GameWorld/WorldPlace.cs index ab82561..b0df199 100644 --- a/Packets/Game/GameWorld/WorldPlace.cs +++ b/Packets/Game/GameWorld/WorldPlace.cs @@ -102,6 +102,7 @@ namespace KCM.Packets.Game.GameWorld Main.helper.Log(building.LandMass().ToString()); Main.helper.Log("Player add Building unpacked"); player.inst.AddBuilding(building); + Main.ApplyPendingBuildingState(building); try { diff --git a/Packets/State/BuildingStatePacket.cs b/Packets/State/BuildingStatePacket.cs index 70ea74b..eb18c3c 100644 --- a/Packets/State/BuildingStatePacket.cs +++ b/Packets/State/BuildingStatePacket.cs @@ -36,25 +36,33 @@ namespace KCM.Packets.State { if (clientId == KCClient.client.Id) return; //prevent double placing on same client - //Main.helper.Log("Received building state packet for: " + uniqueName + " from " + Main.kCPlayers[Main.GetPlayerByClientID(clientId).steamId].name + $"({clientId})"); - - Building building = player.inst.GetBuilding(guid); if (building == null) { Main.helper.Log("Building not found."); + Main.QueuePendingBuildingState(this); return; } + ApplyToBuilding(building); + } + + public override void HandlePacketServer() + { + //throw new NotImplementedException(); + } + + public void ApplyToBuilding(Building building) + { + if (building == null) + return; + try { - //PrintProperties(); - building.UniqueName = uniqueName; building.customName = customName; - building.transform.position = this.globalPosition; building.transform.GetChild(0).rotation = this.rotation; building.transform.GetChild(0).localPosition = this.localPosition; @@ -63,7 +71,6 @@ namespace KCM.Packets.State SetPrivateFieldValue(building, "placed", placed); SetPrivateFieldValue(building, "resourceProgress", resourceProgress); - building.Open = open; building.doBuildAnimation = doBuildAnimation; building.constructionPaused = constructionPaused; @@ -71,7 +78,6 @@ namespace KCM.Packets.State building.Life = life; building.ModifiedMaxLife = ModifiedMaxLife; - //building.yearBuilt = yearBuilt; SetPrivateFieldValue(building, "yearBuilt", yearBuilt); @@ -86,11 +92,6 @@ namespace KCM.Packets.State } } - public override void HandlePacketServer() - { - //throw new NotImplementedException(); - } - private void SetPrivateFieldValue(object obj, string fieldName, object value) { Type type = obj.GetType();