Compare commits
9 Commits
8ac2f2df1c
...
dc50bf2892
| Author | SHA1 | Date | |
|---|---|---|---|
| dc50bf2892 | |||
| e636ad6e19 | |||
| fbb947a23b | |||
| a2d87106ba | |||
| 0f8f3ce818 | |||
| 07c7e14cde | |||
| 0549ab0e19 | |||
| f45402af9a | |||
| 6bf74dda43 |
@@ -102,6 +102,7 @@ namespace KCM
|
||||
public static void Connect(string ip)
|
||||
{
|
||||
Main.helper.Log("Trying to connect to: " + ip);
|
||||
try { Application.runInBackground = true; } catch { }
|
||||
client.Connect(ip, useMessageHandlers: false);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,25 @@ namespace KCM.LoadSaveOverrides
|
||||
this.DragonSpawnSaveData = new DragonSpawn.DragonSpawnSaveData().Pack(DragonSpawn.inst);
|
||||
this.UnitSystemSaveData = new UnitSystem.UnitSystemSaveData().Pack(UnitSystem.inst);
|
||||
this.RaidSystemSaveData2 = new RaiderSystem.RaiderSystemSaveData2().Pack(RaiderSystem.inst);
|
||||
this.ShipSystemSaveData = new ShipSystem.ShipSystemSaveData().Pack(ShipSystem.inst);
|
||||
|
||||
if (ShipSystem.inst != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.ShipSystemSaveData = new ShipSystem.ShipSystemSaveData().Pack(ShipSystem.inst);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.helper.Log("Error packing ShipSystem for save; skipping ShipSystemSaveData.");
|
||||
Main.helper.Log(ex.ToString());
|
||||
this.ShipSystemSaveData = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ShipSystemSaveData = null;
|
||||
}
|
||||
|
||||
this.AIBrainsSaveData = new AIBrainsContainer.SaveData().Pack(AIBrainsContainer.inst);
|
||||
this.SiegeMonsterSaveData = new SiegeMonster.SiegeMonsterSaveData().Pack(null);
|
||||
this.CartSystemSaveData = new CartSystem.CartSystemSaveData().Pack(CartSystem.inst);
|
||||
@@ -247,6 +265,20 @@ namespace KCM.LoadSaveOverrides
|
||||
}
|
||||
Main.helper.Log("Unpacking done");
|
||||
|
||||
try
|
||||
{
|
||||
Main.helper.Log("Post-load: rebuilding path costs + villager grid");
|
||||
try { World.inst.SetupInitialPathCosts(); } catch (Exception e) { Main.helper.Log(e.ToString()); }
|
||||
try { World.inst.RebuildVillagerGrid(); } catch (Exception e) { Main.helper.Log(e.ToString()); }
|
||||
try { Player.inst.irrigation.UpdateIrrigation(); } catch (Exception e) { Main.helper.Log(e.ToString()); }
|
||||
try { Player.inst.CalcMaxResources(null, -1); } catch (Exception e) { Main.helper.Log(e.ToString()); }
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Main.helper.Log("Post-load rebuild failed");
|
||||
Main.helper.Log(e.ToString());
|
||||
}
|
||||
|
||||
|
||||
World.inst.UpscaleFeatures();
|
||||
Player.inst.RefreshVisibility(true);
|
||||
@@ -279,6 +311,14 @@ namespace KCM.LoadSaveOverrides
|
||||
loadTickDelayField.SetValue(JobSystem.inst, 1);
|
||||
}
|
||||
|
||||
// VillagerSystem.inst.loadTickDelay = 1;
|
||||
Type villagerSystemType = typeof(VillagerSystem);
|
||||
loadTickDelayField = villagerSystemType.GetField("loadTickDelay", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (loadTickDelayField != null)
|
||||
{
|
||||
loadTickDelayField.SetValue(VillagerSystem.inst, 1);
|
||||
}
|
||||
|
||||
Main.helper.Log($"Setting kingdom name to: {kingdomNames[Main.PlayerSteamID]}");
|
||||
TownNameUI.inst.SetTownName(kingdomNames[Main.PlayerSteamID]);
|
||||
|
||||
|
||||
441
Main.cs
441
Main.cs
@@ -57,6 +57,28 @@ namespace KCM
|
||||
|
||||
private static readonly Dictionary<int, long> lastTeamIdLookupLogMs = new Dictionary<int, long>();
|
||||
private static int resetInProgress = 0;
|
||||
private static int multiplayerSaveLoadInProgress = 0;
|
||||
private static int suppressVillagerTeleportPackets = 0;
|
||||
|
||||
public static bool IsMultiplayerSaveLoadInProgress
|
||||
{
|
||||
get { return Volatile.Read(ref multiplayerSaveLoadInProgress) != 0; }
|
||||
}
|
||||
|
||||
public static void SetMultiplayerSaveLoadInProgress(bool inProgress)
|
||||
{
|
||||
Interlocked.Exchange(ref multiplayerSaveLoadInProgress, inProgress ? 1 : 0);
|
||||
}
|
||||
|
||||
private static bool ShouldSuppressVillagerTeleportPackets
|
||||
{
|
||||
get { return Volatile.Read(ref suppressVillagerTeleportPackets) != 0; }
|
||||
}
|
||||
|
||||
private static void SetSuppressVillagerTeleportPackets(bool suppress)
|
||||
{
|
||||
Interlocked.Exchange(ref suppressVillagerTeleportPackets, suppress ? 1 : 0);
|
||||
}
|
||||
|
||||
public static void ResetMultiplayerState(string reason = null)
|
||||
{
|
||||
@@ -82,6 +104,7 @@ namespace KCM
|
||||
try { LoadSaveLoadAtPathHook.saveData = new byte[0]; } catch { }
|
||||
|
||||
try { LobbyManager.loadingSave = false; } catch { }
|
||||
try { SetMultiplayerSaveLoadInProgress(false); } catch { }
|
||||
|
||||
try
|
||||
{
|
||||
@@ -132,6 +155,164 @@ namespace KCM
|
||||
return kCPlayers[clientSteamIds[clientId]];
|
||||
}
|
||||
|
||||
private static void SetLoadTickDelay(object instance, int ticks)
|
||||
{
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
FieldInfo loadTickDelayField = instance.GetType().GetField("loadTickDelay", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (loadTickDelayField != null)
|
||||
loadTickDelayField.SetValue(instance, ticks);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetVillagerPosition(Villager villager, out Vector3 position)
|
||||
{
|
||||
position = Vector3.zero;
|
||||
if (villager == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
Type type = villager.GetType();
|
||||
|
||||
string[] gameObjectNames = new string[] { "gameObject", "go", "Go" };
|
||||
for (int i = 0; i < gameObjectNames.Length; i++)
|
||||
{
|
||||
string name = gameObjectNames[i];
|
||||
|
||||
PropertyInfo prop = type.GetProperty(name, flags);
|
||||
if (prop != null && typeof(GameObject).IsAssignableFrom(prop.PropertyType))
|
||||
{
|
||||
GameObject go = prop.GetValue(villager, null) as GameObject;
|
||||
if (go != null)
|
||||
{
|
||||
position = go.transform.position;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo field = type.GetField(name, flags);
|
||||
if (field != null && typeof(GameObject).IsAssignableFrom(field.FieldType))
|
||||
{
|
||||
GameObject go = field.GetValue(villager) as GameObject;
|
||||
if (go != null)
|
||||
{
|
||||
position = go.transform.position;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string[] positionNames = new string[] { "pos", "Pos", "position", "Position" };
|
||||
for (int i = 0; i < positionNames.Length; i++)
|
||||
{
|
||||
string name = positionNames[i];
|
||||
|
||||
PropertyInfo prop = type.GetProperty(name, flags);
|
||||
if (prop != null && prop.PropertyType == typeof(Vector3))
|
||||
{
|
||||
position = (Vector3)prop.GetValue(villager, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
FieldInfo field = type.GetField(name, flags);
|
||||
if (field != null && field.FieldType == typeof(Vector3))
|
||||
{
|
||||
position = (Vector3)field.GetValue(villager);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
string[] getPosNames = new string[] { "GetPos", "GetPosition" };
|
||||
for (int i = 0; i < getPosNames.Length; i++)
|
||||
{
|
||||
MethodInfo method = type.GetMethod(getPosNames[i], flags, null, new Type[0], null);
|
||||
if (method != null && method.ReturnType == typeof(Vector3))
|
||||
{
|
||||
position = (Vector3)method.Invoke(villager, null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void RunPostLoadRebuild(string reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
helper?.Log("Post-load rebuild: " + (reason ?? string.Empty));
|
||||
|
||||
try { World.inst.SetupInitialPathCosts(); } catch (Exception e) { helper?.Log(e.ToString()); }
|
||||
try { World.inst.RebuildVillagerGrid(); } catch (Exception e) { helper?.Log(e.ToString()); }
|
||||
try { Player.inst.irrigation.UpdateIrrigation(); } catch (Exception e) { helper?.Log(e.ToString()); }
|
||||
try { Player.inst.CalcMaxResources(null, -1); } catch (Exception e) { helper?.Log(e.ToString()); }
|
||||
|
||||
try { if (UnitSystem.inst != null) UnitSystem.inst.enabled = true; } catch (Exception e) { helper?.Log(e.ToString()); }
|
||||
try { if (JobSystem.inst != null) JobSystem.inst.enabled = true; } catch (Exception e) { helper?.Log(e.ToString()); }
|
||||
try { if (VillagerSystem.inst != null) VillagerSystem.inst.enabled = true; } catch (Exception e) { helper?.Log(e.ToString()); }
|
||||
|
||||
SetLoadTickDelay(Player.inst, 1);
|
||||
SetLoadTickDelay(UnitSystem.inst, 1);
|
||||
SetLoadTickDelay(JobSystem.inst, 1);
|
||||
SetLoadTickDelay(VillagerSystem.inst, 1);
|
||||
|
||||
try
|
||||
{
|
||||
// A nudge helps recover from cases where villagers have jobs but never begin moving.
|
||||
SetSuppressVillagerTeleportPackets(true);
|
||||
foreach (var kcPlayer in kCPlayers.Values)
|
||||
{
|
||||
if (kcPlayer == null || kcPlayer.inst == null)
|
||||
continue;
|
||||
|
||||
var workers = kcPlayer.inst.Workers;
|
||||
for (int i = 0; i < workers.Count; i++)
|
||||
{
|
||||
Villager v = workers.data[i];
|
||||
if (v == null)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
Vector3 pos;
|
||||
if (TryGetVillagerPosition(v, out pos))
|
||||
v.TeleportTo(pos);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
helper?.Log("Post-load villager nudge failed");
|
||||
helper?.Log(e.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetSuppressVillagerTeleportPackets(false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
helper?.Log("Post-load rebuild failed");
|
||||
helper?.Log(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public static Player GetPlayerByTeamID(int teamId) // Need to replace building / production types so that the correct player is used. IResourceStorage and IResourceProvider, and jobs
|
||||
{
|
||||
KCPlayer match = kCPlayers.Values.FirstOrDefault(p =>
|
||||
@@ -383,6 +564,26 @@ namespace KCM
|
||||
|
||||
if ((MenuState)newState == MenuState.Menu && (KCClient.client.IsConnected || KCServer.IsRunning))
|
||||
ResetMultiplayerState("Returned to main menu");
|
||||
|
||||
if ((MenuState)newState == (MenuState)200 && KCClient.client.IsConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
RunPostLoadRebuild("Entered playing mode");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (SpeedControlUI.inst != null)
|
||||
SpeedControlUI.inst.SetSpeed(1);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,26 +942,14 @@ namespace KCM
|
||||
[HarmonyPatch(typeof(Player), "AddBuilding")]
|
||||
public class PlayerAddBuildingHook
|
||||
{
|
||||
static int step = 1;
|
||||
static void LogStep(bool reset = false)
|
||||
{
|
||||
if (reset)
|
||||
step = 1;
|
||||
|
||||
Main.helper.Log(step.ToString());
|
||||
step++;
|
||||
}
|
||||
|
||||
public static bool Prefix(Player __instance, Building b)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (KCClient.client.IsConnected)
|
||||
{
|
||||
LogStep(true);
|
||||
__instance.Buildings.Add(b);
|
||||
IResourceStorage[] storages = b.GetComponents<IResourceStorage>();
|
||||
LogStep();
|
||||
for (int i = 0; i < storages.Length; i++)
|
||||
{
|
||||
bool flag = !storages[i].IsPrivate();
|
||||
@@ -769,50 +958,38 @@ namespace KCM
|
||||
FreeResourceManager.inst.AddResourceStorage(storages[i]);
|
||||
}
|
||||
}
|
||||
LogStep();
|
||||
int landMass = b.LandMass();
|
||||
Home res = b.GetComponent<Home>();
|
||||
bool flag2 = res != null;
|
||||
LogStep();
|
||||
if (flag2)
|
||||
{
|
||||
__instance.Residentials.Add(res);
|
||||
__instance.ResidentialsPerLandmass[landMass].Add(res);
|
||||
}
|
||||
WagePayer wagePayer = b.GetComponent<WagePayer>();
|
||||
LogStep();
|
||||
bool flag3 = wagePayer != null;
|
||||
if (flag3)
|
||||
{
|
||||
__instance.WagePayers.Add(wagePayer);
|
||||
}
|
||||
RadiusBonus radiusBonus = b.GetComponent<RadiusBonus>();
|
||||
LogStep();
|
||||
bool flag4 = radiusBonus != null;
|
||||
if (flag4)
|
||||
{
|
||||
__instance.RadiusBonuses.Add(radiusBonus);
|
||||
}
|
||||
LogStep();
|
||||
var globalBuildingRegistry = __instance.GetType().GetField("globalBuildingRegistry", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance) as ArrayExt<Player.BuildingRegistry>;
|
||||
LogStep();
|
||||
var landMassBuildingRegistry = __instance.GetType().GetField("landMassBuildingRegistry", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance) as ArrayExt<Player.LandMassBuildingRegistry>;
|
||||
LogStep();
|
||||
var unbuiltBuildingsPerLandmass = __instance.GetType().GetField("unbuiltBuildingsPerLandmass", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance) as ArrayExt<ArrayExt<Building>>;
|
||||
LogStep();
|
||||
|
||||
__instance.AddToRegistry(globalBuildingRegistry, b);
|
||||
LogStep();
|
||||
__instance.AddToRegistry(landMassBuildingRegistry.data[landMass].registry, b);
|
||||
LogStep();
|
||||
landMassBuildingRegistry.data[landMass].buildings.Add(b);
|
||||
LogStep();
|
||||
bool flag5 = !b.IsBuilt();
|
||||
if (flag5)
|
||||
{
|
||||
unbuiltBuildingsPerLandmass.data[landMass].Add(b);
|
||||
}
|
||||
LogStep();
|
||||
|
||||
|
||||
return false;
|
||||
@@ -1068,39 +1245,75 @@ namespace KCM
|
||||
public class SpeedControlUISetSpeedHook
|
||||
{
|
||||
private static long lastTime = 0;
|
||||
private static long lastClientBlockLogTime = 0;
|
||||
private static long lastHostPauseTraceLogTime = 0;
|
||||
private static int lastSentSpeed = -1;
|
||||
|
||||
public static bool Prefix()
|
||||
public static bool Prefix(int idx, ref bool __state)
|
||||
{
|
||||
if (KCClient.client.IsConnected)
|
||||
{
|
||||
if ((DateTimeOffset.Now.ToUnixTimeMilliseconds() - lastTime) < 250) // Set speed spam fix / hack
|
||||
return false;
|
||||
__state = false;
|
||||
if (!KCClient.client.IsConnected)
|
||||
return true;
|
||||
|
||||
if (!new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
|
||||
return false;
|
||||
bool calledFromPacket = false;
|
||||
try { calledFromPacket = PacketHandler.IsHandlingPacket; } catch { }
|
||||
|
||||
// In multiplayer, keep time control authoritative to the host to avoid clients pausing/stalling the simulation.
|
||||
if (!KCServer.IsRunning)
|
||||
{
|
||||
if (calledFromPacket)
|
||||
return true;
|
||||
|
||||
long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if ((now - lastClientBlockLogTime) >= 2000)
|
||||
{
|
||||
lastClientBlockLogTime = now;
|
||||
Main.helper.Log("Blocked SpeedControlUI.SetSpeed on non-host client: " + idx);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!calledFromPacket)
|
||||
{
|
||||
long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
// Ensure that real speed changes are always propagated, even if they happen in quick succession (eg. pause/unpause).
|
||||
if (idx != lastSentSpeed || (now - lastTime) >= 250) // Set speed spam fix / hack
|
||||
__state = true;
|
||||
|
||||
// Diagnostics for "random pause": log a stack trace when the host hits speed 0 from local code.
|
||||
if (idx == 0 && (now - lastHostPauseTraceLogTime) >= 2000)
|
||||
{
|
||||
lastHostPauseTraceLogTime = now;
|
||||
try
|
||||
{
|
||||
Main.helper.Log("Host speed set to 0 (pause). Call stack:");
|
||||
Main.helper.Log(new StackTrace(2, false).ToString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Postfix(int idx, bool skipNextSfx)
|
||||
public static void Postfix(int idx, bool skipNextSfx, bool __state)
|
||||
{
|
||||
if (KCClient.client.IsConnected)
|
||||
{
|
||||
/*Main.helper.Log($"set speed Called by 0: {new StackFrame(0).GetMethod()} {new StackFrame(0).GetMethod().Name.Contains("HandlePacket")}");
|
||||
Main.helper.Log($"set speed Called by 1: {new StackFrame(1).GetMethod()} {new StackFrame(1).GetMethod().Name.Contains("HandlePacket")}");
|
||||
Main.helper.Log($"set speed Called by 2: {new StackFrame(2).GetMethod()} {new StackFrame(2).GetMethod().Name.Contains("HandlePacket")}");
|
||||
Main.helper.Log($"set speed Called by 3: {new StackFrame(3).GetMethod()} {new StackFrame(3).GetMethod().Name.Contains("HandlePacket")}");*/
|
||||
|
||||
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
|
||||
if (!__state)
|
||||
return;
|
||||
|
||||
Main.helper.Log("SpeedControlUI.SetSpeed (local): " + idx);
|
||||
new SetSpeed()
|
||||
{
|
||||
speed = idx
|
||||
}.Send();
|
||||
|
||||
lastTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
lastSentSpeed = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1209,7 +1422,12 @@ namespace KCM
|
||||
{
|
||||
if (KCClient.client.IsConnected)
|
||||
{
|
||||
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
|
||||
if (ShouldSuppressVillagerTeleportPackets)
|
||||
return;
|
||||
|
||||
bool calledFromPacket = false;
|
||||
try { calledFromPacket = PacketHandler.IsHandlingPacket; } catch { }
|
||||
if (calledFromPacket)
|
||||
return;
|
||||
|
||||
new VillagerTeleportTo()
|
||||
@@ -1257,18 +1475,13 @@ namespace KCM
|
||||
public static bool Prefix(ref string __result)
|
||||
{
|
||||
Main.helper.Log("Get save dir");
|
||||
if (KCClient.client.IsConnected)
|
||||
if (KCServer.IsRunning)
|
||||
{
|
||||
if (KCServer.IsRunning)
|
||||
{
|
||||
|
||||
}
|
||||
__result = Application.persistentDataPath + "/Saves/Multiplayer";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
__result = Application.persistentDataPath + "/Saves"; ;
|
||||
__result = Application.persistentDataPath + "/Saves";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1279,8 +1492,9 @@ namespace KCM
|
||||
//public static string saveFile = "";
|
||||
public static byte[] saveData = new byte[0];
|
||||
|
||||
public static bool Prefix(string path, string filename, bool visitedWorld)
|
||||
public static bool Prefix(string path, string filename, bool visitedWorld, ref bool __state)
|
||||
{
|
||||
__state = false;
|
||||
if (KCServer.IsRunning)
|
||||
{
|
||||
Main.helper.Log("Trying to load multiplayer save");
|
||||
@@ -1294,20 +1508,18 @@ namespace KCM
|
||||
{
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
bf.Binder = new MultiplayerSaveDeserializationBinder();
|
||||
Stream file = new FileStream(path, FileMode.Open);
|
||||
Stream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
MultiplayerSaveContainer loadData = null;
|
||||
try
|
||||
{
|
||||
object deserialized = bf.Deserialize(file);
|
||||
MultiplayerSaveContainer loadData = deserialized as MultiplayerSaveContainer;
|
||||
loadData = deserialized as MultiplayerSaveContainer;
|
||||
if (loadData == null)
|
||||
{
|
||||
Main.helper.Log("Selected save is not a MultiplayerSaveContainer; falling back to vanilla load.");
|
||||
saveData = new byte[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
saveData = File.ReadAllBytes(path);
|
||||
loadData.Unpack(null);
|
||||
Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1326,6 +1538,38 @@ namespace KCM
|
||||
file.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
saveData = File.ReadAllBytes(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
saveData = new byte[0];
|
||||
Main.helper.Log("Failed reading save bytes for transfer; clients will not be able to load this save.");
|
||||
Main.helper.Log(e.ToString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Main.SetMultiplayerSaveLoadInProgress(true);
|
||||
__state = true;
|
||||
loadData.Unpack(null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Main.SetMultiplayerSaveLoadInProgress(false);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
RunPostLoadRebuild("LoadAtPath (multiplayer)");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1333,6 +1577,17 @@ namespace KCM
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Postfix(string path, string filename, bool visitedWorld, bool __state)
|
||||
{
|
||||
if (!KCClient.client.IsConnected)
|
||||
return;
|
||||
|
||||
if (__state)
|
||||
return;
|
||||
|
||||
RunPostLoadRebuild("LoadAtPath (vanilla)");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(LoadSave), "Load")]
|
||||
@@ -1524,7 +1779,7 @@ namespace KCM
|
||||
{
|
||||
public static bool Prefix(Building.BuildingSaveData structureData, Player p, ref Building __result)
|
||||
{
|
||||
if (KCClient.client.IsConnected)
|
||||
if (KCClient.client.IsConnected && Main.IsMultiplayerSaveLoadInProgress)
|
||||
{
|
||||
|
||||
Building Building = GameState.inst.GetPlaceableByUniqueName(structureData.uniqueName);
|
||||
@@ -1561,19 +1816,6 @@ namespace KCM
|
||||
p.keep = keep;
|
||||
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
|
||||
@@ -1597,6 +1839,12 @@ namespace KCM
|
||||
if (KCClient.client.IsConnected)
|
||||
{
|
||||
var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
var landmassOwner = p.PlayerLandmassOwner;
|
||||
if (landmassOwner == null)
|
||||
{
|
||||
Main.helper.Log("PlayerLandmassOwner was null during PlayerSaveData.Pack; using vanilla save pack.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Main.helper.Log("Running patched player pack method");
|
||||
Main.helper.Log("Saving banner system");
|
||||
@@ -1608,8 +1856,8 @@ namespace KCM
|
||||
__instance.GetType().GetField("upgrades", bindingFlags).SetValue(__instance, new List<Player.UpgradeType>());
|
||||
|
||||
|
||||
Main.helper.Log("Saving player bannerIdx");
|
||||
__instance.bannerIdx = p.PlayerLandmassOwner.bannerIdx;
|
||||
Main.helper.Log("Saving player bannerIdx");
|
||||
__instance.bannerIdx = landmassOwner.bannerIdx;
|
||||
|
||||
Main.helper.Log("Saving player WorkersArray");
|
||||
__instance.WorkersArray = new Villager.VillagerSaveData[p.Workers.Count];
|
||||
@@ -1622,11 +1870,13 @@ namespace KCM
|
||||
}
|
||||
}
|
||||
|
||||
Main.helper.Log("Saving player HomelessData");
|
||||
Main.helper.Log("Saving player HomelessData");
|
||||
__instance.HomelessData = new List<Guid>();
|
||||
for (int k = 0; k < p.Homeless.Count; k++)
|
||||
{
|
||||
__instance.HomelessData.Add(p.Homeless.data[k].guid);
|
||||
var homeless = p.Homeless.data[k];
|
||||
if (homeless != null)
|
||||
__instance.HomelessData.Add(homeless.guid);
|
||||
}
|
||||
__instance.structures = new List<Building.BuildingSaveData[]>();
|
||||
__instance.subStructures = new List<Building.BuildingSaveData[]>();
|
||||
@@ -1634,17 +1884,35 @@ namespace KCM
|
||||
Main.helper.Log("Saving player structures");
|
||||
World.inst.ForEachTile(0, 0, World.inst.GridWidth, World.inst.GridHeight, delegate (int x, int z, Cell cell)
|
||||
{
|
||||
bool flag4 = cell.OccupyingStructure.Count > 0;
|
||||
if (cell == null)
|
||||
return;
|
||||
|
||||
bool flag4 = cell.OccupyingStructure != null && cell.OccupyingStructure.Count > 0;
|
||||
if (flag4)
|
||||
{
|
||||
List<Building.BuildingSaveData> occupyingStructureData = new List<Building.BuildingSaveData>();
|
||||
for (int i3 = 0; i3 < cell.OccupyingStructure.Count; i3++)
|
||||
{
|
||||
var building = cell.OccupyingStructure[i3];
|
||||
bool flag5 = Vector3.Distance(cell.OccupyingStructure[i3].transform.position.xz(), cell.Position.xz()) <= 1E-05f;
|
||||
if (flag5 && building.TeamID() == p.PlayerLandmassOwner.teamId)
|
||||
if (building == null)
|
||||
continue;
|
||||
|
||||
var buildingTransform = building.transform;
|
||||
if (buildingTransform == null)
|
||||
continue;
|
||||
|
||||
bool flag5 = Vector3.Distance(buildingTransform.position.xz(), cell.Position.xz()) <= 1E-05f;
|
||||
if (flag5 && building.TeamID() == landmassOwner.teamId)
|
||||
{
|
||||
occupyingStructureData.Add(new Building.BuildingSaveData().Pack(cell.OccupyingStructure[i3]));
|
||||
try
|
||||
{
|
||||
occupyingStructureData.Add(new Building.BuildingSaveData().Pack(building));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.helper.Log("Error packing structure for save: " + (building.UniqueName ?? string.Empty));
|
||||
Main.helper.Log(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
bool flag6 = occupyingStructureData.Count > 0;
|
||||
@@ -1653,17 +1921,32 @@ namespace KCM
|
||||
__instance.structures.Add(occupyingStructureData.ToArray());
|
||||
}
|
||||
}
|
||||
bool flag7 = cell.SubStructure.Count > 0;
|
||||
bool flag7 = cell.SubStructure != null && cell.SubStructure.Count > 0;
|
||||
if (flag7)
|
||||
{
|
||||
List<Building.BuildingSaveData> subStructureData = new List<Building.BuildingSaveData>();
|
||||
for (int i4 = 0; i4 < cell.SubStructure.Count; i4++)
|
||||
{
|
||||
var building = cell.SubStructure[i4];
|
||||
bool flag8 = Vector3.Distance(cell.SubStructure[i4].transform.position.xz(), cell.Position.xz()) <= 1E-05f;
|
||||
if (flag8 && building.TeamID() == p.PlayerLandmassOwner.teamId)
|
||||
if (building == null)
|
||||
continue;
|
||||
|
||||
var buildingTransform = building.transform;
|
||||
if (buildingTransform == null)
|
||||
continue;
|
||||
|
||||
bool flag8 = Vector3.Distance(buildingTransform.position.xz(), cell.Position.xz()) <= 1E-05f;
|
||||
if (flag8 && building.TeamID() == landmassOwner.teamId)
|
||||
{
|
||||
subStructureData.Add(new Building.BuildingSaveData().Pack(cell.SubStructure[i4]));
|
||||
try
|
||||
{
|
||||
subStructureData.Add(new Building.BuildingSaveData().Pack(building));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.helper.Log("Error packing sub-structure for save: " + (building.UniqueName ?? string.Empty));
|
||||
Main.helper.Log(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
bool flag9 = subStructureData.Count > 0;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace KCM.Packets.Game
|
||||
|
||||
public override void HandlePacketServer()
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
// Server relay is handled automatically by PacketHandler unless [NoServerRelay] is used.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,14 @@ namespace KCM.Packets.Handlers
|
||||
{
|
||||
public class PacketHandler
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static bool isHandlingPacket;
|
||||
|
||||
public static bool IsHandlingPacket
|
||||
{
|
||||
get { return isHandlingPacket; }
|
||||
}
|
||||
|
||||
public static Dictionary<ushort, PacketRef> Packets = new Dictionary<ushort, PacketRef>();
|
||||
public class PacketRef
|
||||
{
|
||||
@@ -183,6 +191,7 @@ namespace KCM.Packets.Handlers
|
||||
{
|
||||
try
|
||||
{
|
||||
isHandlingPacket = true;
|
||||
packet.HandlePacketClient();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -205,6 +214,10 @@ namespace KCM.Packets.Handlers
|
||||
Main.helper.Log(ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
isHandlingPacket = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* if (PacketHandlers.TryGetValue(id, out PacketHandlerDelegate handler))
|
||||
|
||||
@@ -108,8 +108,23 @@ namespace KCM.Packets.Lobby
|
||||
|
||||
LoadSave.Load();
|
||||
|
||||
try
|
||||
{
|
||||
Main.SetMultiplayerSaveLoadInProgress(true);
|
||||
LoadSaveLoadHook.saveContainer.Unpack(null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Main.SetMultiplayerSaveLoadInProgress(false);
|
||||
}
|
||||
|
||||
LoadSaveLoadHook.saveContainer.Unpack(null);
|
||||
try
|
||||
{
|
||||
RunPostLoadRebuild("Save transfer complete");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent());
|
||||
|
||||
try
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace KCM.Packets.Lobby
|
||||
|
||||
try
|
||||
{
|
||||
int desiredSpeed = 1;
|
||||
if (!LobbyManager.loadingSave)
|
||||
{
|
||||
SpeedControlUI.inst.SetSpeed(0);
|
||||
@@ -39,12 +40,13 @@ namespace KCM.Packets.Lobby
|
||||
Main.helper.Log(ex.ToString());
|
||||
}
|
||||
|
||||
SpeedControlUI.inst.SetSpeed(0);
|
||||
SpeedControlUI.inst.SetSpeed(desiredSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
LobbyManager.loadingSave = false;
|
||||
GameState.inst.SetNewMode(GameState.inst.playingMode);
|
||||
SpeedControlUI.inst.SetSpeed(desiredSpeed);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -22,27 +22,72 @@ namespace KCM.StateManagement.BuildingState
|
||||
{
|
||||
try
|
||||
{
|
||||
Observer observer = (Observer)sender;
|
||||
Observer observer = sender as Observer;
|
||||
if (observer == null)
|
||||
return;
|
||||
|
||||
Building building = (Building)observer.state;
|
||||
Building building = observer.state as Building;
|
||||
if (building == null)
|
||||
return;
|
||||
|
||||
//Main.helper.Log("Should send building network update for: " + building.UniqueName);
|
||||
|
||||
var t = building.transform;
|
||||
if (t == null)
|
||||
return;
|
||||
|
||||
Quaternion rotation = t.rotation;
|
||||
Vector3 globalPosition = t.position;
|
||||
Vector3 localPosition = t.localPosition;
|
||||
|
||||
if (t.childCount > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var child = t.GetChild(0);
|
||||
if (child != null)
|
||||
{
|
||||
rotation = child.rotation;
|
||||
localPosition = child.localPosition;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
float resourceProgress = 0f;
|
||||
try
|
||||
{
|
||||
var field = building.GetType().GetField("resourceProgress", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (field != null)
|
||||
{
|
||||
object value = field.GetValue(building);
|
||||
if (value is float)
|
||||
resourceProgress = (float)value;
|
||||
else if (value != null)
|
||||
resourceProgress = Convert.ToSingle(value);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
new BuildingStatePacket()
|
||||
{
|
||||
customName = building.customName,
|
||||
guid = building.guid,
|
||||
uniqueName = building.UniqueName,
|
||||
rotation = building.transform.GetChild(0).rotation,
|
||||
globalPosition = building.transform.position,
|
||||
localPosition = building.transform.GetChild(0).localPosition,
|
||||
rotation = rotation,
|
||||
globalPosition = globalPosition,
|
||||
localPosition = localPosition,
|
||||
built = building.IsBuilt(),
|
||||
placed = building.IsPlaced(),
|
||||
open = building.Open,
|
||||
doBuildAnimation = building.doBuildAnimation,
|
||||
constructionPaused = building.constructionPaused,
|
||||
constructionProgress = building.constructionProgress,
|
||||
resourceProgress = (float)building.GetType().GetField("resourceProgress", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(building),
|
||||
resourceProgress = resourceProgress,
|
||||
life = building.Life,
|
||||
ModifiedMaxLife = building.ModifiedMaxLife,
|
||||
yearBuilt = building.YearBuilt,
|
||||
|
||||
@@ -128,6 +128,31 @@ namespace KCM.StateManagement.Observers
|
||||
if (this.state == null)
|
||||
return;
|
||||
|
||||
// Unity uses "fake null" for destroyed objects. Since our state is stored as object,
|
||||
// we must explicitly detect that case to avoid exceptions + log spam.
|
||||
try
|
||||
{
|
||||
UnityEngine.Object unityObj = this.state as UnityEngine.Object;
|
||||
if (this.state is UnityEngine.Object && unityObj == null)
|
||||
{
|
||||
try { StateObserver.observers.Remove(this.state.GetHashCode()); } catch { }
|
||||
try
|
||||
{
|
||||
if (observerObject != null)
|
||||
UnityEngine.Object.Destroy(observerObject);
|
||||
else
|
||||
UnityEngine.Object.Destroy(this.gameObject);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (!(currentMs - lastUpdate > updateInterval)) // Don't run if the update interval hasn't passed (default 100 milliseconds);
|
||||
return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user