Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FishComp v1.0.0
FishComp.dll
Decompiled a day agousing 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<float> ConfigPositionX { get; private set; } public static ConfigEntry<float> 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<float>("UI", "PanelPositionX", 20f, "Horizontal position of the leaderboard panel from the left edge"); ConfigPositionY = ((BaseUnityPlugin)this).Config.Bind<float>("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>(); 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<Canvas>(); component.renderMode = (RenderMode)0; component.sortingOrder = 100; CanvasScaler component2 = val.GetComponent<CanvasScaler>(); 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<RectTransform>(); 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<Image>(); component4.sprite = null; ((Graphic)component4).color = new Color(0f, 0f, 0f, 0.6f); VerticalLayoutGroup val2 = panel.AddComponent<VerticalLayoutGroup>(); ((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<ContentSizeFitter>(); 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<Text>(); 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<ContentSizeFitter>(); 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<VerticalLayoutGroup>(); ((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<ContentSizeFitter>(); component2.horizontalFit = (FitMode)0; component2.verticalFit = (FitMode)2; RectTransform component3 = val.GetComponent<RectTransform>(); 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<RectTransform>(); 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<LeaderboardEntry> { 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<RectTransform>()); } 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<RectTransform>()); } } private void RebuildList(Transform list, List<LeaderboardEntry> 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<Fish>(); 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<ZPackage>("FishComp_SyncState", (Action<long, ZPackage>)RPC_SyncState); ZRoutedRpc.instance.Register<ZPackage>("FishComp_AnnounceWinner", (Action<long, ZPackage>)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<LeaderboardEntry> list = new List<LeaderboardEntry>(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<string, LeaderboardEntry> Entries { get; } = new Dictionary<string, LeaderboardEntry>(); public event Action<CompetitionResult> 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<LeaderboardEntry> 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 <minutes>"; 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 <x> <y>"; 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 <x> <y> 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; } } }