Decompiled source of REPOLobbyFilter v1.1.1

REPOLobbyFilter.dll

Decompiled 3 weeks ago
using 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";
	}
}