Compare commits

...

2 Commits

Author SHA1 Message Date
efa6016fe5 íyx 2025-12-13 16:08:37 +01:00
0b16efc4be Farm és save desync feltérképezése 2025-12-13 16:01:09 +01:00
3 changed files with 136 additions and 30 deletions

98
Main.cs
View File

@@ -1430,6 +1430,17 @@ namespace KCM
Main.helper.Log($"loading building: {building.FriendlyName}"); Main.helper.Log($"loading building: {building.FriendlyName}");
Main.helper.Log($" (teamid: {building.TeamID()})"); Main.helper.Log($" (teamid: {building.TeamID()})");
Main.helper.Log(p.ToString()); 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<Keep>() != null && building.TeamID() == p.PlayerLandmassOwner.teamId; bool flag2 = building.GetComponent<Keep>() != null && building.TeamID() == p.PlayerLandmassOwner.teamId;
Main.helper.Log("Set keep? " + flag2); Main.helper.Log("Set keep? " + flag2);
if (flag2) if (flag2)
@@ -1437,6 +1448,19 @@ namespace KCM
p.keep = building.GetComponent<Keep>(); p.keep = building.GetComponent<Keep>();
Main.helper.Log(p.keep.ToString()); 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; __result = building;
} }
else else
@@ -1467,16 +1491,6 @@ namespace KCM
Main.helper.Log("Saving player creativeMode"); Main.helper.Log("Saving player creativeMode");
__instance.creativeMode = p.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"); Main.helper.Log("Saving player upgrades");
__instance.GetType().GetField("upgrades", bindingFlags).SetValue(__instance, new List<Player.UpgradeType>()); __instance.GetType().GetField("upgrades", bindingFlags).SetValue(__instance, new List<Player.UpgradeType>());
@@ -2130,7 +2144,19 @@ namespace KCM
{ {
Assembly assembly = typeof(Building).Assembly; Assembly assembly = typeof(Building).Assembly;
Type[] types = new Type[] { typeof(Building) }; Type[] allTypes;
try
{
allTypes = assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
allTypes = e.Types.Where(t => t != null).ToArray();
}
var types = allTypes
.Where(t => t != null && typeof(Building).IsAssignableFrom(t) && !t.IsAbstract)
.ToArray();
var methodsInNamespace = types var methodsInNamespace = types
.SelectMany(t => t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => !m.IsAbstract)) .SelectMany(t => t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => !m.IsAbstract))
@@ -2175,6 +2201,56 @@ namespace KCM
} }
} }
[HarmonyPatch]
public class FieldSystemPlayerReferencePatch
{
static FieldInfo playerField;
static IEnumerable<MethodBase> 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<MethodBase>();
}
static IEnumerable<CodeInstruction> Transpiler(MethodBase method, IEnumerable<CodeInstruction> 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<CodeInstruction>(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] [HarmonyPatch]
public class PlayerPatch public class PlayerPatch

View File

@@ -29,38 +29,54 @@ namespace KCM.Packets.Lobby
public override void HandlePacketClient() 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 (initialisingTransfer)
if (saveData.Length == 1)
{ {
Main.helper.Log("Save Transfer started!"); Main.helper.Log("Save Transfer started!");
loadingSave = true; loadingSave = true;
received = 0;
ServerLobbyScript.LoadingSave.SetActive(true);
// save percentage
saveData = new byte[saveSize]; saveData = new byte[saveSize];
chunksReceived = new bool[totalChunks]; 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); Array.Copy(saveDataChunk, 0, saveData, saveDataIndex, saveDataChunk.Length);
// Mark this chunk as received
chunksReceived[chunkId] = true; chunksReceived[chunkId] = true;
// Seek to the next position to write to
received += chunkSize; received += chunkSize;
float savePercent = saveSize > 0 ? (float)received / (float)saveSize : 0f;
ServerLobbyScript.ProgressBar.fillAmount = savePercent; if (ServerLobbyScript.ProgressBar != null)
ServerLobbyScript.ProgressBarText.text = (savePercent * 100).ToString("0.00") + "%"; ServerLobbyScript.ProgressBar.fillAmount = savePercent;
ServerLobbyScript.ProgressText.text = $"{((float)(received / 1000)).ToString("0.00")} KB / {((float)(saveSize / 1000)).ToString("0.00")} KB"; 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) if (chunkId + 1 == totalChunks)
@@ -85,7 +101,13 @@ namespace KCM.Packets.Lobby
LoadSaveLoadHook.saveContainer.Unpack(null); LoadSaveLoadHook.saveContainer.Unpack(null);
Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent()); 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];
} }
} }

View File

@@ -18,6 +18,11 @@ A mellékelt log (`output.txt`) alapján több tipikus hiba okozta a szerver ind
- `PlayerReady` packet: ha nincs player, ne crasheljen - `PlayerReady` packet: ha nincs player, ne crasheljen
- Szerver oldalon a csatlakozáskor a játékos regisztráció/map frissítése - 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 - 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)
- Kompatibilitási fix: `World.inst.liverySets` lista esetén `.Count` használata `.Length` helyett (különben `Compilation failed` lehet egyes verziókon)
Érintett fájlok (főbb pontok): Érintett fájlok (főbb pontok):
@@ -27,6 +32,7 @@ A mellékelt log (`output.txt`) alapján több tipikus hiba okozta a szerver ind
- `Packets/Packet.cs` - `Packets/Packet.cs`
- `Packets/Lobby/PlayerReady.cs` - `Packets/Lobby/PlayerReady.cs`
- `Packets/Lobby/PlayerList.cs` - `Packets/Lobby/PlayerList.cs`
- `Packets/Lobby/SaveTransferPacket.cs`
- `KCServer.cs` - `KCServer.cs`
- `Packets/Handlers/LobbyHandler.cs` - `Packets/Handlers/LobbyHandler.cs`
- `RiptideSteamTransport/LobbyManager.cs` - `RiptideSteamTransport/LobbyManager.cs`
@@ -49,6 +55,8 @@ Ha továbbra is hibát látsz:
- Küldd el a `output.txt` releváns részét (a hiba előtti/utáni stack trace-t), vagy írd le a pontos üzenetet. - Küldd el a `output.txt` releváns részét (a hiba előtti/utáni stack trace-t), vagy írd le a pontos üzenetet.
- Írd meg, hogy: hostoltál-e, hány kliens csatlakozott, és mindenkin ugyanaz a mod-verzió van-e. - Írd meg, hogy: hostoltál-e, hány kliens csatlakozott, és mindenkin ugyanaz a mod-verzió van-e.
- Teszthez kapcsold ki a többi modot (különösen azokat, amik Harmony patch-elnek). A logban egy `Profiler` mod (`Profiler.ProfilerMod`) is hibázott, ez meg tudja zavarni a betöltést.
- Farm/termés desync esetén írd meg: host vagy kliens oldalon nem látszik-e a termés, új világban történik-e vagy save betöltés után, és hány perc játék után jön elő.
## Repo higiénia ## Repo higiénia