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";
}
}