Files
K-C-Multiplayer/Main.cs

2486 lines
102 KiB
C#

using Assets.Code;
using Assets.Code.UI;
using Assets.Interface;
using Harmony;
using KCM.Enums;
using KCM.LoadSaveOverrides;
using KCM.Packets.Game;
using KCM.Packets.Game.Dragon;
using KCM.Packets.Game.GameBuilding;
using KCM.Packets.Game.GamePlayer;
using KCM.Packets.Game.GameTrees;
using KCM.Packets.Game.GameVillager;
using KCM.Packets.Game.GameWeather;
using KCM.Packets.Game.GameWorld;
using KCM.Packets.Handlers;
using KCM.Packets.Lobby;
using KCM.StateManagement.BuildingState;
using KCM.StateManagement.Observers;
using KCM.UI;
using Newtonsoft.Json;
using Riptide;
using Riptide.Demos.Steam.PlayerHosted;
using Riptide.Transports.Steam;
using Riptide.Utils;
using Steamworks;
using System;
using System.Collections.Generic;
using System.Configuration.Assemblies;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.AccessControl;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using static ModCompiler;
using static World;
namespace KCM
{
public class Main : MonoBehaviour
{
public static KCModHelper helper;
public static MenuState menuState = (MenuState)MainMenuMode.State.Uninitialized;
public static Dictionary<string, KCPlayer> kCPlayers = new Dictionary<string, KCPlayer>();
public static Dictionary<ushort, string> clientSteamIds = new Dictionary<ushort, string>();
public static KCPlayer GetPlayerByClientID(ushort clientId)
{
return kCPlayers[clientSteamIds[clientId]];
}
public static Player GetPlayerByTeamID(int teamId) // Need to replace building / production types so that the correct player is used. IResourceStorage and IResourceProvider, and jobs
{
try
{
var player = kCPlayers.Values.FirstOrDefault(p => p.inst.PlayerLandmassOwner.teamId == teamId).inst;
return player;
}
catch (Exception e)
{
if (KCServer.IsRunning || KCClient.client.IsConnected)
{
Main.helper.Log("Failed finding player by teamID: " + teamId + " My teamID is: " + Player.inst.PlayerLandmassOwner.teamId);
Main.helper.Log(kCPlayers.Count.ToString());
Main.helper.Log(string.Join(", ", kCPlayers.Values.Select(p => p.inst.PlayerLandmassOwner.teamId.ToString())));
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
}
}
return Player.inst;
}
public static Player GetPlayerByBuilding(Building building)
{
try
{
var lmo = World.GetLandmassOwner(building.LandMass());
if (lmo == null) // Return the actual player for the client if the landmass owner is null
return Player.inst;
// Return the player by teamId so that the correct player instance is updated/used on the server
return GetPlayerByTeamID(building.TeamID());
}
catch (Exception e)
{
Main.helper.Log("Failed finding player by building: " + building.UniqueName);
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
}
return Player.inst;
}
public static string PlayerSteamID = SteamUser.GetSteamID().ToString();
public static KCMSteamManager KCMSteamManager = null;
public static LobbyManager lobbyManager = null;
public static SteamServer steamServer = new SteamServer();
public static Riptide.Transports.Steam.SteamClient steamClient = new Riptide.Transports.Steam.SteamClient(steamServer);
public static ushort currentClient = 0;
public static void CleanupMultiplayerSession()
{
if (helper == null) return; // Avoid running if mod is not fully initialized
helper.Log("--- Starting Multiplayer Session Cleanup ---");
// Disconnect client
if (KCClient.client != null && KCClient.client.IsConnected)
{
helper.Log("Disconnecting client...");
KCClient.client.Disconnect();
}
// Stop server
if (KCServer.server != null && KCServer.IsRunning)
{
helper.Log("Stopping server...");
KCServer.server.Stop();
}
// Clear player lists
if (kCPlayers.Count > 0 || clientSteamIds.Count > 0)
{
helper.Log($"Clearing {kCPlayers.Count} KCPlayer entries and {clientSteamIds.Count} client steam IDs.");
kCPlayers.Clear();
clientSteamIds.Clear();
}
// Destroy persistent managers
if (lobbyManager != null)
{
helper.Log("Destroying LobbyManager.");
Destroy(lobbyManager.gameObject);
lobbyManager = null;
}
helper.Log("--- Multiplayer Session Cleanup Finished ---");
}
#region "SceneLoaded"
private void SceneLoaded(KCModHelper helper)
{
helper.Log("SceneLoaded run in main");
RiptideLogger.Initialize(helper.Log, helper.Log, helper.Log, helper.Log, false);
helper.Log($"{SteamFriends.GetPersonaName()}");
KCMSteamManager = new GameObject("KCMSteamManager").AddComponent<KCMSteamManager>();
DontDestroyOnLoad(KCMSteamManager);
lobbyManager = new GameObject("LobbyManager").AddComponent<LobbyManager>();
DontDestroyOnLoad(lobbyManager);
//SteamFriends.InviteUserToGame(new CSteamID(76561198036307537), "test");
//SteamMatchmaking.lobby
//Main.helper.Log($"Timer duration for hazardpay {Player.inst.hazardPayWarmup.Duration}");
try
{
SteamFriends.SetRichPresence("status", "Playing Multiplayer");
PacketHandler.Initialise();
Main.helper.Log(JsonConvert.SerializeObject(World.inst.mapSizeDefs, Formatting.Indented));
KaC_Button serverBrowser = new KaC_Button(Constants.MainMenuUI_T.Find("TopLevelUICanvas/TopLevel/Body/ButtonContainer/New").parent)
{
Name = "Multiplayer",
Text = "Multiplayer",
FirstSibling = true,
OnClick = () =>
{
//Constants.MainMenuUI_T.Find("TopLevelUICanvas/TopLevel").gameObject.SetActive(false);
SfxSystem.PlayUiSelect();
//ServerBrowser.serverBrowserRef.SetActive(true);
TransitionTo(MenuState.ServerBrowser);
}
};
serverBrowser.Transform.SetSiblingIndex(2);
Destroy(Constants.MainMenuUI_T.Find("TopLevelUICanvas/TopLevel/Body/ButtonContainer/Kingdom Share").gameObject);
}
catch (Exception ex)
{
Main.helper.Log("----------------------- Main exception -----------------------");
Main.helper.Log(ex.ToString());
Main.helper.Log("----------------------- Main message -----------------------");
Main.helper.Log(ex.Message);
Main.helper.Log("----------------------- Main stacktrace -----------------------");
Main.helper.Log(ex.StackTrace);
if (ex.InnerException != null)
{
Main.helper.Log("----------------------- Inner exception -----------------------");
Main.helper.Log(ex.InnerException.ToString());
Main.helper.Log("----------------------- Inner message -----------------------");
Main.helper.Log(ex.InnerException.Message);
Main.helper.Log("----------------------- Inner stacktrace -----------------------");
Main.helper.Log(ex.InnerException.StackTrace);
}
}
}
#endregion
public static int FixedUpdateInterval = 0;
private void FixedUpdate()
{
// send batched building placement info
/*if (PlaceHook.QueuedBuildings.Count > 0 && (FixedUpdateInterval % 25 == 0))
{
foreach (Building building in PlaceHook.QueuedBuildings)
{
new WorldPlace()
{
uniqueName = building.UniqueName,
customName = building.customName,
guid = building.guid,
rotation = building.transform.GetChild(0).rotation,
globalPosition = building.transform.position,
localPosition = building.transform.GetChild(0).localPosition,
built = building.IsBuilt(),
placed = building.IsPlaced(),
open = building.Open,
doBuildAnimation = building.doBuildAnimation,
constructionPaused = building.constructionPaused,
constructionProgress = building.constructionProgress,
life = building.Life,
ModifiedMaxLife = building.ModifiedMaxLife,
//CollectForBuild = CollectForBuild,
yearBuilt = building.YearBuilt,
decayProtection = building.decayProtection,
seenByPlayer = building.seenByPlayer
}.Send();
}
PlaceHook.QueuedBuildings.Clear();
}*/
FixedUpdateInterval++;
}
#region "TransitionTo"
public static void TransitionTo(MenuState state)
{
try
{
ServerBrowser.serverBrowserRef.SetActive(state == MenuState.ServerBrowser);
ServerBrowser.serverLobbyRef.SetActive(state == MenuState.ServerLobby);
ServerBrowser.KCMUICanvas.gameObject.SetActive((int)state > 21);
helper.Log(((int)state > 21).ToString());
GameState.inst.mainMenuMode.TransitionTo((MainMenuMode.State)state);
}
catch (Exception ex)
{
Main.helper.Log("----------------------- Main exception -----------------------");
Main.helper.Log(ex.ToString());
Main.helper.Log("----------------------- Main message -----------------------");
Main.helper.Log(ex.Message);
Main.helper.Log("----------------------- Main stacktrace -----------------------");
Main.helper.Log(ex.StackTrace);
if (ex.InnerException != null)
{
Main.helper.Log("----------------------- Inner exception -----------------------");
Main.helper.Log(ex.InnerException.ToString());
Main.helper.Log("----------------------- Inner message -----------------------");
Main.helper.Log(ex.InnerException.Message);
Main.helper.Log("----------------------- Inner stacktrace -----------------------");
Main.helper.Log(ex.InnerException.StackTrace);
}
}
}
#endregion
private void Preload(KCModHelper helper)
{
helper.Log("Preload start in main");
try
{
//MainMenuPatches.Patch();
Main.helper = helper;
helper.Log(helper.modPath);
var harmony = HarmonyInstance.Create("harmony");
harmony.PatchAll(Assembly.GetExecutingAssembly());
helper.Log("Preload run in main");
}
catch (Exception ex)
{
Main.helper.Log("----------------------- Main exception -----------------------");
Main.helper.Log(ex.ToString());
Main.helper.Log("----------------------- Main message -----------------------");
Main.helper.Log(ex.Message);
Main.helper.Log("----------------------- Main stacktrace -----------------------");
Main.helper.Log(ex.StackTrace);
}
helper.Log("Preload end in main");
}
#region "MainMenu Hooks"
public static MenuState prevMenuState = MenuState.Uninitialized;
[HarmonyPatch(typeof(MainMenuMode))]
[HarmonyPatch("TransitionTo")]
public class TransitionToHook
{
private static void Prefix(MainMenuMode.State newState)
{
Main.helper.Log($"Menu set to: {(MenuState)newState}");
Main.prevMenuState = Main.menuState;
if (newState != MainMenuMode.State.Uninitialized)
Main.menuState = (MenuState)newState;
}
}
[HarmonyPatch(typeof(MainMenuMode))]
[HarmonyPatch("OnClickedClose")]
public class OnClickedCloseHook
{
private static bool Prefix()
{
helper.Log("Transition back");
TransitionTo(prevMenuState);
return false;
}
}
[HarmonyPatch(typeof(MainMenuMode))]
[HarmonyPatch("OnClickedBackToModeSelect")]
public class OnClickedBackToModeSelectPatch
{
private static bool Prefix()
{
if (KCClient.client.IsConnected)
{
Main.TransitionTo(MenuState.ServerLobby);
SfxSystem.PlayUiCancel();
return false;
}
else return true;
}
}
[HarmonyPatch(typeof(MainMenuMode))]
[HarmonyPatch("OnClickedAcceptNameBanner")]
public class OnClickedAcceptNameBannerPatch
{
private static bool Prefix()
{
if (KCClient.client.IsConnected)
{
Main.TransitionTo(MenuState.ServerLobby);
SfxSystem.PlayUiCancel();
return false;
}
else return true;
}
}
#endregion
#region "TownName Hooks"
[HarmonyPatch(typeof(TownNameUI))]
[HarmonyPatch("SetTownNameQuiet")]
public static class TownNameHook
{
//A function to run after target function invocation
private static void Postfix(TownNameUI __instance)
{
helper.Log($"name set: {__instance.townName}");
new KingdomName() { kingdomName = __instance.townName }.Send();
}
}
#endregion
#region "ChooseBanner Hooks"
[HarmonyPatch(typeof(ChooseBannerUI))]
[HarmonyPatch("OnAccept")]
public class ChooseBannerUIOnAcceptHook
{
private static void Postfix()
{
if (KCClient.client.IsConnected)
{
var banner = Player.inst.PlayerLandmassOwner.bannerIdx;
new PlayerBanner() { banner = banner }.Send();
//return true;
}
//else return true;
}
}
#endregion
[HarmonyPatch(typeof(Keep))]
[HarmonyPatch("OnPlayerPlacement")]
public class KeepHook
{
public static void Postfix()
{
// Your code here
// Get the name of the last method that called OnPlayerPlacement
List<string> strings = new List<string>();
for (int i = 1; i < 10; i++)
{
try
{
string callingMethodName = new StackFrame(i).GetMethod().Name;
strings.Add(callingMethodName);
}
catch
{
strings.Add("Start");
break;
}
}
strings.Reverse();
Main.helper.Log($"Last {strings.Count} methods in call tree: {string.Join(" -> ", strings)}");
}
}
#region "GameUI Hooks"
//GameUI hook for acceptcursorobjplacement
/*[HarmonyPatch(typeof(GameUI), "AcceptCursorObjPlacement")]
public class AcceptCursorObjPlacementHook
{
}*/
#endregion
#region "World Hooks"
[HarmonyPatch(typeof(World))]
[HarmonyPatch("Place")]
public class PlaceHook
{
/*public static bool Prefix()
{
if (KCClient.client.IsConnected && !KCServer.IsRunning)
{
if (!new StackFrame(3).GetMethod().kingdomName.Contains("HandlePacket"))
return false;
}
return true;
}*/
public static void Postfix(Building PendingObj)
{
try
{
if (KCClient.client.IsConnected)
{
/*string callTree = "";
List<string> strings = new List<string>();
for (int i = 1; i < 10; i++)
{
try
{
string callingMethodName = new StackFrame(i).GetMethod().Name;
strings.Add($"{callingMethodName} ({i})");
}
catch
{
strings.Add("Start");
break;
}
}
strings.Reverse();
Main.helper.Log($"WORLDPLACE Last {strings.Count} methods in call tree: {string.Join(" -> ", strings)}");*/
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket") && !new StackFrame(2).GetMethod().Name.Equals("RandomPlacement"))
return;
Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().Name}");
Main.helper.Log($"{KCClient.client.Id} {Main.kCPlayers[PlayerSteamID].name} - Sending building place packet for " + PendingObj.UniqueName);
// Need to batch building placements to prevent network spam
new WorldPlace()
{
uniqueName = PendingObj.UniqueName,
customName = PendingObj.customName,
guid = PendingObj.guid,
rotation = PendingObj.transform.GetChild(0).rotation,
globalPosition = PendingObj.transform.position,
localPosition = PendingObj.transform.GetChild(0).localPosition,
built = PendingObj.IsBuilt(),
placed = PendingObj.IsPlaced(),
open = PendingObj.Open,
doBuildAnimation = PendingObj.doBuildAnimation,
constructionPaused = PendingObj.constructionPaused,
constructionProgress = PendingObj.constructionProgress,
life = PendingObj.Life,
ModifiedMaxLife = PendingObj.ModifiedMaxLife,
//CollectForBuild = CollectForBuild,
yearBuilt = PendingObj.YearBuilt,
decayProtection = PendingObj.decayProtection,
seenByPlayer = PendingObj.seenByPlayer
}.Send();
//return true;
}
//else return true;
}
catch (Exception e)
{
Main.helper.Log("World Place error");
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
}
}
}
[HarmonyPatch(typeof(World), "RelationBetween")]
public class WorldRelationBetweenHook
{
public static void Prefix(ref int teamIDA, ref int teamIDB)
{
//Main.helper.Log($"RelationBetween {teamIDA} and {teamIDB}");
if (KCClient.client.IsConnected)
{
if (teamIDA == 0 || teamIDB == 0)
{
if (teamIDA == 0)
teamIDA = Player.inst.PlayerLandmassOwner.teamId;
if (teamIDB == 0)
teamIDB = Player.inst.PlayerLandmassOwner.teamId;
}
}
}
}
#endregion
#region "Player Hooks"
[HarmonyPatch(typeof(Player), "Reset")]
public class PlayerResetHook
{
public static bool Prefix(Player __instance)
{
if (KCClient.client.IsConnected && __instance.gameObject.name.Contains("Client Player") && !LobbyManager.loadingSave)
{
try
{
var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
__instance.GetType().GetField("resetting", bindingFlags).SetValue(__instance, true);
//__instance.resetting = true;
__instance.GetType().GetField("poorHealthGracePeriod", bindingFlags).SetValue(__instance, 0f);
//__instance.poorHealthGracePeriod = 0f;
__instance.PlayerLandmassOwner.Gold = 0;
__instance.CurrYear = 0;
__instance.buildingDamageAnimator.Reset();
__instance.fruitSystem.Reset();
__instance.fieldSystem.Reset();
bool flag = __instance.DamagedList != null;
if (flag)
{
for (int i = 0; i < __instance.DamagedList.Length; i++)
{
__instance.DamagedList[i].Clear();
}
__instance.DamagedList = null;
}
__instance.irrigation.Reset();
__instance.ClearRegistry();
bool flag2 = __instance.buildingContainer;
if (flag2)
{
Building[] buildings = __instance.buildingContainer.transform.GetComponentsInChildren<Building>();
for (int j = 0; j < buildings.Length; j++)
{
buildings[j].destroyedWhileInPlay = false;
UnityEngine.Object.Destroy(buildings[j].gameObject);
}
}
UnityEngine.Object.Destroy(__instance.buildingContainer);
__instance.buildingContainer = new GameObject();
__instance.buildingContainer.name = "Buildings";
for (int k = 0; k < __instance.Workers.Count; k++)
{
__instance.Workers.data[k].Shutdown();
}
__instance.Workers.Clear();
/*int r = 0;
for (int l = 0; l < __instance.Homeless.Count; l++)
{
bool flag3 = !__instance.Homeless.data[l].shutdown;
if (flag3)
{
r++;
}
}*/
__instance.Homeless.Clear();
__instance.Residentials.Clear();
__instance.Buildings.Clear();
__instance.RadiusBonuses.Clear();
__instance.WagePayers.Clear();
__instance.timeAtFailHappiness = 0f;
__instance.MaxGoldStorage = 0;
__instance.KingdomHappiness = 100;
ReflectionHelper.ClearPrivateListField<Player.HappinessInfo>(__instance, "landMassHappiness");
//__instance.landMassHappiness.Clear();
ReflectionHelper.ClearPrivateListField<Player.HealthInfo>(__instance, "landMassHealth");
//__instance.landMassHealth.Clear();
ReflectionHelper.ClearPrivateListField<Player.IntegrityInfo>(__instance, "landMassIntegrity");
//__instance.landMassIntegrity.Clear();
//__instance.HealthTimer.ForceExpire(); // TO-DO implement timer
__instance.happinessMods.Clear();
/*for (int m = 0; m < __instance.plagueDeathInfo.Count; m++)
{
__instance.plagueDeathInfo[m].deathQueue.Clear();
__instance.plagueDeathInfo[m].deaths = 0;
__instance.plagueDeathInfo[m].deathTime = 0f;
}*/
ReflectionHelper.ClearPrivateListField<Villager>(__instance, "OldAgeDeathQueue");
//__instance.OldAgeDeathQueue.Clear();
__instance.GetType().GetField("deathsThisYear", bindingFlags).SetValue(__instance, 0);
//__instance.deathsThisYear = 0;
__instance.ResetPerLandMassData();
__instance.ResetTaxRates();
__instance.ResetCreativeModeOptions();
__instance.PlayerLandmassOwner.ReleaseOwnership();
ReflectionHelper.ClearPrivateListField<Player.DockOpening>(__instance, "dockOpenings");
//__instance.dockOpenings.Clear();
__instance.GetType().GetField("resetting", bindingFlags).SetValue(__instance, false);
//__instance.resetting = false;
}
catch (Exception e)
{
Main.helper.Log("Error in reset player hook");
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
}
return false;
}
return true;
}
}
[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();
if (flag)
{
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;
}
}
catch (Exception e)
{
Main.helper.Log("Error in add building hook");
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
}
return true;
}
}
[HarmonyPatch(typeof(Player), "SetupInitialWorkers")]
public class PlayerSetupInitialWorkersHook
{
public static void Postfix(Keep keep)
{
if (KCClient.client.IsConnected)
{
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new SetupInitialWorkersPacket()
{
keepGuid = keep.gameObject.GetComponent<Building>().guid
}.Send();
}
}
}
[HarmonyPatch(typeof(VillagerSystem), "AddVillager")]
public class PlayerAddVillagerHook
{
public static void Postfix(Villager __result, Vector3 pos)
{
if (KCClient.client.IsConnected)
{
try
{
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
if (Enumerable.Range(0, 4).Select(i => new StackFrame(i).GetMethod()?.Name).Any(name => name?.Contains("unpack") == true)) // If called by unpack in the tree, do not run, since clients already unpacked villager data
return;
new AddVillagerPacket()
{
guid = __result.guid,
}.Send();
}
catch (Exception e)
{
Main.helper.Log("Error in add villager hook");
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
}
}
}
}
#endregion
#region "Tree Hooks"
[HarmonyPatch(typeof(TreeSystem), "FellTree")]
public class TreeSystemFellTreeHook
{
/*static IEnumerable<MethodBase> TargetMethods()
{
var tmeth = typeof(TreeSystem).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static);
return tmeth.Cast<MethodBase>();
}*/
public static void Postfix(MethodBase __originalMethod, Cell cell, int idx)
{
if (KCClient.client.IsConnected)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new FellTree()
{
idx = idx,
x = cell.x,
z = cell.z
}.Send();
}
}
}
[HarmonyPatch(typeof(TreeSystem), "ShakeTree")]
public class TreeSystemShakeTreeHook
{
public static void Postfix(MethodBase __originalMethod, int idx)
{
if (KCClient.client.IsConnected)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new ShakeTree()
{
idx = idx
}.Send();
}
}
}
[HarmonyPatch(typeof(TreeSystem), "GrowTree")]
public class TreeSystemGrowTreeHook
{
/*public static bool Prefix()
{
if (KCClient.client.IsConnected && !KCServer.IsRunning)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (!new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return false;
}
return true;
}*/
// Only server should send this information
public static void Postfix(MethodBase __originalMethod, Cell cell)
{
if (KCServer.IsRunning)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new GrowTree()
{
X = cell.x,
Z = cell.z
}.SendToAll(KCClient.client.Id);
}
}
}
#endregion
#region "Weather Hooks"
[HarmonyPatch(typeof(Weather), "ChangeWeather")]
public class WeatherChangeWeatherHook
{
public static void Postfix(MethodBase __originalMethod, Weather.WeatherType type)
{
if (KCServer.IsRunning && KCClient.client.IsConnected)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
if (type != Weather.inst.currentWeather)
new ChangeWeather()
{
weatherType = (int)type
}.Send();
}
}
}
#endregion
#region "Building Hooks"
[HarmonyPatch(typeof(Building), "CompleteBuild")]
public class BuildingCompleteBuildHook
{
public static bool Prefix(MethodBase __originalMethod, Building __instance)
{
if (KCClient.client.IsConnected)
{
Main.helper.Log("Overridden complete build");
Player player = Main.GetPlayerByTeamID(__instance.TeamID());
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
typeof(Building).GetField("built", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, true);
__instance.UpdateMaterialSelection();
__instance.SendMessage("OnBuilt", SendMessageOptions.DontRequireReceiver);
typeof(Building).GetField("yearBuilt", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, player.CurrYear);
typeof(Building).GetMethod("AddAllResourceProviders", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null);
player.BuildingNowBuilt(__instance);
typeof(Building).GetMethod("TryAddJobs", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null);
__instance.BakePathing();
return false;
}
return true;
}
}
[HarmonyPatch(typeof(Building), "UpdateConstruction")]
public class BuildingUpdateHook
{
public static void Prefix(Building __instance)
{
try
{
if (KCClient.client.IsConnected)
{
if (__instance.TeamID() == Player.inst.PlayerLandmassOwner.teamId)
StateObserver.RegisterObserver(__instance, new string[] {
"customName", "guid", "UniqueName", "built", "placed", "open", "doBuildAnimation", "constructionPaused", "constructionProgress", "resourceProgress",
"Life", "ModifiedMaxLife", "CollectForBuild", "yearBuilt", "decayProtection", "seenByPlayer",
}, BuildingStateManager.BuildingStateChanged, BuildingStateManager.SendBuildingUpdate);
//StateObserver.Update(__instance);
}
}
catch (Exception e)
{
helper.Log(e.ToString());
helper.Log(e.Message);
helper.Log(e.StackTrace);
}
}
}
#endregion
#region "Time Hooks"
// TimeManager TrySetSpeed hook
[HarmonyPatch(typeof(SpeedControlUI), "SetSpeed")]
public class SpeedControlUISetSpeedHook
{
private static long lastTime = 0;
public static bool Prefix()
{
if (KCClient.client.IsConnected)
{
if ((DateTimeOffset.Now.ToUnixTimeMilliseconds() - lastTime) < 250) // Set speed spam fix / hack
return false;
if (!new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return false;
}
return true;
}
public static void Postfix(int idx, bool skipNextSfx)
{
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"))
return;
new SetSpeed()
{
speed = idx
}.Send();
lastTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
}
}
}
#endregion
#region "SteamManager Hook"
[HarmonyPatch]
public class SteamManagerAwakeHook
{
static IEnumerable<MethodBase> TargetMethods()
{
var meth = typeof(SteamManager).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
return meth.Cast<MethodBase>();
}
public static bool Prefix(MethodBase __originalMethod)
{
return false;
}
}
#endregion
#region "Dragon Hooks"
#region "Dragon Spawn Hooks"
[HarmonyPatch(typeof(DragonSpawn), "SpawnSiegeDragon")]
public class DragonSpawnSpawnSiegeDragonHook
{
public static bool Prefix()
{
if (KCClient.client.IsConnected && !KCServer.IsRunning && !new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return false;
return true;
}
public static void Postfix(MethodBase __originalMethod, Vector3 start)
{
if (KCClient.client.IsConnected)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new SpawnSiegeDragonPacket() { start = start }.Send();
}
}
}
[HarmonyPatch(typeof(DragonSpawn), "SpawnMamaDragon", new Type[] { typeof(Vector3) })]
public class DragonSpawnSpawnMamaDragonHook
{
public static bool Prefix()
{
if (KCClient.client.IsConnected && !KCServer.IsRunning && !new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return false;
return true;
}
public static void Postfix(MethodBase __originalMethod, Vector3 start)
{
if (KCClient.client.IsConnected)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new SpawnMamaDragonPacket() { start = start }.Send();
}
}
}
[HarmonyPatch(typeof(DragonSpawn), "SpawnBabyDragon", new Type[] { typeof(Vector3) })]
public class DragonSpawnSpawnBabyDragonHook
{
public static bool Prefix()
{
if (KCClient.client.IsConnected && !KCServer.IsRunning && !new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return false;
return true;
}
public static void Postfix(MethodBase __originalMethod, Vector3 start)
{
if (KCClient.client.IsConnected)
{
//Main.helper.Log($"Called by: {new StackFrame(3).GetMethod().kingdomName}");
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new SpawnBabyDragonPacket() { start = start }.Send();
}
}
}
#endregion
#endregion
#region "Villager Hooks"
[HarmonyPatch(typeof(Villager), "TeleportTo")]
public class VillagerTeleportToHook
{
public static void Postfix(Villager __instance, Vector3 newPos)
{
if (KCClient.client.IsConnected)
{
if (new StackFrame(3).GetMethod().Name.Contains("HandlePacket"))
return;
new VillagerTeleportTo()
{
guid = __instance.guid,
pos = newPos
}.Send();
}
}
}
#endregion
#region "Job Hooks"
/*[HarmonyPatch(typeof(Job), "OnEmployeeQuit")]
public class JobOnEmployeeQuitHook
{
public static Player oldPlayer;
public static void Prefix(Job __instance)
{
if (KCClient.client.IsConnected)
{
oldPlayer = Player.inst;
Player.inst = Main.GetPlayerByTeamID(World.GetLandmassOwner(__instance.employer.LandMass()).teamId);
}
}
public static void Postfix(Job __instance)
{
if (KCClient.client.IsConnected)
{
Player.inst = oldPlayer;
}
}
}*/
#endregion
#region "LoadSave Hooks"
[HarmonyPatch(typeof(LoadSave), "GetSaveDir")]
public class LoadSaveGetSaveDirHook
{
public static bool Prefix(ref string __result)
{
Main.helper.Log("Get save dir");
if (KCClient.client.IsConnected)
{
if (KCServer.IsRunning)
{
}
__result = Application.persistentDataPath + "/Saves/Multiplayer";
return false;
}
__result = Application.persistentDataPath + "/Saves"; ;
return true;
}
}
[HarmonyPatch(typeof(LoadSave), "LoadAtPath")]
public class LoadSaveLoadAtPathHook
{
//public static string saveFile = "";
public static byte[] saveData = new byte[0];
public static bool Prefix(string path, string filename, bool visitedWorld)
{
if (KCServer.IsRunning)
{
Main.helper.Log("Trying to load multiplayer save");
LoadSave.LastLoadDirectory = path;
path = path + "/" + filename;
bool flag = !File.Exists(path);
if (!flag)
{
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new MultiplayerSaveDeserializationBinder();
saveData = File.ReadAllBytes(path);
Stream file = new FileStream(path, FileMode.Open);
try
{
MultiplayerSaveContainer loadData = (MultiplayerSaveContainer)bf.Deserialize(file);
loadData.Unpack(null);
Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent());
}
catch (Exception e)
{
GameState.inst.mainMenuMode.TransitionTo(MainMenuMode.State.LoadError);
Main.helper.Log("Error loading save");
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
throw;
}
finally
{
bool flag2 = file != null;
if (flag2)
{
file.Close();
file.Dispose();
}
}
}
return false;
}
return true;
}
}
[HarmonyPatch(typeof(LoadSave), "Load")]
public class LoadSaveLoadHook
{
public static bool memoryStreamHook = false;
public static byte[] saveBytes = new byte[0];
public static MultiplayerSaveContainer saveContainer;
public static bool Prefix()
{
if (memoryStreamHook)
{
Main.helper.Log("Attempting to load save from server");
using (MemoryStream ms = new MemoryStream(saveBytes))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new MultiplayerSaveDeserializationBinder();
saveContainer = (MultiplayerSaveContainer)bf.Deserialize(ms);
}
memoryStreamHook = false;
return false;
}
return true;
}
}
[HarmonyPatch(typeof(LoadSave), "Save")]
public class LoadSaveSaveHook
{
private class OutData
{
// Token: 0x04002176 RID: 8566
public string Path;
// Token: 0x04002177 RID: 8567
public MultiplayerSaveContainer LoadSaveContainer;
}
private static void OutToFile(object data)
{
OutData outData = (OutData)data;
BinaryFormatter bf = new BinaryFormatter();
Stream file = null;
try
{
file = new FileStream(outData.Path, FileMode.Create, FileAccess.Write);
bf.Serialize(file, outData.LoadSaveContainer);
}
catch (Exception e)
{
LoadSave.AppendToLocalErrorLog(string.Concat(new string[]
{
"Problem during save",
Environment.NewLine,
e.Message,
Environment.NewLine,
e.StackTrace
}));
}
finally
{
bool flag = file != null;
if (flag)
{
file.Close();
file.Dispose();
}
}
}
public static bool Prefix(string pathOverride, UnityAction onCompleteCallback, ref Thread __result)
{
if (KCServer.IsRunning)
{
Directory.CreateDirectory(LoadSave.GetSaveDir());
Guid guid = Guid.NewGuid();
string path = (pathOverride != "") ? pathOverride : (LoadSave.GetSaveDir() + "/" + guid);
Directory.CreateDirectory(path);
Thread thread;
try
{
thread = new Thread(new ParameterizedThreadStart(OutToFile));
MultiplayerSaveContainer packedData = new MultiplayerSaveContainer().Pack(null);
Broadcast.OnSaveEvent.Broadcast(new OnSaveEvent());
thread.Start(new OutData
{
LoadSaveContainer = packedData,
Path = path + "/world"
});
}
catch (Exception e)
{
//LoadSave.ErrorToKingdomLog(e);
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
throw;
}
finally
{
LoadSave.SaveWorldSummaryData(path);
}
// Custom banners not implemented yet
/*try
{
bool usingCustomBanner = Player.inst.usingCustomBanner;
if (usingCustomBanner)
{
File.WriteAllBytes(path + "/custombanner.png", Player.inst.customBannerTexture2D.EncodeToPNG());
}
}
catch (Exception e2)
{
Main.helper.Log(e2.Message);
}*/
try
{
World.inst.TakeScreenshot(path + "/cover", new Func<int, int, Texture2D>(World.inst.Func_CaptureWorldShot), onCompleteCallback);
}
catch (Exception e3)
{
Main.helper.Log(e3.Message);
}
bool flag = onCompleteCallback != null;
if (flag)
{
bool flag2 = thread != null && thread.ThreadState == System.Threading.ThreadState.Running;
if (flag2)
{
thread.Join();
}
}
GC.Collect();
__result = thread;
return false;
}
return true;
}
}
[HarmonyPatch(typeof(SaveLoadUI), "ClickLoadItem")]
public class SaveLoadUIClickedLoadItemHook
{
public static bool Prefix(SaveLoadUI __instance, string id)
{
if (KCServer.IsRunning)
{
LoadSave.Load(id);
TransitionTo(MenuState.ServerLobby);
//GameState.inst.SetNewMode(GameState.inst.playingMode);
return false;
}
return true;
}
}
[HarmonyPatch(typeof(Player.PlayerSaveData), "ProcessBuilding")]
public class PlayerProcessBuildingHook
{
public static bool Prefix(Building.BuildingSaveData structureData, Player p, ref Building __result)
{
if (KCClient.client.IsConnected)
{
Building Building = GameState.inst.GetPlaceableByUniqueName(structureData.uniqueName);
bool flag = Building;
if (flag)
{
Building building = UnityEngine.Object.Instantiate<Building>(Building);
building.transform.position = structureData.globalPosition;
building.Init();
building.transform.SetParent(p.buildingContainer.transform, true);
structureData.Unpack(building);
p.AddBuilding(building);
Main.helper.Log($"Loading player id: {p.PlayerLandmassOwner.teamId}");
Main.helper.Log($"loading building: {building.FriendlyName}");
Main.helper.Log($" (teamid: {building.TeamID()})");
Main.helper.Log(p.ToString());
bool flag2 = building.GetComponent<Keep>() != null && building.TeamID() == p.PlayerLandmassOwner.teamId;
Main.helper.Log("Set keep? " + flag2);
if (flag2)
{
p.keep = building.GetComponent<Keep>();
Main.helper.Log(p.keep.ToString());
}
__result = building;
}
else
{
Main.helper.Log(structureData.uniqueName + " failed to load correctly");
__result = null;
}
return false;
}
return true;
}
}
[HarmonyPatch(typeof(Player.PlayerSaveData), "Pack")]
public class PlayerSaveDataPackgHook
{
public static bool Prefix(Player.PlayerSaveData __instance, Player p, ref Player.PlayerSaveData __result)
{
if (KCClient.client.IsConnected)
{
var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
Main.helper.Log("Running patched player pack method");
Main.helper.Log("Saving banner system");
__instance.newBannerSystem = true;
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<Player.UpgradeType>());
Main.helper.Log("Saving player bannerIdx");
__instance.bannerIdx = p.PlayerLandmassOwner.bannerIdx;
Main.helper.Log("Saving player WorkersArray");
__instance.WorkersArray = new Villager.VillagerSaveData[p.Workers.Count];
for (int j = 0; j < p.Workers.Count; j++)
{
bool flag2 = p.Workers.data[j] != null;
if (flag2)
{
__instance.WorkersArray[j] = new Villager.VillagerSaveData().Pack(p.Workers.data[j]);
}
}
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);
}
__instance.structures = new List<Building.BuildingSaveData[]>();
__instance.subStructures = new List<Building.BuildingSaveData[]>();
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 (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)
{
occupyingStructureData.Add(new Building.BuildingSaveData().Pack(cell.OccupyingStructure[i3]));
}
}
bool flag6 = occupyingStructureData.Count > 0;
if (flag6)
{
__instance.structures.Add(occupyingStructureData.ToArray());
}
}
bool flag7 = 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)
{
subStructureData.Add(new Building.BuildingSaveData().Pack(cell.SubStructure[i4]));
}
}
bool flag9 = subStructureData.Count > 0;
if (flag9)
{
__instance.subStructures.Add(subStructureData.ToArray());
}
}
});
Main.helper.Log($"Saving town happiness");
__instance.TownHappiness = p.KingdomHappiness;
Main.helper.Log($"Saving town happiness infos");
__instance.happinessInfos = p.GetType().GetField("landMassHappiness", bindingFlags).GetValue(p) as List<Player.HappinessInfo>;
Main.helper.Log($"Saving town integrity infos");
__instance.integrityInfos = p.GetType().GetField("landMassIntegrity", bindingFlags).GetValue(p) as List<Player.IntegrityInfo>;
Main.helper.Log($"Saving town landmass owner");
__instance.playerLandmassOwnerSaveData = new LandmassOwner.LandmassOwnerSaveData().Pack(p.PlayerLandmassOwner);
Main.helper.Log($"Saving town bDidFirstFire");
__instance.bDidFirstFire = (bool)p.GetType().GetField("bDidFirstFire", bindingFlags).GetValue(p);
bool flag3 = p.taxRates != null;
if (flag3)
{
Main.helper.Log($"Saving town tax rates");
__instance.TaxRates = new float[p.taxRates.Length];
Array.Copy(p.taxRates, __instance.TaxRates, p.taxRates.Length);
}
Main.helper.Log($"Saving difficulty");
__instance.Difficulty = p.difficulty;
Main.helper.Log($"Saving CurrYear");
__instance.CurrYear = p.CurrYear;
Main.helper.Log($"Saving timeAtFailHappiness");
__instance.timeAtFailHappiness = p.timeAtFailHappiness;
Main.helper.Log($"Saving happinessMods");
__instance.happinessMods = p.happinessMods;
Main.helper.Log($"Saving currConsumption");
__instance.currConsumptionList = p.currConsumption;
Main.helper.Log($"Saving lastConsumption");
__instance.lastConsumptionList = p.lastConsumption;
Main.helper.Log($"Saving currProduction");
__instance.currProductionList = p.currProduction;
Main.helper.Log($"Saving lastProduction");
__instance.lastProductionList = p.lastProduction;
Main.helper.Log($"Saving landMassNames");
__instance.landMassNames = new List<string>();
for (int l = 0; l < p.LandMassNames.Count; l++)
{
__instance.landMassNames.Add(p.LandMassNames[l]);
}
Main.helper.Log($"Saving JobPriorityOrder");
__instance.JobPriorityOrder = new int[p.JobPriorityOrder.Length][];
__instance.JobEnabledFlag = new bool[p.JobEnabledFlag.Length][];
for (int m = 0; m < p.JobPriorityOrder.Length; m++)
{
__instance.JobPriorityOrder[m] = new int[p.JobPriorityOrder[m].Length];
__instance.JobEnabledFlag[m] = new bool[p.JobEnabledFlag[m].Length];
Array.Copy(p.JobPriorityOrder[m], __instance.JobPriorityOrder[m], __instance.JobPriorityOrder[m].Length);
Array.Copy(p.JobEnabledFlag[m], __instance.JobEnabledFlag[m], __instance.JobEnabledFlag[m].Length);
}
Main.helper.Log($"Saving JobFilledAvailable");
__instance.JobFilledAvailable = new int[World.inst.NumLandMasses][];
__instance.JobCustomMaxEnabledFlag = new bool[World.inst.NumLandMasses][];
for (int lm = 0; lm < World.inst.NumLandMasses; lm++)
{
int numJobTypes = p.JobFilledAvailable.data[lm].GetLength(0);
__instance.JobFilledAvailable[lm] = new int[numJobTypes];
__instance.JobCustomMaxEnabledFlag[lm] = new bool[numJobTypes];
for (int n = 0; n < numJobTypes; n++)
{
__instance.JobFilledAvailable[lm][n] = p.JobFilledAvailable.data[lm][n, 1];
}
Array.Copy(p.JobCustomMaxEnabledFlag[lm], __instance.JobCustomMaxEnabledFlag[lm], __instance.JobCustomMaxEnabledFlag[lm].Length);
}
Main.helper.Log($"Saving CanUseTools");
__instance.CanUseTools = new bool[p.CanUseTools.Length][];
for (int i2 = 0; i2 < p.CanUseTools.Length; i2++)
{
__instance.CanUseTools[i2] = new bool[p.CanUseTools[i2].Length];
Array.Copy(p.CanUseTools[i2], __instance.CanUseTools[i2], __instance.CanUseTools[i2].Length);
}
Main.helper.Log($"Saving usedCheats");
__instance.usedCheats = p.hasUsedCheats;
Main.helper.Log($"Saving nameForOldAgeDeath");
__instance.nameForOldAgeDeath = (string)p.GetType().GetField("nameForOldAgeDeath", bindingFlags).GetValue(p);
Main.helper.Log($"Saving deathsThisYear");
__instance.deathsThisYear = (int)p.GetType().GetField("deathsThisYear", bindingFlags).GetValue(p);
Main.helper.Log($"Saving poorHealthGracePeriod");
__instance.poorHealthGracePeriod = (float)p.GetType().GetField("poorHealthGracePeriod", bindingFlags).GetValue(p);
Main.helper.Log($"Saving dockOpenings");
__instance.dockOpenings = p.GetType().GetField("dockOpenings", bindingFlags).GetValue(p) as List<Player.DockOpening>;
Main.helper.Log($"Saving tourism");
__instance.tourism = p.tourism;
__result = __instance;
return false;
}
return true;
}
}
/*[HarmonyPatch(typeof(Player.PlayerSaveData), "Unpack")]
public class PlayerSaveDataUnpackHook
{
public static bool Prefix(Player.PlayerSaveData __instance, Player p, ref Player __result)
{
Main.helper.Log("Running patched player unpack method");
if (KCClient.client.IsConnected)
{
Main.helper.Log("Running patched unpack method");
var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
Main.helper.Log("1");
Weather.inst.weatherTimeScale = 0f;
p.creativeMode = __instance.creativeMode;
p.ResetPerLandMassData();
Main.helper.Log("2");
bool flag = __instance.JobPriorityOrder != null && __instance.JobPriorityOrder[0].Length == ((int[])p.GetType().GetField("defaultPriorityOrder", bindingFlags).GetValue(p)).Length;
if (flag)
{
Main.helper.Log(__instance.JobPriorityOrder.Length.ToString());
Main.helper.Log(__instance.JobEnabledFlag.Length.ToString());
Main.helper.Log(p.JobEnabledFlag.Length.ToString());
for (int i = 0; i < __instance.JobPriorityOrder.Length; i++)
{
Array.Copy(__instance.JobPriorityOrder[i], p.JobPriorityOrder[i], __instance.JobPriorityOrder[i].Length);
Main.helper.Log("2.1");
Array.Copy(__instance.JobEnabledFlag[i], p.JobEnabledFlag[i], __instance.JobPriorityOrder[i].Length);
Main.helper.Log("2.2");
}
}
Main.helper.Log("3");
bool flag2 = __instance.JobFilledAvailable != null && __instance.JobFilledAvailable.Length == p.JobFilledAvailable.data.Length;
if (flag2)
{
for (int lm = 0; lm < World.inst.NumLandMasses; lm++)
{
bool flag3 = __instance.JobFilledAvailable[lm].Length != p.JobFilledAvailable.data[lm].Length / 2;
if (flag3)
{
break;
}
for (int j = 0; j < 38; j++)
{
p.JobFilledAvailable.data[lm][j, 0] = 0;
p.JobFilledAvailable.data[lm][j, 1] = __instance.JobFilledAvailable[lm][j];
}
Array.Copy(__instance.JobCustomMaxEnabledFlag[lm], p.JobCustomMaxEnabledFlag[lm], __instance.JobCustomMaxEnabledFlag[lm].Length);
}
}
Main.helper.Log("4");
// not saving creative info
p.ResetCreativeModeOptions();
p.KingdomHappiness = __instance.TownHappiness;
Main.helper.Log("5");
p.GetType().GetField("landMassHappiness", bindingFlags).SetValue(p, __instance.happinessInfos);
var landMassHappiness = p.GetType().GetField("landMassHappiness", bindingFlags).GetValue(p) as List<Player.HappinessInfo>;
bool flag5 = landMassHappiness == null;
if (flag5)
{
landMassHappiness = new List<Player.HappinessInfo>();
}
while (landMassHappiness.Count < World.inst.NumLandMasses)
{
landMassHappiness.Add(new Player.HappinessInfo());
}
Main.helper.Log("6");
p.GetType().GetField("landMassHealth", bindingFlags).SetValue(p, __instance.healthInfos);
var landMassHealth = p.GetType().GetField("landMassHealth", bindingFlags).GetValue(p) as List<Player.HealthInfo>;
bool flag6 = landMassHealth == null;
if (flag6)
{
landMassHealth = new List<Player.HealthInfo>();
}
while (landMassHealth.Count < World.inst.NumLandMasses)
{
landMassHealth.Add(new Player.HealthInfo());
}
Main.helper.Log("7");
p.GetType().GetField("landMassIntegrity", bindingFlags).SetValue(p, __instance.integrityInfos);
var landMassIntegrity = p.GetType().GetField("landMassIntegrity", bindingFlags).GetValue(p) as List<Player.IntegrityInfo>;
bool flag7 = landMassIntegrity == null;
if (flag7)
{
landMassIntegrity = new List<Player.IntegrityInfo>();
}
while (landMassIntegrity.Count < World.inst.NumLandMasses)
{
landMassIntegrity.Add(new Player.IntegrityInfo());
}
Main.helper.Log("8");
p.GetType().GetField("bDidFirstFire", bindingFlags).SetValue(p, __instance.bDidFirstFire);
bool flag8 = __instance.TaxRates == null;
if (flag8)
{
p.taxRates = new float[World.inst.NumLandMasses];
int l = 0;
int m = World.inst.NumLandMasses;
while (l < m)
{
p.taxRates[l] = (float)__instance.TaxRate;
l++;
}
}
else
{
p.taxRates = new float[__instance.TaxRates.Length];
Array.Copy(__instance.TaxRates, p.taxRates, __instance.TaxRates.Length);
}
p.difficulty = __instance.Difficulty;
p.CurrYear = __instance.CurrYear;
p.timeAtFailHappiness = __instance.timeAtFailHappiness;
p.currConsumption = __instance.currConsumptionList;
Main.helper.Log("9");
while (p.currConsumption.Count < World.inst.NumLandMasses)
{
p.currConsumption.Add(new Player.Consumption());
}
p.lastConsumption = __instance.lastConsumptionList;
while (p.lastConsumption.Count < World.inst.NumLandMasses)
{
p.lastConsumption.Add(new Player.Consumption());
}
p.currProduction = __instance.currProductionList;
while (p.currProduction.Count < World.inst.NumLandMasses)
{
p.currProduction.Add(new Player.Production());
}
p.lastProduction = __instance.lastProductionList;
while (p.lastProduction.Count < World.inst.NumLandMasses)
{
p.lastProduction.Add(new Player.Production());
}
p.happinessMods = __instance.happinessMods;
Main.helper.Log("10");
bool flag9 = p.happinessMods == null;
if (flag9)
{
p.happinessMods = new List<Player.HappinessMod>();
}
Main.helper.Log("11");
bool flag10 = __instance.landMassNames == null;
if (flag10)
{
p.ResetLandMassNames();
}
else
{
p.LandMassNames.Clear();
for (int n = 0; n < __instance.landMassNames.Count; n++)
{
p.LandMassNames.Add(__instance.landMassNames[n]);
}
}
Main.helper.Log("12");
bool flag11 = __instance.playerLandmassOwnerSaveData != null;
if (flag11)
{
__instance.playerLandmassOwnerSaveData.Unpack(p.PlayerLandmassOwner);
}
else
{
var Resources = (ResourceAmount)__instance.GetType().GetField("Resources").GetValue(__instance);
p.PlayerLandmassOwner.Gold = Resources.Get(FreeResourceType.Gold);
for (int i2 = 0; i2 < __instance.structures.Count; i2++)
{
Building.BuildingSaveData[] occupyingStructureData = __instance.structures[i2];
for (int h = 0; h < occupyingStructureData.Length; h++)
{
int lIdx = World.inst.GetCellData(occupyingStructureData[h].globalPosition).landMassIdx;
bool flag12 = !p.PlayerLandmassOwner.OwnsLandMass(lIdx);
if (flag12)
{
p.PlayerLandmassOwner.TakeOwnership(lIdx);
}
}
}
}
Main.helper.Log("13");
bool flag13 = __instance.bannerIdx != -1;
if (flag13)
{
p.SetIndexedBanner(__instance.bannerIdx);
}
bool flag14 = !__instance.newBannerSystem;
Main.helper.Log("14");
if (flag14)
{
p.SetIndexedBanner(5);
p.SetCustomBannerTexture(World.inst.liverySets[__instance.bannerIdx].bannerMaterial.mainTexture as Texture2D);
}
bool flag15 = p.PlayerLandmassOwner.Gold < 0;
if (flag15)
{
p.PlayerLandmassOwner.Gold = 0;
}
Main.helper.Log("15");
var upgrades = __instance.GetType().GetField("upgrades", bindingFlags).GetValue(__instance) as List<Player.UpgradeType>;
for (int i3 = 0; i3 < upgrades.Count; i3++)
{
p.PlayerLandmassOwner.AddUpgrade(upgrades[i3]);
}
p.Workers.Clear();
p.Homeless.Clear();
Main.helper.Log("16");
Main.helper.Log($"Loading {__instance.WorkersArray.Length} workers from workers array for {p.PlayerLandmassOwner.teamId}");
bool flag16 = __instance.WorkersArray != null;
if (flag16)
{
Main.helper.Log("17");
for (int i4 = 0; i4 < __instance.WorkersArray.Length; i4++)
{
Villager person = Villager.CreateVillager();
person.Pos = __instance.WorkersArray[i4].pos;
__instance.WorkersArray[i4].Unpack(person);
p.Workers.Add(person);
}
}
else
{
Main.helper.Log("18");
Main.helper.Log($"Loading {__instance.Workers.Count} workers for {p.PlayerLandmassOwner.teamId}");
for (int i5 = 0; i5 < __instance.Workers.Count; i5++)
{
Villager person2 = Villager.CreateVillager();
person2.Pos = __instance.Workers[i5].pos;
__instance.Workers[i5].Unpack(person2);
p.Workers.Add(person2);
}
}
Main.helper.Log("19");
for (int i6 = 0; i6 < __instance.HomelessData.Count; i6++)
{
Villager worker = p.GetWorker(__instance.HomelessData[i6]);
bool flag17 = worker != null;
if (flag17)
{
p.Homeless.Add(worker);
}
}
Main.helper.Log("20");
/*List<Player.PlayerSaveData.BuildingLoadHelper> buildingsToPlace = new List<Player.PlayerSaveData.BuildingLoadHelper>();
for (int i7 = 0; i7 < __instance.structures.Count; i7++)
{
Building.BuildingSaveData[] occupyingStructureData2 = __instance.structures[i7];
for (int h2 = 0; h2 < occupyingStructureData2.Length; h2++)
{
Building building = __instance.ProcessBuilding(occupyingStructureData2[h2], p);
bool flag18 = building != null;
if (flag18)
{
buildingsToPlace.Add(new Player.PlayerSaveData.BuildingLoadHelper(building, occupyingStructureData2[h2], h2));
}
}
}
for (int i8 = 0; i8 < __instance.subStructures.Count; i8++)
{
Building.BuildingSaveData[] occupyingStructureData3 = __instance.subStructures[i8];
for (int h3 = 0; h3 < occupyingStructureData3.Length; h3++)
{
Building building2 = __instance.ProcessBuilding(occupyingStructureData3[h3], p);
bool flag19 = building2 != null;
if (flag19)
{
buildingsToPlace.Add(new Player.PlayerSaveData.BuildingLoadHelper(building2, occupyingStructureData3[h3], h3 - 1000));
}
}
}
foreach (Player.PlayerSaveData.BuildingLoadHelper buildingHelper in from x in buildingsToPlace
orderby x.priority
select x)
{
World.inst.PlaceFromLoad(buildingHelper.building);
buildingHelper.buildingSaveData.UnpackStage2(buildingHelper.building);
}
for (int i9 = 0; i9 < p.Workers.Count; i9++)
{
bool flag20 = p.Workers.data[i9].Residence == null;
if (flag20)
{
bool flag21 = !p.Homeless.Contains(p.Workers.data[i9]);
if (flag21)
{
p.Homeless.Add(p.Workers.data[i9]);
Debug.LogError("worker with null residence not in homeless saved data...");
}
}
}
for (int i10 = 0; i10 < buildingsToPlace.Count; i10++)
{
Player.PlayerSaveData.BuildingLoadHelper buildingHelper2 = buildingsToPlace[i10];
Road roadComp = buildingHelper2.building.GetComponent<Road>();
bool flag22 = roadComp != null;
if (flag22)
{
roadComp.UpdateRotation();
}
bool flag23 = buildingHelper2.building.uniqueNameHash == Player.PlayerSaveData.aqueductHash;
if (flag23)
{
buildingHelper2.building.GetComponent<Aqueduct>().UpdateRotation();
}
}
Cell[] cellData = World.inst.GetCellsData();
for (int i11 = 0; i11 < cellData.Length; i11++)
{
List<Building> structures = cellData[i11].OccupyingStructure;
for (int j2 = 0; j2 < structures.Count; j2++)
{
bool flag24 = structures[j2].categoryHash == Player.PlayerSaveData.castleblockHash;
if (flag24)
{
structures[j2].GetComponent<CastleBlock>().UpdateStackPostLoad();
break;
}
}
}
bool flag25 = Player.inst.keep != null;
if (flag25)
{
p.buildingContainer.BroadcastMessage("OnAnyBuildingAdded", Player.inst.keep.GetComponent<Building>(), SendMessageOptions.DontRequireReceiver);
}
World.inst.SetupInitialPathCosts();
Player.inst.irrigation.UpdateIrrigation();
Player.inst.CalcMaxResources(null, -1);
bool flag26 = __instance.CanUseTools != null;
if (flag26)
{
int i12 = 0;
while (i12 < __instance.CanUseTools.Length && i12 < p.CanUseTools.Length)
{
Array.Copy(__instance.CanUseTools[i12], p.CanUseTools[i12], __instance.CanUseTools[i12].Length);
i12++;
}
}
ToolInfo.inst.SetTogglesFromPlayerData();
p.hasUsedCheats = __instance.usedCheats;
p.checkedForceCreative = false;
p.nameForOldAgeDeath = __instance.nameForOldAgeDeath;
p.deathsThisYear = __instance.deathsThisYear;
World.inst.RebuildVillagerGrid();
ArrayExt<Building> keepers = Player.inst.GetBuildingList(World.cemeteryKeeperHash);
for (int i13 = 0; i13 < keepers.Count; i13++)
{
keepers.data[i13].GetComponent<CemeteryKeeper>().RebuildPathData();
}
p.ChangeHazardPayActive(p.PlayerLandmassOwner.hazardPay, false);
p.poorHealthGracePeriod = __instance.poorHealthGracePeriod;
p.UpdateFocusedLandmass();
PopulationUI.inst.regionName.RefreshRegionName();
p.dockOpenings = __instance.dockOpenings;
bool flag27 = p.dockOpenings == null;
if (flag27)
{
p.dockOpenings = new List<Player.DockOpening>();
}
p.IntegrityTimer.ForceExpire();
bool flag28 = __instance.tourism != null;
if (flag28)
{
p.tourism = __instance.tourism;
}
else
{
p.tourism = new Player.TourismInfo();
}
__result = p;
return false;
}
return true;
}
}*/
#endregion
/**
*
* Find all Player.inst references and reconstruct method with references to client planyer
*
* Instantiating main player object and setting landmass teamid in KCPLayer
*
* E.G instead of Player.inst, it should be Main.kCPlayers[Client].player for example, and the rest of the code is the same
*
* Prefix that sets Player.inst to the right client instance and then calls that instances method?
*
*/
[HarmonyPatch]
public class PlayerReferencePatch
{
static IEnumerable<MethodBase> TargetMethods()
{
Assembly assembly = typeof(Player).Assembly;
Type[] types = new Type[] { typeof(Player)/*, typeof(World), typeof(LandmassOwner), typeof(Keep), typeof(Villager), typeof(DragonSpawn), typeof(DragonController), typeof(Dragon)*/ };
var methodsInNamespace = types
.SelectMany(t => t.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)
{
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); // Replace all instance methods static ref with "this" instead of Player.inst
// Replace ldsfld Player::inst with the sequence to load from Main.kCPlayers
// Step 1: Load Main.kCPlayers onto the evaluation stack.
//codes[i] = new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField("kCPlayers"));
// Step 2: Load the value of Main.PlayerSteamID onto the evaluation stack as the key
//codes.Insert(++i, new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField("PlayerSteamID")));
// Step 3: Call Dictionary<TKey, TValue>.get_Item(TKey key) to get the Player instance.
//codes.Insert(++i, new CodeInstruction(OpCodes.Callvirt, typeof(Dictionary<string, KCPlayer>).GetMethod("get_Item")));
// Now, access the 'inst' field of the fetched Player instance, if necessary.
//codes.Insert(++i, new CodeInstruction(OpCodes.Ldfld, typeof(KCPlayer).GetField("inst")));
}
}
if (PlayerInstCount > 0)
Main.helper.Log($"Found {PlayerInstCount} static Player.inst references in {method.Name}");
return codes.AsEnumerable();
}
}
[HarmonyPatch]
public class BuildingPlayerReferencePatch
{
static IEnumerable<MethodBase> TargetMethods()
{
Assembly assembly = typeof(Building).Assembly;
Type[] types = new Type[] { typeof(Building) };
var methodsInNamespace = types
.SelectMany(t => t.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)
{
int PlayerInstCount = 0;
var codes = new List<CodeInstruction>(instructions);
MethodInfo getPlayerByBuildingMethodInfo = typeof(Main).GetMethod("GetPlayerByBuilding", BindingFlags.Static | BindingFlags.Public);
for (var i = 0; i < codes.Count; i++)
{
if (codes[i].opcode == OpCodes.Ldsfld && codes[i].operand.ToString() == "Player inst")
{
PlayerInstCount++;
// Check if the current instruction is ldsfld Player.inst
if (codes[i].opcode == OpCodes.Ldsfld && codes[i].operand.ToString().Contains("Player inst"))
{
// Replace the instruction sequence
// Step 1: Load 'this' for the Building instance
codes[i].opcode = OpCodes.Ldarg_0;
// Step 2: Call GetPlayerByBuilding(Building instance) static method in Main
var callTeamID = new CodeInstruction(OpCodes.Call, getPlayerByBuildingMethodInfo);
codes.Insert(++i, callTeamID);
}
}
}
if (PlayerInstCount > 0)
Main.helper.Log($"Found {PlayerInstCount} static building Player.inst references in {method.Name}");
return codes.AsEnumerable();
}
}
[HarmonyPatch]
public class PlayerPatch
{
static IEnumerable<MethodBase> TargetMethods()
{
var meth = typeof(Player).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
return meth.Cast<MethodBase>();
}
public static bool Prefix(MethodBase __originalMethod, Player __instance)
{
if (__originalMethod.Name.Equals("Awake") && (KCServer.IsRunning || KCClient.client.IsConnected))
{
helper.Log("Awake run on player instance while server is running");
return false;
}
if (__originalMethod.Name.Equals("Awake") && __instance.gameObject.name.Contains("Client Player"))
{
helper.Log("Awake run on client instance");
try
{
//___defaultEnabledFlags = new bool[38];
//for (int i = 0; i < ___defaultEnabledFlags.Length; i++)
//{
// ___defaultEnabledFlags[i] = true;
//}
//__instance.PlayerLandmassOwner = __instance.gameObject.AddComponent<LandmassOwner>();
//helper.Log(__instance.PlayerLandmassOwner.ToString());
}
catch (Exception e)
{
helper.Log(e.ToString());
helper.Log(e.Message);
helper.Log(e.StackTrace);
}
return false;
}
if (__originalMethod.Name.Equals("Update") && __instance.gameObject.name.Contains("Client Player"))
{
//helper.Log("Update run on client instance");
try
{
//___defaultEnabledFlags = new bool[38];
//for (int i = 0; i < ___defaultEnabledFlags.Length; i++)
//{
// ___defaultEnabledFlags[i] = true;
//}
//__instance.PlayerLandmassOwner = __instance.gameObject.AddComponent<LandmassOwner>();
//helper.Log(__instance.PlayerLandmassOwner.ToString());
}
catch (Exception e)
{
helper.Log(e.ToString());
helper.Log(e.Message);
helper.Log(e.StackTrace);
}
return false;
}
if (__originalMethod.Name.Equals("Update"))
{
//helper.Log($"Update called for: {__instance.gameObject.name}");
try
{
if (KCClient.client.IsConnected && !__instance.gameObject.name.Contains("Client Player"))
{
StateObserver.RegisterObserver(__instance, new string[] {
"bannerIdx", "kingdomHappiness", "landMassHappiness", "landMassIntegrity", "bDidFirstFire", "CurrYear",
"timeAtFailHappiness", "hasUsedCheats", "nameForOldAgeDeath", "deathsThisYear", /*"poorHealthGracePeriod",*/
});
//StateObserver.Update(__instance);
}
}
catch (Exception e)
{
helper.Log(e.ToString());
helper.Log(e.Message);
helper.Log(e.StackTrace);
}
return true;
}
return true;
}
public static void Postfix(MethodBase __originalMethod, Player __instance)
{
if (__originalMethod.Name.Equals("Update"))
{
//helper.Log($"Update called for: {__instance.gameObject.name} in POSTFIX");
//helper.Log("CHECKING ALL COMPONENTS IN UPDATE: ");
//Component[] components = __instance.gameObject.GetComponents<Component>();
//foreach (Component component in components)
//{
// helper.Log("--- " + component.GetType().kingdomName);
//}
}
}
}
#region "Unity Log Hooks"
[HarmonyPatch(typeof(UnityEngine.Debug), "Log", new Type[] { typeof(object) })]
public class DebugLogPatch
{
public static void Prefix(object message)
{
if (Main.kCPlayers.Values.Any((p) => message.ToString().StartsWith(p.inst.PlayerLandmassOwner.teamId.ToString())))
return;
//Main.helper.Log($"UNITY 3D DEBUG LOG");
//Main.helper.Log(message.ToString());
}
}
[HarmonyPatch(typeof(UnityEngine.Debug), "Log", new Type[] { typeof(object), typeof(UnityEngine.Object) })]
public class DebugLogCPatch
{
public static void Prefix(object message, UnityEngine.Object context)
{
if (Main.kCPlayers.Values.Any((p) => message.ToString().StartsWith(p.inst.PlayerLandmassOwner.teamId.ToString())))
return;
//Main.helper.Log($"UNITY 3D DEBUG LOG");
//Main.helper.Log(context.ToString());
//Main.helper.Log(message.ToString());
}
}
[HarmonyPatch(typeof(UnityEngine.Debug), "LogError", new Type[] { typeof(object) })]
public class DebugLogErrorPatch
{
public static void Prefix(object message)
{
if (message.ToString().StartsWith(Player.inst?.PlayerLandmassOwner?.teamId.ToString() ?? ""))
return;
//Main.helper.Log($"UNITY 3D DEBUG LOG ERROR");
//Main.helper.Log(message.ToString());
}
}
[HarmonyPatch(typeof(UnityEngine.Debug), "LogError", new Type[] { typeof(object), typeof(UnityEngine.Object) })]
public class DebugLogErrorCPatch
{
public static void Prefix(object message, UnityEngine.Object context)
{
if (message.ToString().StartsWith(Player.inst?.PlayerLandmassOwner?.teamId.ToString() ?? ""))
return;
//Main.helper.Log($"UNITY 3D DEBUG LOG ERROR");
//Main.helper.Log(context.ToString());
//Main.helper.Log(message.ToString());
}
}
[HarmonyPatch(typeof(UnityEngine.Debug), "LogException", new Type[] { typeof(Exception) })]
public class DebugLogExceptionPatch
{
public static void Prefix(Exception exception)
{
//Main.helper.Log($"UNITY 3D DEBUG LOG EXCEPTION");
//Main.helper.Log(exception.Message);
//Main.helper.Log(exception.StackTrace);
}
}
[HarmonyPatch(typeof(UnityEngine.Debug), "LogException", new Type[] { typeof(Exception), typeof(UnityEngine.Object) })]
public class DebugLogExceptionCPatch
{
public static void Prefix(Exception exception, UnityEngine.Object context)
{
//Main.helper.Log($"UNITY 3D DEBUG LOG EXCEPTION");
//Main.helper.Log(exception.Message);
//Main.helper.Log(exception.StackTrace);
}
}
[HarmonyPatch(typeof(UnityEngine.Debug), "LogWarning", new Type[] { typeof(object) })]
public class DebugLogWarningPatch
{
public static void Prefix(object message)
{
if (message.ToString().Contains("Failed to send 928 byte"))
return;
//Main.helper.Log($"UNITY 3D DEBUG LOG WARNING");
//Main.helper.Log(message.ToString());
}
}
[HarmonyPatch(typeof(UnityEngine.Debug), "LogWarning", new Type[] { typeof(object), typeof(UnityEngine.Object) })]
public class DebugLogWarningCPatch
{
public static void Prefix(object message, UnityEngine.Object context)
{
if (Main.kCPlayers.Values.Any((p) => message.ToString().StartsWith(p.inst.PlayerLandmassOwner.teamId.ToString())))
return;
//Main.helper.Log($"UNITY 3D DEBUG LOG WARNING");
//Main.helper.Log(context.ToString());
//Main.helper.Log(message.ToString());
}
}
#endregion
}
public class DetailedTransformData
{
public string name;
public string path;
public Vector3 position;
public Vector3 localPosition;
public Quaternion rotation;
public Quaternion localRotation;
public Vector3 eulerAngles;
public Vector3 localEulerAngles;
public Vector3 localScale;
public Vector3 lossyScale;
public List<DetailedTransformData> children;
public List<string> components;
public DetailedTransformData(Transform transform, string parentPath = "")
{
name = transform.name;
path = parentPath == "" ? name : parentPath + "/" + name;
position = transform.position;
localPosition = transform.localPosition;
rotation = transform.rotation;
localRotation = transform.localRotation;
eulerAngles = transform.eulerAngles;
localEulerAngles = transform.localEulerAngles;
localScale = transform.localScale;
lossyScale = transform.lossyScale;
children = new List<DetailedTransformData>();
components = transform.GetComponents<Component>().Select(c => c.GetType().ToString()).ToList();
foreach (Transform child in transform)
{
children.Add(new DetailedTransformData(child, path));
}
}
}
}