From 0b16efc4bea84677ff8caea544ff56f22c3b1d10 Mon Sep 17 00:00:00 2001 From: devbeni Date: Sat, 13 Dec 2025 16:01:09 +0100 Subject: [PATCH] =?UTF-8?q?Farm=20=C3=A9s=20save=20desync=20felt=C3=A9rk?= =?UTF-8?q?=C3=A9pez=C3=A9se?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Main.cs | 89 +++++++++++++++++++++++++---- Packets/Lobby/SaveTransferPacket.cs | 60 +++++++++++++------ README.md | 5 ++ 3 files changed, 124 insertions(+), 30 deletions(-) diff --git a/Main.cs b/Main.cs index 701d47d..6b7f49d 100644 --- a/Main.cs +++ b/Main.cs @@ -1430,6 +1430,17 @@ namespace KCM Main.helper.Log($"loading building: {building.FriendlyName}"); Main.helper.Log($" (teamid: {building.TeamID()})"); Main.helper.Log(p.ToString()); + + try + { + p.PlayerLandmassOwner.TakeOwnership(building.LandMass()); + } + catch (Exception e) + { + Main.helper.Log("Failed setting landmass ownership during load"); + Main.helper.Log(e.Message); + } + bool flag2 = building.GetComponent() != null && building.TeamID() == p.PlayerLandmassOwner.teamId; Main.helper.Log("Set keep? " + flag2); if (flag2) @@ -1437,6 +1448,19 @@ namespace KCM p.keep = building.GetComponent(); Main.helper.Log(p.keep.ToString()); } + + try + { + World.inst.PlaceFromLoad(building); + structureData.UnpackStage2(building); + building.SetVisibleForFog(false); + } + catch (Exception e) + { + Main.helper.Log("Error placing building into world during load"); + Main.helper.Log(e.Message); + Main.helper.Log(e.StackTrace); + } __result = building; } else @@ -1467,16 +1491,6 @@ namespace KCM Main.helper.Log("Saving player creativeMode"); __instance.creativeMode = p.creativeMode; - //cmo options not used for saving or loading in multiplayer - /**for (int i = 0; i < p.cmoOptionsOn.Length; i++) - { - bool flag = p.cmoOptionsOn[i]; - if (flag) - { - __instance.cmoOptions.Add((Player.CreativeOptions)i); - } - }**/ - Main.helper.Log("Saving player upgrades"); __instance.GetType().GetField("upgrades", bindingFlags).SetValue(__instance, new List()); @@ -2130,7 +2144,10 @@ namespace KCM { Assembly assembly = typeof(Building).Assembly; - Type[] types = new Type[] { typeof(Building) }; + var types = assembly + .GetTypes() + .Where(t => t != null && typeof(Building).IsAssignableFrom(t) && !t.IsAbstract) + .ToArray(); var methodsInNamespace = types .SelectMany(t => t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => !m.IsAbstract)) @@ -2175,6 +2192,56 @@ namespace KCM } } + [HarmonyPatch] + public class FieldSystemPlayerReferencePatch + { + static FieldInfo playerField; + + static IEnumerable TargetMethods() + { + var methodsInNamespace = typeof(FieldSystem) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) + .Where(m => !m.IsAbstract) + .ToList(); + + helper.Log("Methods in namespace: " + methodsInNamespace.Count); + + return methodsInNamespace.ToArray().Cast(); + } + + static IEnumerable Transpiler(MethodBase method, IEnumerable instructions) + { + if (playerField == null) + { + var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; + playerField = typeof(FieldSystem).GetFields(bindingFlags).FirstOrDefault(f => f.FieldType == typeof(Player)); + } + + if (playerField == null) + return instructions; + + int playerInstCount = 0; + + var codes = new List(instructions); + for (var i = 0; i < codes.Count; i++) + { + if (codes[i].opcode == OpCodes.Ldsfld && codes[i].operand.ToString() == "Player inst") + { + playerInstCount++; + + codes[i].opcode = OpCodes.Ldarg_0; + codes[i].operand = null; + codes.Insert(++i, new CodeInstruction(OpCodes.Ldfld, playerField)); + } + } + + if (playerInstCount > 0) + Main.helper.Log($"Found {playerInstCount} static FieldSystem Player.inst references in {method.Name}"); + + return codes.AsEnumerable(); + } + } + [HarmonyPatch] public class PlayerPatch diff --git a/Packets/Lobby/SaveTransferPacket.cs b/Packets/Lobby/SaveTransferPacket.cs index 18282e6..ec62433 100644 --- a/Packets/Lobby/SaveTransferPacket.cs +++ b/Packets/Lobby/SaveTransferPacket.cs @@ -29,38 +29,54 @@ namespace KCM.Packets.Lobby public override void HandlePacketClient() { - float savePercent = (float)received / (float)saveSize; + bool initialisingTransfer = !loadingSave || + saveData == null || + saveData.Length != saveSize || + chunksReceived == null || + chunksReceived.Length != totalChunks; - // Initialize saveData and chunksReceived on the first packet received - if (saveData.Length == 1) + if (initialisingTransfer) { - Main.helper.Log("Save Transfer started!"); loadingSave = true; - - ServerLobbyScript.LoadingSave.SetActive(true); - - // save percentage - + received = 0; saveData = new byte[saveSize]; chunksReceived = new bool[totalChunks]; + + if (ServerLobbyScript.LoadingSave != null) + ServerLobbyScript.LoadingSave.SetActive(true); } + if (chunkId < 0 || chunkId >= totalChunks) + { + Main.helper.Log($"Invalid save chunk id: {chunkId} / {totalChunks}"); + return; + } + + if (saveDataChunk == null) + { + Main.helper.Log($"Null save chunk data for chunk: {chunkId}"); + return; + } + + if (saveDataIndex < 0 || saveDataIndex + saveDataChunk.Length > saveData.Length) + { + Main.helper.Log($"Invalid save chunk write range: index={saveDataIndex} len={saveDataChunk.Length} size={saveData.Length}"); + return; + } - // Copy the chunk data into the correct position in saveData Array.Copy(saveDataChunk, 0, saveData, saveDataIndex, saveDataChunk.Length); - - // Mark this chunk as received chunksReceived[chunkId] = true; - - // Seek to the next position to write to received += chunkSize; - - ServerLobbyScript.ProgressBar.fillAmount = savePercent; - ServerLobbyScript.ProgressBarText.text = (savePercent * 100).ToString("0.00") + "%"; - ServerLobbyScript.ProgressText.text = $"{((float)(received / 1000)).ToString("0.00")} KB / {((float)(saveSize / 1000)).ToString("0.00")} KB"; + float savePercent = saveSize > 0 ? (float)received / (float)saveSize : 0f; + if (ServerLobbyScript.ProgressBar != null) + ServerLobbyScript.ProgressBar.fillAmount = savePercent; + if (ServerLobbyScript.ProgressBarText != null) + ServerLobbyScript.ProgressBarText.text = (savePercent * 100).ToString("0.00") + "%"; + if (ServerLobbyScript.ProgressText != null) + ServerLobbyScript.ProgressText.text = $"{((float)(received / 1000)).ToString("0.00")} KB / {((float)(saveSize / 1000)).ToString("0.00")} KB"; if (chunkId + 1 == totalChunks) @@ -85,7 +101,13 @@ namespace KCM.Packets.Lobby LoadSaveLoadHook.saveContainer.Unpack(null); Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent()); - ServerLobbyScript.LoadingSave.SetActive(false); + if (ServerLobbyScript.LoadingSave != null) + ServerLobbyScript.LoadingSave.SetActive(false); + + loadingSave = false; + received = 0; + saveData = new byte[1]; + chunksReceived = new bool[1]; } } diff --git a/README.md b/README.md index a833f51..38f13d2 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ A mellékelt log (`output.txt`) alapján több tipikus hiba okozta a szerver ind - `PlayerReady` packet: ha nincs player, ne crasheljen - Szerver oldalon a csatlakozáskor a játékos regisztráció/map frissítése - Kilépés/clear esetén `clientSteamIds` takarítása, hogy ne maradjanak “árva” bejegyzések +- Épületek `Player.inst` referenciáinak patch-elése már nem csak a base `Building` osztályban fut, hanem az összes `Building`-ből származó típusban (pl. farmok speciális logikája) +- `FieldSystem` `Player.inst` referenciáinak patch-elése (farm/termés állapotkezelés több helyen erre támaszkodik) +- Mentés betöltéskor a `ProcessBuilding` útvonal kiegészítése `World.inst.PlaceFromLoad(...)` + `UnpackStage2(...)` hívásokkal (különösen fontos a “világba helyezés” mellékhatásai miatt, pl. farm/field regisztráció) +- Save transfer kliens oldalon robusztusabb inicializálás/reset (ne ragadjon be a statikus állapot több betöltés után, plusz bounds/null ellenőrzések) Érintett fájlok (főbb pontok): @@ -27,6 +31,7 @@ A mellékelt log (`output.txt`) alapján több tipikus hiba okozta a szerver ind - `Packets/Packet.cs` - `Packets/Lobby/PlayerReady.cs` - `Packets/Lobby/PlayerList.cs` +- `Packets/Lobby/SaveTransferPacket.cs` - `KCServer.cs` - `Packets/Handlers/LobbyHandler.cs` - `RiptideSteamTransport/LobbyManager.cs`