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 REPOLobbyFilter v1.1.1
REPOLobbyFilter.dll
Decompiled 4 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyCompany("REPOLobbyFilter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("R.E.P.O. Lobby Filter - Filter and manage game lobbies by region")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1+6abc6c319bb2b4646897551b41f723e16f26f379")] [assembly: AssemblyProduct("REPOLobbyFilter")] [assembly: AssemblyTitle("REPOLobbyFilter")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 REPOLobbyFilter { [BepInPlugin("REPOLobbyFilter", "REPOLobbyFilter", "1.1.1")] public class Plugin : BaseUnityPlugin { private static Plugin Instance; private Harmony _harmony; public ConfigEntry<bool> enableFilter; public ConfigEntry<KeyCode> blockHotkey; private static HashSet<string> autoBlocklist = new HashSet<string>(); private static HashSet<string> manualBlocklist = new HashSet<string>(); private static Dictionary<string, string> autoBlocklistNames = new Dictionary<string, string>(); private static Dictionary<string, string> manualBlocklistNames = new Dictionary<string, string>(); private static string autoBlocklistPath; private static string manualBlocklistPath; private static Dictionary<string, object> currentRooms = new Dictionary<string, object>(); private void Awake() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin REPOLobbyFilter v1.1.1 loaded!"); enableFilter = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "EnableFilter", true, "Enable Russian/Belarusian lobby filtering"); blockHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Hotkey", "BlockKey", (KeyCode)98, "Press this key to manually block the currently displayed lobby"); autoBlocklistPath = Path.Combine(Paths.ConfigPath, "REPOLobbyFilter_AutoBlocked.txt"); manualBlocklistPath = Path.Combine(Paths.ConfigPath, "REPOLobbyFilter_ManualBlocked.txt"); MigrateOldBlocklist(); LoadBlocklists(); _harmony = new Harmony("REPOLobbyFilter"); PatchMenuPageServerList(); PatchMenuPageServerListUpdate(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Lobby filter ready! Blocked lobbies will be auto-saved."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Press [B] or [F9] to manually block currently displayed lobby"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Auto-blocked lobbies: " + autoBlocklistPath)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Manual blocklist: " + manualBlocklistPath)); } private void MigrateOldBlocklist() { try { string text = Path.Combine(Paths.ConfigPath, "REPOLobbyFilter_Blocklist.txt"); if (!File.Exists(text) || File.Exists(autoBlocklistPath)) { return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Found v1.0.0 blocklist, migrating to v1.1.0 format..."); string[] array = File.ReadAllLines(text); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2)) { string item = text2.Trim(); autoBlocklist.Add(item); } } SaveAutoBlocklist(); string destFileName = Path.Combine(Paths.ConfigPath, "REPOLobbyFilter_Blocklist.txt.v1.0.backup"); File.Move(text, destFileName); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Migration complete! Migrated {autoBlocklist.Count} entries."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Old file backed up to: REPOLobbyFilter_Blocklist.txt.v1.0.backup"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Error migrating old blocklist: " + ex.Message)); } } private void PatchMenuPageServerListUpdate() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown Type type = AccessTools.TypeByName("MenuPageServerList"); if (!(type == null)) { MethodInfo methodInfo = AccessTools.Method(type, "Update", (Type[])null, (Type[])null); if (methodInfo != null) { HarmonyMethod val = new HarmonyMethod(typeof(Plugin).GetMethod("MenuPageServerList_Update_Postfix", BindingFlags.Static | BindingFlags.NonPublic)); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched MenuPageServerList.Update for hotkey detection"); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"MenuPageServerList has no Update method"); } } } private static void MenuPageServerList_Update_Postfix(object __instance) { if (Input.GetKeyDown((KeyCode)98) || Input.GetKeyDown((KeyCode)290)) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)"KEY PRESSED IN UPDATE PATCH!"); Instance.TryBlockCurrentLobby(__instance); } } public void TryBlockCurrentLobby(object menuPageInstance) { try { ((BaseUnityPlugin)this).Logger.LogInfo((object)"TryBlockCurrentLobby started"); Type type = menuPageInstance.GetType(); FieldInfo fieldInfo = AccessTools.Field(type, "roomList"); FieldInfo fieldInfo2 = AccessTools.Field(type, "serverElementParent"); if (fieldInfo == null || fieldInfo2 == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not find required fields"); return; } IList list = fieldInfo.GetValue(menuPageInstance) as IList; object? value = fieldInfo2.GetValue(menuPageInstance); Transform val = (Transform)((value is Transform) ? value : null); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"roomList.Count = {list?.Count ?? 0}"); if ((Object)(object)val == (Object)null) { ((BaseUnityPlugin)this).Logger.LogError((object)"serverElementParent is null"); return; } string text = null; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Checking {val.childCount} server UI elements..."); for (int i = 0; i < val.childCount; i++) { Transform child = val.GetChild(i); if ((Object)(object)child == (Object)null || !((Component)child).gameObject.activeInHierarchy) { continue; } Component component = ((Component)child).GetComponent(AccessTools.TypeByName("MenuElementHover")); if (!((Object)(object)component != (Object)null)) { continue; } FieldInfo fieldInfo3 = AccessTools.Field(((object)component).GetType(), "isHovering"); if (!(fieldInfo3 != null) || !(bool)fieldInfo3.GetValue(component)) { continue; } Component component2 = ((Component)child).GetComponent(AccessTools.TypeByName("MenuElementServer")); if ((Object)(object)component2 != (Object)null) { FieldInfo fieldInfo4 = AccessTools.Field(((object)component2).GetType(), "roomName"); if (fieldInfo4 != null) { text = fieldInfo4.GetValue(component2)?.ToString(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Found hovered element at UI index {i}, roomGUID = {text}"); break; } } } if (string.IsNullOrEmpty(text)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"No hovered lobby found!"); return; } if (list == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"roomList is null"); return; } object obj = null; foreach (object item in list) { if (item != null && AccessTools.Field(item.GetType(), "roomName")?.GetValue(item)?.ToString() == text) { obj = item; break; } } if (obj == null) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not find room with GUID " + text + " in roomList")); return; } string text2 = "Unknown"; FieldInfo fieldInfo5 = AccessTools.Field(obj.GetType(), "displayName"); if (fieldInfo5 != null) { text2 = fieldInfo5.GetValue(obj)?.ToString() ?? "Unknown"; } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Blocking hovered lobby: \"" + text2 + "\" (GUID: " + text + ")")); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Blocking hovered lobby: \"" + text2 + "\" (GUID: " + text + ")")); AddToManualBlocklist(text, text2); ((BaseUnityPlugin)this).Logger.LogWarning((object)("⛔ MANUALLY BLOCKED: \"" + text2 + "\"")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to block lobby: " + ex.Message)); } } private void LoadBlocklists() { try { string[] array; if (File.Exists(autoBlocklistPath)) { array = File.ReadAllLines(autoBlocklistPath); foreach (string text in array) { if (string.IsNullOrWhiteSpace(text)) { continue; } string[] array2 = text.Split(new string[1] { " | " }, StringSplitOptions.None); string text2 = array2[0].Trim(); if (!string.IsNullOrEmpty(text2)) { autoBlocklist.Add(text2); if (array2.Length > 1) { autoBlocklistNames[text2] = array2[1].Trim(); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {autoBlocklist.Count} auto-blocked lobbies"); } if (!File.Exists(manualBlocklistPath)) { return; } array = File.ReadAllLines(manualBlocklistPath); foreach (string text3 in array) { if (string.IsNullOrWhiteSpace(text3)) { continue; } string[] array3 = text3.Split(new string[1] { " | " }, StringSplitOptions.None); string text4 = array3[0].Trim(); if (!string.IsNullOrEmpty(text4)) { manualBlocklist.Add(text4); if (array3.Length > 1) { manualBlocklistNames[text4] = array3[1].Trim(); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {manualBlocklist.Count} manually blocked lobbies"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Error loading blocklists: " + ex.Message)); } } private void SaveAutoBlocklist() { try { List<string> list = new List<string>(); foreach (string item in autoBlocklist) { if (autoBlocklistNames.ContainsKey(item)) { list.Add(item + " | " + autoBlocklistNames[item]); } else { list.Add(item); } } File.WriteAllLines(autoBlocklistPath, list); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Error saving auto-blocklist: " + ex.Message)); } } private void SaveManualBlocklist() { try { List<string> list = new List<string>(); foreach (string item in manualBlocklist) { if (manualBlocklistNames.ContainsKey(item)) { list.Add(item + " | " + manualBlocklistNames[item]); } else { list.Add(item); } } File.WriteAllLines(manualBlocklistPath, list); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Error saving manual blocklist: " + ex.Message)); } } public static void AddToAutoBlocklist(string identifier, string displayName = null) { if (autoBlocklist.Add(identifier)) { if (!string.IsNullOrEmpty(displayName)) { autoBlocklistNames[identifier] = displayName; ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("✅ Auto-blocked: \"" + displayName + "\" (GUID: " + identifier + ")")); } Instance.SaveAutoBlocklist(); } } public static void AddToManualBlocklist(string identifier, string displayName = null) { if (manualBlocklist.Add(identifier)) { if (!string.IsNullOrEmpty(displayName)) { manualBlocklistNames[identifier] = displayName; ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("✅ Manually blocked: \"" + displayName + "\" (GUID: " + identifier + ")")); } Instance.SaveManualBlocklist(); } } private void PatchMenuPageServerList() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown Type type = AccessTools.TypeByName("MenuPageServerList"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"MenuPageServerList type not found!"); return; } MethodInfo methodInfo = AccessTools.Method(type, "OnRoomListUpdate", (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"OnRoomListUpdate method not found!"); return; } HarmonyMethod val = new HarmonyMethod(typeof(Plugin).GetMethod("OnRoomListUpdate_Prefix", BindingFlags.Static | BindingFlags.NonPublic)); _harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched OnRoomListUpdate"); } private static void OnRoomListUpdate_Prefix(object _roomList) { if (!Instance.enableFilter.Value || !(_roomList is IList list) || list.Count == 0) { return; } int count = list.Count; int num = 0; currentRooms.Clear(); foreach (object item in list) { if (item != null) { string text = AccessTools.Property(item.GetType(), "Name")?.GetValue(item)?.ToString(); if (!string.IsNullOrEmpty(text)) { currentRooms[text] = item; } } } for (int num2 = list.Count - 1; num2 >= 0; num2--) { object obj = list[num2]; if (obj != null && ShouldBlockLobby(obj)) { list.RemoveAt(num2); num++; } } if (num > 0) { int num3 = autoBlocklist.Count + manualBlocklist.Count; ((BaseUnityPlugin)Instance).Logger.LogInfo((object)$"Blocked {num}/{count} lobbies (Auto: {autoBlocklist.Count}, Manual: {manualBlocklist.Count}, Total: {num3})"); } } private static bool ShouldBlockLobby(object room) { Type type = room.GetType(); string text = type.GetProperty("Name")?.GetValue(room)?.ToString(); if (!string.IsNullOrEmpty(text) && (autoBlocklist.Contains(text) || manualBlocklist.Contains(text))) { return true; } PropertyInfo property = type.GetProperty("CustomProperties"); if (property != null && property.GetValue(room) is IDictionary dictionary) { string text2 = null; if (dictionary.Contains("server_name")) { text2 = dictionary["server_name"]?.ToString(); if (!string.IsNullOrEmpty(text2) && (autoBlocklist.Contains(text2) || manualBlocklist.Contains(text2))) { return true; } if (!string.IsNullOrEmpty(text2) && ContainsCyrillic(text2)) { if (!string.IsNullOrEmpty(text)) { AddToAutoBlocklist(text, text2); } return true; } if (!string.IsNullOrEmpty(text2)) { string text3 = text2.ToUpper(); if (text3.Contains(" RU ") || text3.StartsWith("RU ") || text3.EndsWith(" RU") || text3.Contains(" BY ") || text3.StartsWith("BY ") || text3.EndsWith(" BY") || text3.Contains("(RU)") || text3.Contains("(RUS)") || text3.Contains("(BY)") || text3.Contains(" RUS ") || text3.Contains(" RUS)") || text3.Contains("(RUS ") || text3.Contains("RUSSIA") || text3.Contains("BELARUS") || text3.Contains("РУСС") || text3.Contains("РУС")) { if (!string.IsNullOrEmpty(text)) { AddToAutoBlocklist(text, text2); } return true; } } } if (dictionary.Contains("Region")) { string text4 = dictionary["Region"]?.ToString() ?? ""; if (text4.Equals("RU", StringComparison.OrdinalIgnoreCase) || text4.Equals("BY", StringComparison.OrdinalIgnoreCase) || text4.IndexOf("Russia", StringComparison.OrdinalIgnoreCase) >= 0 || text4.IndexOf("Belarus", StringComparison.OrdinalIgnoreCase) >= 0) { if (!string.IsNullOrEmpty(text)) { string displayName = ((!dictionary.Contains("server_name")) ? null : dictionary["server_name"]?.ToString()); AddToAutoBlocklist(text, displayName); } return true; } } } return false; } private static bool ContainsCyrillic(string text) { foreach (char c in text) { if (c >= 'Ѐ' && c <= 'ӿ') { return true; } } return false; } } public static class PluginInfo { public const string PLUGIN_GUID = "REPOLobbyFilter"; public const string PLUGIN_NAME = "REPOLobbyFilter"; public const string PLUGIN_VERSION = "1.1.1"; } }