using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Pigeon.Movement; using Sparroh.UI; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.SceneManagement; 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("Coruscnium")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Lobby ban management. Block unwanted players from joining with a simple UI. Banned players get disconnected when they try to connect.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0.0+c78a02b8bbc826bb52d1512172f258d0a0cac473")] [assembly: AssemblyProduct("BanFromLobby")] [assembly: AssemblyTitle("BanFromLobbyMod")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BanFromLobbyMod { [BepInPlugin("coruscnium.banfromlobby", "BanFromLobby", "1.1.2.0")] [BepInProcess("Mycopunk.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] [MycoMod(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private struct BanEntry { public string PlayerName; public ulong SteamID; } public const string PluginGUID = "coruscnium.banfromlobby"; public const string PluginName = "BanFromLobby"; public const string PluginVersion = "1.1.2.0"; internal static ManualLogSource Logger; private static ConfigEntry _banListConfig; private static ConfigEntry _toggleKeyConfig; private List _bans = new List(); private bool _bansDirty = true; private const char BanEntrySep = '|'; private UIWindow _window; private bool _windowVisible; private FileSystemWatcher _configWatcher; private volatile bool _reloadPending; private float _reloadCooldown; private const float ReloadDebounceSeconds = 0.25f; private Key _toggleKey = (Key)100; private bool _keyCached; private const string BanRejectionMessage = "This player has banned you from their lobbies via the BanFromLobby mod."; internal static Plugin Instance { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; _banListConfig = ((BaseUnityPlugin)this).Config.Bind("Ban List", "Bans", "", "One ban per line. Format: PlayerName|SteamID"); _toggleKeyConfig = ((BaseUnityPlugin)this).Config.Bind("General", "ToggleKey", "F7", "Key to toggle the ban management window."); _toggleKeyConfig.SettingChanged += delegate { CacheKey(); }; ((BaseUnityPlugin)this).Config.Save(); CacheKey(); ParseBanList(); SetupConfigHotReload(); SceneManager.sceneLoaded += OnSceneLoaded; try { GameManager.OnClientConnected += OnClientConnected; Logger.LogInfo((object)"Subscribed to GameManager.OnClientConnected"); } catch (Exception ex) { Logger.LogWarning((object)("Could not subscribe to OnClientConnected: " + ex.Message)); } Logger.LogInfo((object)"BanFromLobby v1.1.2.0 loaded."); } private void OnClientConnected(ulong clientID) { ((MonoBehaviour)this).StartCoroutine(DelayedClientCheck(clientID)); } private IEnumerator DelayedClientCheck(ulong clientID) { yield return (object)new WaitForSeconds(1f); try { Player target = GameManager.GetPlayer(clientID); if ((Object)(object)target == (Object)null) { Logger.LogWarning((object)$"DelayedCheck: GetPlayer({clientID}) returned null."); yield break; } ulong steamID = target.UserID; if (steamID == 0) { Logger.LogWarning((object)$"DelayedCheck: Player still has UserID=0 for clientID={clientID}, cannot check."); } else if (IsBanned(steamID)) { Logger.LogInfo((object)$"Auto-disconnecting banned player (SteamID={steamID}, clientID={clientID})."); DoKickPlayer(target); } } catch (Exception ex) { Exception ex2 = ex; Logger.LogWarning((object)("Error in DelayedClientCheck: " + ex2.Message)); } } private void ParseBanList() { _bans.Clear(); string text = _banListConfig.Value ?? ""; if (string.IsNullOrWhiteSpace(text)) { return; } string[] array = text.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { string text3 = text2.Trim(); if (!string.IsNullOrEmpty(text3)) { string[] array2 = text3.Split('|'); if (array2.Length >= 2 && ulong.TryParse(array2[1], out var result)) { _bans.Add(new BanEntry { PlayerName = array2[0], SteamID = result }); } } } Logger.LogInfo((object)$"Loaded {_bans.Count} ban(s) from config."); _bansDirty = false; } private void SerializeBanList() { List list = new List(); foreach (BanEntry ban in _bans) { list.Add($"{ban.PlayerName}{'|'}{ban.SteamID}"); } _banListConfig.Value = string.Join("\n", list); ((BaseUnityPlugin)this).Config.Save(); _bansDirty = false; } private bool IsBanned(ulong steamId) { if (_bansDirty) { ParseBanList(); } return _bans.Any((BanEntry b) => b.SteamID == steamId); } private void CacheKey() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) try { _toggleKey = (Key)Enum.Parse(typeof(Key), _toggleKeyConfig.Value, ignoreCase: true); } catch { _toggleKey = (Key)100; } _keyCached = true; } private void BanPlayer(ulong steamId, string playerName) { if (IsBanned(steamId)) { SendChatMsg("" + playerName + " is already banned."); return; } _bans.Add(new BanEntry { PlayerName = playerName, SteamID = steamId }); SerializeBanList(); Player[] source = Resources.FindObjectsOfTypeAll(); Player val = ((IEnumerable)source).FirstOrDefault((Func)((Player p) => p.UserID == steamId)); if ((Object)(object)val != (Object)null) { DoKickPlayer(val); } SendChatMsg("BANNED: " + playerName + ""); Logger.LogInfo((object)$"Banned {playerName} (SteamID={steamId})."); RefreshWindow(); } private void UnbanPlayer(ulong steamId) { string playerName = _bans.FirstOrDefault((BanEntry b) => b.SteamID == steamId).PlayerName; _bans.RemoveAll((BanEntry b) => b.SteamID == steamId); SerializeBanList(); SendChatMsg("UNBANNED: " + playerName + ""); RefreshWindow(); } private static void DoKickPlayer(Player target) { try { if (!((Object)(object)target == (Object)null)) { ulong ownerClientId = ((NetworkBehaviour)target).OwnerClientId; NetworkManager.Singleton.DisconnectClient(ownerClientId, "This player has banned you from their lobbies via the BanFromLobby mod."); Logger.LogInfo((object)$"Disconnected player {target.Name} (SteamID={target.UserID}, clientId={ownerClientId})."); } } catch (Exception ex) { Logger.LogWarning((object)("Failed to disconnect player: " + ex.Message)); } } private void SendChatMsg(string message) { try { if ((Object)(object)Player.LocalPlayer != (Object)null && (Object)(object)Player.LocalPlayer.PlayerLook != (Object)null) { Player.LocalPlayer.PlayerLook.AddTextChatMessage(message, Player.LocalPlayer); } } catch { } } private void SetupConfigHotReload() { try { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; string directoryName = Path.GetDirectoryName(configFilePath); string fileName = Path.GetFileName(configFilePath); if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(fileName)) { Logger.LogWarning((object)"Could not set up config hot reload: invalid config path."); return; } _configWatcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite), EnableRaisingEvents = true }; _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileChanged; Logger.LogInfo((object)("Config hot reload enabled for " + fileName)); } catch (Exception ex) { Logger.LogWarning((object)("Failed to set up config hot reload: " + ex.Message)); } } private void OnConfigFileChanged(object sender, FileSystemEventArgs e) { _reloadPending = true; } private void CreateWindow() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (_window != null) { return; } try { _window = UIWindow.Create("BanFromLobbyWindow", (Vector2?)new Vector2(540f, 500f), $"BAN FROM LOBBY ({_bans.Count})", true, false, (int?)29999); _window.OnClose((Action)delegate { }); RefreshWindow(); Logger.LogInfo((object)"BanFromLobby UI window created."); } catch (Exception ex) { Logger.LogError((object)("Failed to create BanFromLobby window: " + ex.Message)); } } private void RefreshWindow() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (_window == null || (Object)(object)_window.Content == (Object)null) { return; } foreach (Transform item in _window.Content) { Transform val = item; Object.Destroy((Object)(object)((Component)val).gameObject); } _window.WithTitle($"BAN FROM LOBBY ({_bans.Count})"); UIWindow.CreateSectionHeader(_window.Content, "CONNECTED PLAYERS"); ListPlayers(_window.Content); UIWindow.CreateSectionHeader(_window.Content, "BANNED PLAYERS"); ListBans(_window.Content); UIButton.Create(_window.Content, "[CLOSE]", (Action)delegate { ToggleWindow(); }, (UIButtonStyle)1, "CloseBtn", (float?)null); } private void ListPlayers(Transform content) { //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) try { List list = (from p in Resources.FindObjectsOfTypeAll() where (Object)(object)p != (Object)null && p.UserID != 0L && (Object)(object)p != (Object)(object)Player.LocalPlayer select p).ToList(); if (list.Count == 0) { UIText.Create(content, "NoPlayersLabel", "No other players connected.", UITheme.ScaledFontBody, (Color?)UIColors.TextMuted, (TextAlignmentOptions)513, false); return; } foreach (Player item in list) { ulong steamId = item.UserID; bool flag = IsBanned(steamId); UIPanel val = UIPanel.Create(content, "PlayerRow_" + steamId, (Color?)UIColors.PanelBg, false); UIFactory.AddHorizontalLayout(val.GameObject, 4f, new RectOffset(4, 4, 2, 2), (TextAnchor)3, true, false, true, true); string text = (string.IsNullOrEmpty(item.Name) ? $"Player-{item.PlayerNumber}" : item.Name); UIText val2 = UIText.Create(val.Content, "Label", $"{text} [{steamId}]", UITheme.ScaledFontBody, (Color?)(flag ? UIColors.TextMuted : UIColors.TextPrimary), (TextAlignmentOptions)513, false); if (val2 != null) { LayoutElement val3 = val2.GameObject.AddComponent(); val3.flexibleWidth = 1f; } if (!flag) { string name = text; UIButton val4 = UIButton.Create(val.Content, "BAN", (Action)delegate { BanPlayer(steamId, name); }, (UIButtonStyle)2, (string)null, (float?)36f); if (val4 != null) { LayoutElement val5 = val4.GameObject.AddComponent(); val5.minWidth = 80f; } } else { UIText val6 = UIText.Create(val.Content, "BannedLabel", "[BANNED]", UITheme.ScaledFontBody, (Color?)UIColors.TextMuted, (TextAlignmentOptions)513, false); if (val6 != null) { LayoutElement val7 = val6.GameObject.AddComponent(); val7.flexibleWidth = 1f; } } } } catch (Exception ex) { UIText.Create(content, "ErrorLabel", "Error listing players: " + ex.Message, UITheme.ScaledFontBody, (Color?)UIColors.Rose, (TextAlignmentOptions)513, false); } } private void ListBans(Transform content) { //IL_0024: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if (_bans.Count == 0) { UIText.Create(content, "NoBansLabel", "No banned players.", UITheme.ScaledFontBody, (Color?)UIColors.TextMuted, (TextAlignmentOptions)513, false); return; } foreach (BanEntry ban in _bans) { UIPanel val = UIPanel.Create(content, "BanRow_" + ban.SteamID, (Color?)UIColors.PanelBg, false); UIFactory.AddHorizontalLayout(val.GameObject, 4f, new RectOffset(4, 4, 2, 2), (TextAnchor)3, true, false, true, true); UIText val2 = UIText.Create(val.Content, "Label", $"{ban.PlayerName} | {ban.SteamID}", UITheme.ScaledFontBody, (Color?)UIColors.TextPrimary, (TextAlignmentOptions)513, false); if (val2 != null) { LayoutElement val3 = val2.GameObject.AddComponent(); val3.flexibleWidth = 1f; } ulong sid = ban.SteamID; UIButton val4 = UIButton.Create(val.Content, "UNBAN", (Action)delegate { UnbanPlayer(sid); }, (UIButtonStyle)1, (string)null, (float?)36f); if (val4 != null) { LayoutElement val5 = val4.GameObject.AddComponent(); val5.minWidth = 80f; } } } private void ToggleWindow() { if (_window == null) { CreateWindow(); if (_window == null) { return; } } _windowVisible = !_windowVisible; if (_windowVisible) { RefreshWindow(); _window.Show(); LockGameInput(locked: true); } else { _window.Hide(true); LockGameInput(locked: false); } } private void LockGameInput(bool locked) { try { if (locked) { PlayerInput.EnableMenu(); if ((Object)(object)Player.LocalPlayer != (Object)null) { PlayerLook playerLook = Player.LocalPlayer.PlayerLook; int rotationLocksX = playerLook.RotationLocksX; playerLook.RotationLocksX = rotationLocksX + 1; PlayerLook playerLook2 = Player.LocalPlayer.PlayerLook; rotationLocksX = playerLook2.RotationLocksY; playerLook2.RotationLocksY = rotationLocksX + 1; Player.LocalPlayer.LockFiring(true); } Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } else { PlayerInput.DisableMenu(); if ((Object)(object)Player.LocalPlayer != (Object)null) { Player.LocalPlayer.PlayerLook.RotationLocksX = Math.Max(0, Player.LocalPlayer.PlayerLook.RotationLocksX - 1); Player.LocalPlayer.PlayerLook.RotationLocksY = Math.Max(0, Player.LocalPlayer.PlayerLook.RotationLocksY - 1); Player.LocalPlayer.LockFiring(false); } Cursor.visible = false; Cursor.lockState = (CursorLockMode)1; } } catch { } } private void Update() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (_reloadPending) { _reloadPending = false; _reloadCooldown = 0.25f; } if (_reloadCooldown > 0f) { _reloadCooldown -= Time.unscaledDeltaTime; if (_reloadCooldown <= 0f) { TryReloadConfig(); } } if (Keyboard.current != null && _keyCached && ((ButtonControl)Keyboard.current[_toggleKey]).wasPressedThisFrame) { ToggleWindow(); } } private void TryReloadConfig() { try { ((BaseUnityPlugin)this).Config.Reload(); _bansDirty = true; Logger.LogInfo((object)"Config reloaded from disk."); } catch (IOException) { _reloadPending = true; } catch (Exception ex2) { Logger.LogWarning((object)("Failed to reload config: " + ex2.Message)); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (_windowVisible) { _windowVisible = false; LockGameInput(locked: false); } try { GameManager.OnClientConnected += OnClientConnected; } catch { } } private void OnDestroy() { if (_configWatcher != null) { _configWatcher.EnableRaisingEvents = false; _configWatcher.Changed -= OnConfigFileChanged; _configWatcher.Created -= OnConfigFileChanged; _configWatcher.Renamed -= OnConfigFileChanged; _configWatcher.Dispose(); _configWatcher = null; } if (_windowVisible) { LockGameInput(locked: false); } try { if (_window != null) { _window.Hide(true); _window.Destroy(); _window = null; } } catch { } } } }