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 kCPlayers = new Dictionary(); public static Dictionary clientSteamIds = new Dictionary(); public static KCPlayer GetPlayerByClientID(ushort clientId) { if (TryGetPlayerByClientID(clientId, out KCPlayer player)) { return player; } return null; } public static bool TryGetPlayerByClientID(ushort clientId, out KCPlayer player) { player = null; if (clientSteamIds.TryGetValue(clientId, out string steamId)) { return kCPlayers.TryGetValue(steamId, out player); } return false; } 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(); DontDestroyOnLoad(KCMSteamManager); lobbyManager = new GameObject("LobbyManager").AddComponent(); DontDestroyOnLoad(lobbyManager); try { SteamFriends.SetRichPresence("status", "Playing Multiplayer"); PacketHandler.Initialise(); Main.helper.Log(JsonConvert.SerializeObject(World.inst.mapSizeDefs, Formatting.Indented)); // Check if MainMenuUI_T is available if (Constants.MainMenuUI_T == null) { Main.helper.Log("MainMenuUI_T is null, cannot create Multiplayer button"); return; } // Debug: Log the UI structure to find the correct path Main.helper.Log($"MainMenuUI_T name: {Constants.MainMenuUI_T.name}"); Main.helper.Log($"MainMenuUI_T children count: {Constants.MainMenuUI_T.childCount}"); for (int i = 0; i < Constants.MainMenuUI_T.childCount; i++) { var child = Constants.MainMenuUI_T.GetChild(i); Main.helper.Log($" Child {i}: {child.name}"); for (int j = 0; j < child.childCount; j++) { Main.helper.Log($" SubChild {j}: {child.GetChild(j).name}"); } } // Correct path based on debug output: MainMenuUI -> TopLevelUICanvas -> TopLevel -> Body -> ButtonContainer -> New var buttonContainer = Constants.MainMenuUI_T.Find("TopLevelUICanvas/TopLevel/Body/ButtonContainer/New"); if (buttonContainer == null) { Main.helper.Log("Button container not found at TopLevelUICanvas/TopLevel/Body/ButtonContainer/New"); return; } Main.helper.Log($"Found button container at: {buttonContainer.name}"); var templateButton = buttonContainer.GetComponent