using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using FishComp.Commands; using FishComp.Competition; using FishComp.Networking; using FishComp.Patches; using FishComp.UI; using HarmonyLib; using Jotunn; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("FishComp")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("FishComp")] [assembly: AssemblyTitle("FishComp")] [assembly: AssemblyVersion("1.0.0.0")] namespace FishComp { [BepInPlugin("com.fishcomp.valheim", "FishComp", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGuid = "com.fishcomp.valheim"; public const string ModName = "FishComp"; public const string ModVersion = "1.0.0"; private static GameObject _updaterHost; private Harmony harmony; public static Plugin Instance { get; private set; } public static ConfigEntry ConfigPositionX { get; private set; } public static ConfigEntry ConfigPositionY { get; private set; } public CompetitionManager CompetitionManager { get; private set; } public RpcManager RpcManager { get; private set; } public LeaderboardUI UI { get; private set; } public FishCompCommands Commands { get; private set; } private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown GameObject val = new GameObject("FishComp_UpdaterHost"); Object.DontDestroyOnLoad((Object)(object)val); _updaterHost = val; Instance = this; ConfigPositionX = ((BaseUnityPlugin)this).Config.Bind("UI", "PanelPositionX", 20f, "Horizontal position of the leaderboard panel from the left edge"); ConfigPositionY = ((BaseUnityPlugin)this).Config.Bind("UI", "PanelPositionY", -180f, "Vertical position of the leaderboard panel from the top edge (negative = downward)"); CompetitionManager = new CompetitionManager(); RpcManager = new RpcManager(this); Commands = new FishCompCommands(); PrefabManager.OnVanillaPrefabsAvailable += InitUI; harmony = new Harmony("com.fishcomp.valheim"); harmony.PatchAll(); MethodInfo methodInfo = AccessTools.Method(typeof(Humanoid), "Pickup", (Type[])null, (Type[])null); if (methodInfo != null) { MethodInfo methodInfo2 = AccessTools.Method(typeof(FishingPatch), "HumanoidPickupPostfix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Logger.LogInfo((object)"Patched Humanoid.Pickup"); } else { Logger.LogError((object)"Humanoid.Pickup not found!"); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"FishComp v1.0.0 loaded."); } private void InitUI() { Logger.LogInfo((object)"InitUI called — creating LeaderboardUI"); UI = new LeaderboardUI(_updaterHost); PrefabManager.OnVanillaPrefabsAvailable -= InitUI; } } } namespace FishComp.UI { public class LeaderboardUI { private class LeaderboardUpdater : MonoBehaviour { private LeaderboardUI leaderboardUI; public void Init(LeaderboardUI ui) { leaderboardUI = ui; } private void Update() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { CompetitionManager.Instance?.Tick(Time.deltaTime); } leaderboardUI?.Refresh(); } } private GameObject panel; private Text timerText; private Transform fishCaughtList; private bool _showTestUI; private bool PanelExists => (Object)(object)panel != (Object)null && Object.op_Implicit((Object)(object)panel); public LeaderboardUI(GameObject updaterHost) { if (GUIManager.Instance == null) { Logger.LogError((object)"LeaderboardUI created before GUIManager is available."); return; } CompetitionManager.Instance.OnLeaderboardChanged += Refresh; LeaderboardUpdater leaderboardUpdater = updaterHost.AddComponent(); leaderboardUpdater.Init(this); } private void CreatePanel() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) Logger.LogInfo((object)"CreatePanel() entered"); GameObject val = new GameObject("FishCompCanvasRoot", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Object.DontDestroyOnLoad((Object)(object)val); Canvas component = val.GetComponent(); component.renderMode = (RenderMode)0; component.sortingOrder = 100; CanvasScaler component2 = val.GetComponent(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(1920f, 1080f); component2.screenMatchMode = (ScreenMatchMode)0; component2.matchWidthOrHeight = 0.5f; panel = new GameObject("FishCompPanel", new Type[2] { typeof(RectTransform), typeof(Image) }); panel.transform.SetParent(val.transform, false); RectTransform component3 = panel.GetComponent(); component3.anchorMin = new Vector2(0f, 1f); component3.anchorMax = new Vector2(0f, 1f); component3.pivot = new Vector2(0f, 1f); component3.anchoredPosition = new Vector2(Plugin.ConfigPositionX.Value, Plugin.ConfigPositionY.Value); component3.sizeDelta = new Vector2(200f, 0f); Image component4 = panel.GetComponent(); component4.sprite = null; ((Graphic)component4).color = new Color(0f, 0f, 0f, 0.6f); VerticalLayoutGroup val2 = panel.AddComponent(); ((LayoutGroup)val2).padding = new RectOffset(10, 10, 8, 10); ((HorizontalOrVerticalLayoutGroup)val2).spacing = 4f; ((LayoutGroup)val2).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = false; ContentSizeFitter val3 = panel.AddComponent(); val3.horizontalFit = (FitMode)0; val3.verticalFit = (FitMode)2; Font font = (((Object)(object)GUIManager.Instance.AveriaSerifBold != (Object)null) ? GUIManager.Instance.AveriaSerifBold : GUIManager.Instance.AveriaSerif); CreateText("Title", panel.transform, "\ud83c\udfa3 Fishing Competition", 15, (FontStyle)1, font, (TextAnchor)4); timerText = CreateText("Timer", panel.transform, "Time Remaining: 00:00", 12, (FontStyle)0, font, (TextAnchor)0); fishCaughtList = CreateList("FishCaughtList", panel.transform); Logger.LogInfo((object)$"CreatePanel() completed — canvas.renderMode: {component.renderMode}, rect.anchoredPosition: {component3.anchoredPosition}, anchorMin: {component3.anchorMin}, pivot: {component3.pivot}"); } private static Text CreateText(string name, Transform parent, string text, int fontSize, FontStyle style, Font font, TextAnchor alignment = (TextAnchor)0) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(Text), typeof(ContentSizeFitter) }); val.transform.SetParent(parent, false); Text component = val.GetComponent(); component.text = text; component.font = font; component.fontSize = fontSize; component.fontStyle = style; ((Graphic)component).color = Color.white; component.alignment = alignment; component.horizontalOverflow = (HorizontalWrapMode)0; component.verticalOverflow = (VerticalWrapMode)0; ContentSizeFitter component2 = val.GetComponent(); component2.horizontalFit = (FitMode)0; component2.verticalFit = (FitMode)2; return component; } private static Transform CreateList(string name, Transform parent) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); val.transform.SetParent(parent, false); VerticalLayoutGroup component = val.GetComponent(); ((HorizontalOrVerticalLayoutGroup)component).spacing = 2f; ((LayoutGroup)component).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ContentSizeFitter component2 = val.GetComponent(); component2.horizontalFit = (FitMode)0; component2.verticalFit = (FitMode)2; RectTransform component3 = val.GetComponent(); component3.sizeDelta = new Vector2(240f, 20f); return val.transform; } public void SetPosition(float x, float y) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) Logger.LogInfo((object)$"SetPosition called: ({x}, {y}), PanelExists: {PanelExists}"); if (PanelExists) { RectTransform component = panel.GetComponent(); Logger.LogInfo((object)$"Before: anchoredPosition={component.anchoredPosition}, anchorMin={component.anchorMin}, anchorMax={component.anchorMax}, pivot={component.pivot}"); component.anchoredPosition = new Vector2(x, y); Logger.LogInfo((object)$"After: anchoredPosition={component.anchoredPosition}"); } } public bool ToggleTestUI() { _showTestUI = !_showTestUI; Logger.LogInfo((object)$"ToggleTestUI: _showTestUI is now {_showTestUI}, PanelExists: {PanelExists}"); Refresh(); return _showTestUI; } public void Refresh() { if (!PanelExists) { if (GUIManager.Instance == null) { return; } CreatePanel(); } if (!PanelExists) { return; } CompetitionManager instance = CompetitionManager.Instance; bool isRunning = instance.IsRunning; if (isRunning) { _showTestUI = false; } panel.SetActive(isRunning || _showTestUI); if (!isRunning && !_showTestUI) { return; } if (_showTestUI && !isRunning) { timerText.text = "Time Remaining: 05:00"; RebuildList(fishCaughtList, new List { new LeaderboardEntry { PlayerId = "1", PlayerName = "Fisherman1", FishCaught = 7 }, new LeaderboardEntry { PlayerId = "2", PlayerName = "Fisherman2", FishCaught = 4 }, new LeaderboardEntry { PlayerId = "3", PlayerName = "Fisherman3", FishCaught = 1 } }); LayoutRebuilder.ForceRebuildLayoutImmediate(panel.GetComponent()); } else { int num = Mathf.Max(0, Mathf.CeilToInt(instance.RemainingSeconds)); timerText.text = $"Time Remaining: {num / 60:00}:{num % 60:00}"; RebuildList(fishCaughtList, (from e in instance.Entries.Values orderby e.FishCaught descending, e.PlayerName select e).ToList()); LayoutRebuilder.ForceRebuildLayoutImmediate(panel.GetComponent()); } } private void RebuildList(Transform list, List entries) { for (int num = list.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)list.GetChild(num)).gameObject); } Font font = (((Object)(object)GUIManager.Instance.AveriaSerifBold != (Object)null) ? GUIManager.Instance.AveriaSerifBold : GUIManager.Instance.AveriaSerif); if (entries.Count == 0) { CreateText("NoEntries", list, "No catches yet", 12, (FontStyle)0, font, (TextAnchor)0); return; } for (int i = 0; i < entries.Count; i++) { LeaderboardEntry leaderboardEntry = entries[i]; CreateText($"Entry{i + 1}", list, $"{i + 1}. {leaderboardEntry.PlayerName} — {leaderboardEntry.FishCaught}", 12, (FontStyle)0, font, (TextAnchor)0); } } } } namespace FishComp.Patches { public static class FishingPatch { public static void HumanoidPickupPostfix(Humanoid __instance, GameObject go, bool __result) { if (!__result) { return; } Fish component = go.GetComponent(); if (!((Object)(object)component == (Object)null) && CompetitionManager.Instance != null && CompetitionManager.Instance.IsRunning && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null)) { string playerId = val.GetPlayerID().ToString(); string playerName = val.GetPlayerName(); Logger.LogInfo((object)("Recording catch — player: " + playerName)); CompetitionManager.Instance.RecordCatch(playerId, playerName); } } } } } namespace FishComp.Networking { public class RpcManager { public const string RpcSyncState = "FishComp_SyncState"; public const string RpcAnnounceWinner = "FishComp_AnnounceWinner"; private readonly Plugin plugin; private bool registered; private bool isApplyingRemoteState; public static RpcManager Instance { get; private set; } public RpcManager(Plugin plugin) { this.plugin = plugin; Instance = this; CompetitionManager.Instance.OnLeaderboardChanged += OnLeaderboardChanged; CompetitionManager.Instance.OnCompetitionEnded += OnCompetitionEnded; Logger.LogInfo((object)"RpcManager subscribed to OnCompetitionEnded/OnLeaderboardChanged"); PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable; } private void OnVanillaPrefabsAvailable() { Register(); } public void Register() { if (!registered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("FishComp_SyncState", (Action)RPC_SyncState); ZRoutedRpc.instance.Register("FishComp_AnnounceWinner", (Action)RPC_AnnounceWinner); registered = true; Logger.LogInfo((object)"RpcManager RPCs registered (SyncState, AnnounceWinner)"); } } public void BroadcastState() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (ZRoutedRpc.instance == null) { return; } Register(); CompetitionManager instance = CompetitionManager.Instance; ZPackage val = new ZPackage(); val.Write(instance.IsRunning); val.Write(instance.RemainingSeconds); val.Write(instance.Entries.Count); foreach (LeaderboardEntry value in instance.Entries.Values) { val.Write(value.PlayerId); val.Write(value.PlayerName); val.Write(value.FishCaught); } ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "FishComp_SyncState", new object[1] { val }); } public void BroadcastWinner(CompetitionResult result) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown if (ZRoutedRpc.instance == null) { Logger.LogWarning((object)"BroadcastWinner called but ZRoutedRpc.instance is null — skipping"); return; } Register(); if (!registered) { Logger.LogWarning((object)"BroadcastWinner called but RPCs are not registered — skipping"); return; } ZPackage val = new ZPackage(); val.Write(result.TopByFishCaught?.PlayerName ?? string.Empty); val.Write(result.TopByFishCaught?.FishCaught ?? 0); Logger.LogInfo((object)string.Format("BroadcastWinner — most fish: {0} ({1} fish)", result.TopByFishCaught?.PlayerName ?? "none", result.TopByFishCaught?.FishCaught ?? 0)); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "FishComp_AnnounceWinner", new object[1] { val }); } private void RPC_SyncState(long sender, ZPackage pkg) { bool isRunning = pkg.ReadBool(); float remainingSeconds = pkg.ReadSingle(); int num = pkg.ReadInt(); List list = new List(num); for (int i = 0; i < num; i++) { list.Add(new LeaderboardEntry { PlayerId = pkg.ReadString(), PlayerName = pkg.ReadString(), FishCaught = pkg.ReadInt() }); } isApplyingRemoteState = true; CompetitionManager.Instance.ApplyRemoteState(isRunning, remainingSeconds, list); isApplyingRemoteState = false; } private void RPC_AnnounceWinner(long sender, ZPackage pkg) { Logger.LogInfo((object)"RPC_AnnounceWinner received"); string text = pkg.ReadString(); int num = pkg.ReadInt(); string text2 = (string.IsNullOrEmpty(text) ? "—" : $"{text} ({num} fish)"); string text3 = "\ud83c\udfc6 FishComp Results — Most Fish Caught: " + text2; if ((Object)(object)MessageHud.instance == (Object)null) { Logger.LogWarning((object)"RPC_AnnounceWinner — MessageHud.instance is null, cannot show message"); return; } Logger.LogInfo((object)("RPC_AnnounceWinner — showing message: " + text3)); MessageHud.instance.ShowMessage((MessageType)2, text3, 0, (Sprite)null, false); } private void OnLeaderboardChanged() { if (!isApplyingRemoteState && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { BroadcastState(); } } private void OnCompetitionEnded(CompetitionResult result) { Logger.LogInfo((object)"RpcManager.OnCompetitionEnded invoked"); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { BroadcastWinner(result); BroadcastState(); } else { Logger.LogInfo((object)"OnCompetitionEnded — not server, skipping broadcast"); } } } } namespace FishComp.Competition { public class CompetitionResult { public LeaderboardEntry TopByFishCaught { get; set; } } public class CompetitionManager { public static CompetitionManager Instance { get; private set; } public bool IsRunning { get; private set; } public float RemainingSeconds { get; private set; } public Dictionary Entries { get; } = new Dictionary(); public event Action OnCompetitionEnded; public event Action OnLeaderboardChanged; public CompetitionManager() { Instance = this; } public void StartCompetition(int minutes) { IsRunning = true; RemainingSeconds = minutes * 60; Entries.Clear(); this.OnLeaderboardChanged?.Invoke(); } public CompetitionResult EndCompetition() { IsRunning = false; CompetitionResult competitionResult = new CompetitionResult { TopByFishCaught = Entries.Values.OrderByDescending((LeaderboardEntry e) => e.FishCaught).FirstOrDefault() }; this.OnCompetitionEnded?.Invoke(competitionResult); return competitionResult; } public void ResetCompetition() { IsRunning = false; Entries.Clear(); RemainingSeconds = 0f; this.OnLeaderboardChanged?.Invoke(); } public void RecordCatch(string playerId, string playerName) { if (IsRunning) { if (!Entries.TryGetValue(playerId, out var value)) { value = new LeaderboardEntry { PlayerId = playerId, PlayerName = playerName }; Entries[playerId] = value; } value.RecordCatch(); this.OnLeaderboardChanged?.Invoke(); } } public void ApplyRemoteState(bool isRunning, float remainingSeconds, IEnumerable entries) { IsRunning = isRunning; RemainingSeconds = remainingSeconds; Entries.Clear(); foreach (LeaderboardEntry entry in entries) { Entries[entry.PlayerId] = entry; } this.OnLeaderboardChanged?.Invoke(); } public void Tick(float deltaTime) { if (IsRunning) { RemainingSeconds -= deltaTime; if (RemainingSeconds <= 0f) { RemainingSeconds = 0f; EndCompetition(); } } } } [Serializable] public class LeaderboardEntry { public string PlayerId; public string PlayerName; public int FishCaught; public void RecordCatch() { FishCaught++; } } } namespace FishComp.Commands { public class FishCompCommands { private class StartCommand : ConsoleCommand { public override string Name => "fishcomp_start"; public override string Help => "Start a fishing competition. Usage: fishcomp_start "; public override bool IsCheat => false; public override bool IsSecret => false; public override bool OnlyServer => false; public override void Run(string[] args) { if (!CheckAdmin() || !CheckServer()) { return; } if (args.Length < 1 || !int.TryParse(args[0], out var result) || result < 1) { Console.instance.Print(((ConsoleCommand)this).Help); return; } if (CompetitionManager.Instance.IsRunning) { Console.instance.Print("A competition is already running."); return; } CompetitionManager.Instance.StartCompetition(result); Console.instance.Print($"FishComp started for {result} minutes. Good luck!"); Chat instance = Chat.instance; if (instance != null) { instance.SendText((Type)2, $"\ud83c\udfa3 A fishing competition has started! You have {result} minutes. Good luck!"); } } } private class EndCommand : ConsoleCommand { public override string Name => "fishcomp_end"; public override string Help => "End the current fishing competition and announce winners."; public override bool IsCheat => false; public override bool IsSecret => false; public override bool OnlyServer => false; public override void Run(string[] args) { if (CheckAdmin() && CheckServer()) { if (!CompetitionManager.Instance.IsRunning) { Console.instance.Print("No competition is currently running."); return; } CompetitionManager.Instance.EndCompetition(); Console.instance.Print("FishComp ended."); } } } private class ResetCommand : ConsoleCommand { public override string Name => "fishcomp_reset"; public override string Help => "Reset and hide the leaderboard UI."; public override bool IsCheat => false; public override bool IsSecret => false; public override bool OnlyServer => false; public override void Run(string[] args) { if (CheckAdmin() && CheckServer()) { CompetitionManager.Instance.ResetCompetition(); RpcManager.Instance.BroadcastState(); Console.instance.Print("FishComp reset."); } } } private class SetPosCommand : ConsoleCommand { public override string Name => "fishcomp_setpos"; public override string Help => "Set leaderboard panel position. Usage: fishcomp_setpos "; public override bool IsCheat => false; public override bool IsSecret => false; public override bool OnlyServer => false; public override void Run(string[] args) { if (args.Length < 2 || !float.TryParse(args[0], out var result) || !float.TryParse(args[1], out var result2)) { Console.instance.Print(((ConsoleCommand)this).Help); return; } Logger.LogInfo((object)$"fishcomp_setpos: parsed x={result}, y={result2}. Plugin.Instance.UI null: {Plugin.Instance.UI == null}"); Plugin.ConfigPositionX.Value = result; Plugin.ConfigPositionY.Value = result2; Plugin.Instance.UI?.SetPosition(result, result2); Console.instance.Print($"Panel position set to ({result}, {result2})"); } } private class TestUICommand : ConsoleCommand { public override string Name => "fishcomp_testui"; public override string Help => "Toggle the leaderboard test panel (dummy data for positioning). Run again to hide."; public override bool IsCheat => false; public override bool IsSecret => false; public override bool OnlyServer => false; public override void Run(string[] args) { Logger.LogInfo((object)"fishcomp_testui called"); if (Plugin.Instance.UI == null) { Console.instance.Print("LeaderboardUI not initialised yet — load a world first."); return; } bool flag = Plugin.Instance.UI.ToggleTestUI(); Console.instance.Print(flag ? "Test UI shown. Use fishcomp_setpos to reposition. Run fishcomp_testui again to hide." : "Test UI hidden."); } } public FishCompCommands() { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new StartCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new EndCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ResetCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetPosCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TestUICommand()); } private static bool CheckAdmin() { if (!SynchronizationManager.Instance.PlayerIsAdmin) { Console.instance.Print("You must be a server admin to use this command."); return false; } return true; } private static bool CheckServer() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { Console.instance.Print("This command must be run on the server."); return false; } return true; } } }