Farm és save desync feltérképezése
This commit is contained in:
89
Main.cs
89
Main.cs
@@ -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,10 @@ namespace KCM
|
|||||||
{
|
{
|
||||||
Assembly assembly = typeof(Building).Assembly;
|
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
|
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 +2192,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
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
- `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)
|
||||||
|
|
||||||
Érintett fájlok (főbb pontok):
|
É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/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`
|
||||||
|
|||||||
Reference in New Issue
Block a user