Decompiled source of BigWalk Public Lobbies v1.5.1

BepInEx/plugins/BigWalkPublicLobbies/BigWalkPublicLobbies.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Epic.OnlineServices;
using Epic.OnlineServices.Lobby;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Threading.Tasks;
using Mirror;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("BigWalkPublicLobbies")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+6bb148ad0526b098035e2faa2825448c8ce6e1a1")]
[assembly: AssemblyProduct("BigWalkPublicLobbies")]
[assembly: AssemblyTitle("BigWalkPublicLobbies")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.0")]
[module: UnverifiableCode]
namespace BigWalkPublicLobbies;

internal static class DebugLog
{
	public static bool Verbose;

	public static void Info(string msg)
	{
		ManualLogSource log = Plugin.Log;
		if (log != null)
		{
			log.LogInfo((object)msg);
		}
	}

	public static void Warn(string msg)
	{
		ManualLogSource log = Plugin.Log;
		if (log != null)
		{
			log.LogWarning((object)msg);
		}
	}

	public static void Error(string msg)
	{
		ManualLogSource log = Plugin.Log;
		if (log != null)
		{
			log.LogError((object)msg);
		}
	}

	public static void Debug(string msg)
	{
		if (Verbose)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogDebug((object)msg);
			}
		}
	}
}
public static class LobbyScanner
{
	private sealed class TaggedSearch
	{
		public Task<List<LobbyInfo>> Task;

		public LobbySearch Search;

		public bool FilterOk;

		public bool Recycled;
	}

	private static readonly Dictionary<string, RoomInfo> Cache = new Dictionary<string, RoomInfo>();

	private const float BatchTimeoutSec = 30f;

	private const uint MaxResults = 200u;

	private const int ResultBudget = 200;

	private static readonly List<TaggedSearch> TaggedTasks = new List<TaggedSearch>();

	private static readonly List<TaggedSearch> OrphanTasks = new List<TaggedSearch>();

	private static float _orphanCheckAt;

	private const int MaxOrphans = 64;

	private static int _taggedTaskCursor;

	private static int _taggedItemCursor;

	private static int _taggedGot;

	private static readonly Stopwatch BatchWatch = new Stopwatch();

	public static void ResetBatch(bool startSearches = true)
	{
		DrainPending("重置");
		TaggedTasks.Clear();
		_taggedTaskCursor = 0;
		_taggedItemCursor = 0;
		_taggedGot = 0;
		BatchWatch.Restart();
		if (startSearches)
		{
			Cache.Clear();
			RoomTag.Clear();
			StartTaggedSearches();
		}
	}

	private static void DrainPending(string reason)
	{
		int num = 0;
		for (int i = _taggedTaskCursor; i < TaggedTasks.Count; i++)
		{
			TaggedSearch taggedSearch = TaggedTasks[i];
			if (taggedSearch.Task == null)
			{
				RecycleEntry(taggedSearch);
			}
			else if (IsUsable(taggedSearch.Task) || ((Task)taggedSearch.Task).IsFaulted || ((Task)taggedSearch.Task).IsCanceled)
			{
				RecycleEntry(taggedSearch);
			}
			else if (OrphanTasks.Count >= 64)
			{
				DebugLog.Warn($"[LobbyScanner] 孤儿表已达上限 {64},放弃回收本条搜索句柄(EOS 句柄泄漏 1 个)");
			}
			else
			{
				OrphanTasks.Add(taggedSearch);
				num++;
			}
		}
		if (num > 0)
		{
			DebugLog.Debug($"[LobbyScanner] {reason}:{num} 个在飞搜索转入孤儿表(完成后补回收句柄)");
		}
	}

	public static void TickOrphanRecycle()
	{
		if (OrphanTasks.Count == 0 || Time.unscaledTime < _orphanCheckAt)
		{
			return;
		}
		_orphanCheckAt = Time.unscaledTime + 0.5f;
		for (int num = OrphanTasks.Count - 1; num >= 0; num--)
		{
			TaggedSearch taggedSearch = OrphanTasks[num];
			Task<List<LobbyInfo>> task = taggedSearch.Task;
			if (task == null || ((Task)task).IsCompleted)
			{
				RecycleEntry(taggedSearch);
				OrphanTasks.RemoveAt(num);
			}
		}
	}

	private static bool IsUsable(Task<List<LobbyInfo>> t)
	{
		if (t != null && ((Task)t).IsCompleted && !((Task)t).IsFaulted)
		{
			return !((Task)t).IsCanceled;
		}
		return false;
	}

	private unsafe static void StartTaggedSearches()
	{
		try
		{
			EOSLobbyManager instance = EOSLobbyManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				DebugLog.Warn("[LobbyScanner] EOSLobbyManager 不可用,本轮无搜索(显示空列表)");
				return;
			}
			int num = 0;
			for (int i = 0; i < ModState.TaggedRounds; i++)
			{
				TaggedSearch entry = new TaggedSearch();
				Task<List<LobbyInfo>> val = instance.FindLobbies(200u, UiDelegates.UaSearch(delegate(LobbySearch search)
				{
					//IL_000d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0012: Unknown result type (might be due to invalid IL or missing references)
					//IL_0019: Unknown result type (might be due to invalid IL or missing references)
					//IL_001b: Invalid comparison between Unknown and I4
					entry.Search = search;
					try
					{
						Result val2 = SetTaggedSearchParameter(search);
						entry.FilterOk = (int)val2 == 0;
						if (!entry.FilterOk)
						{
							DebugLog.Warn("[Tag] 定向搜索 SetParameter 失败(该轮结果丢弃): " + ((object)(*(Result*)(&val2))/*cast due to .constrained prefix*/).ToString());
						}
					}
					catch (Exception ex2)
					{
						DebugLog.Warn("[Tag] 定向搜索 SetParameter 异常(该轮结果丢弃): " + ex2.Message);
					}
				}));
				if (val == null)
				{
					if ((Handle)(object)entry.Search != (Handle)null)
					{
						RecycleEntry(entry);
						DebugLog.Warn("[Tag] 定向搜索发起失败(FindLobbies 返回 null),已回收该轮搜索句柄");
					}
					break;
				}
				entry.Task = val;
				num++;
				TaggedTasks.Add(entry);
			}
			if (num > 0)
			{
				DebugLog.Debug($"[Tag] 已发起 {num} 轮定向搜索(bwpl_open={"1"})");
			}
			else
			{
				DebugLog.Warn("[Tag] 定向搜索发起失败(游戏 FindLobbies 返回 null),本轮无结果");
			}
		}
		catch (Exception ex)
		{
			DebugLog.Warn("[Tag] 定向搜索发起失败: " + ex.Message);
		}
	}

	private static Result SetTaggedSearchParameter(LobbySearch search)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: 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_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		if ((Handle)(object)search == (Handle)null)
		{
			return (Result)38;
		}
		IntPtr intPtr = IntPtr.Zero;
		IntPtr intPtr2 = IntPtr.Zero;
		IntPtr intPtr3 = IntPtr.Zero;
		Result val = (Result)38;
		try
		{
			intPtr = Marshal.StringToHGlobalAnsi("bwpl_open");
			intPtr2 = Marshal.StringToHGlobalAnsi("1");
			AttributeDataInternal structure = new AttributeDataInternal
			{
				m_ApiVersion = 1,
				m_Key = intPtr,
				m_Value = new AttributeDataValueInternal
				{
					m_AsUtf8 = intPtr2
				},
				m_ValueType = (AttributeType)3
			};
			intPtr3 = Marshal.AllocHGlobal(Marshal.SizeOf<AttributeDataInternal>());
			Marshal.StructureToPtr<AttributeDataInternal>(structure, intPtr3, fDeleteOld: false);
			LobbySearchSetParameterOptionsInternal val2 = new LobbySearchSetParameterOptionsInternal
			{
				m_ApiVersion = 1,
				m_Parameter = intPtr3,
				m_ComparisonOp = (ComparisonOp)0
			};
			val = Bindings.EOS_LobbySearch_SetParameter(((Handle)search).InnerHandle, ref val2);
			DebugLog.Debug($"[Tag] 定向搜索 SetParameter → {val}");
			return val;
		}
		finally
		{
			if (intPtr3 != IntPtr.Zero)
			{
				Marshal.FreeHGlobal(intPtr3);
			}
			if (intPtr != IntPtr.Zero)
			{
				Marshal.FreeHGlobal(intPtr);
			}
			if (intPtr2 != IntPtr.Zero)
			{
				Marshal.FreeHGlobal(intPtr2);
			}
		}
	}

	private static RoomInfo ToRoomInfo(LobbyInfo l, bool hasPassword, string password)
	{
		RoomInfo roomInfo = new RoomInfo
		{
			JoinCode = l.joinCode,
			WorldName = l.worldName,
			HostName = l.userName,
			Tag = "1",
			Password = password,
			HasPasswordAttribute = hasPassword
		};
		try
		{
			LobbyDetailsInfo detailsInfo = l.detailsInfo;
			if (detailsInfo != null)
			{
				roomInfo.MaxPlayers = (int)detailsInfo.MaxMembers;
				roomInfo.CurrentPlayers = Math.Max(0, (int)(detailsInfo.MaxMembers - detailsInfo.AvailableSlots));
			}
		}
		catch
		{
		}
		return roomInfo;
	}

	public static bool TryFinishBatch()
	{
		if (TaggedTasks.Count == 0)
		{
			return true;
		}
		if (BatchWatch.Elapsed.TotalSeconds > 30.0)
		{
			DebugLog.Warn($"[LobbyScanner] 批次超时 {30f:F0}s,强制结束(挂起 {TaggedTasks.Count - _taggedTaskCursor} 个定向任务;已处理 {_taggedTaskCursor} 个任务的结果)");
			DrainPending("超时");
			TaggedTasks.Clear();
			_taggedTaskCursor = 0;
			_taggedItemCursor = 0;
			_taggedGot = 0;
			return true;
		}
		for (int i = 0; i < TaggedTasks.Count; i++)
		{
			if (TaggedTasks[i].Task == null || !((Task)TaggedTasks[i].Task).IsCompleted)
			{
				return false;
			}
		}
		if (_taggedTaskCursor == 0 && _taggedItemCursor == 0)
		{
			DebugLog.Debug($"[LobbyScanner] 本批 {TaggedTasks.Count} 个定向搜索全部完成(耗时 {BatchWatch.Elapsed.TotalSeconds:F2}s),开始分帧并入结果");
		}
		int num = 0;
		while (num < 200)
		{
			if (_taggedTaskCursor >= TaggedTasks.Count)
			{
				TaggedTasks.Clear();
				_taggedTaskCursor = 0;
				_taggedItemCursor = 0;
				DebugLog.Debug($"[LobbyScanner] 定向结果全部并入(共 {_taggedGot} 条模组房,句柄已全部回收)");
				return true;
			}
			TaggedSearch taggedSearch = TaggedTasks[_taggedTaskCursor];
			Task<List<LobbyInfo>> task = taggedSearch.Task;
			List<LobbyInfo> val = null;
			if (IsUsable(task))
			{
				try
				{
					val = task.Result;
				}
				catch (Exception ex)
				{
					DebugLog.Warn("处理定向搜索结果失败: " + ex.Message);
				}
			}
			else
			{
				AggregateException exception = ((Task)task).Exception;
				object obj;
				if (exception == null)
				{
					obj = null;
				}
				else
				{
					Exception innerException = ((Exception)exception).InnerException;
					obj = ((innerException != null) ? innerException.Message : null);
				}
				if (obj == null)
				{
					AggregateException exception2 = ((Task)task).Exception;
					obj = ((exception2 != null) ? ((Exception)exception2).Message : null) ?? (((Task)task).IsCanceled ? "已取消" : "未知");
				}
				DebugLog.Debug("[Tag] 定向搜索未成功完成(结果丢弃): " + (string?)obj);
			}
			if (taggedSearch.FilterOk && val != null)
			{
				while (_taggedItemCursor < val.Count && num < 200)
				{
					LobbyInfo val2 = val[_taggedItemCursor++];
					if (val2 != null)
					{
						num++;
						if (RoomTag.HasTag(val2.joinCode))
						{
							RoomTag.TryGetPassword(val2.joinCode, out var hasAttribute, out var password);
							AddOrMerge(ToRoomInfo(val2, hasAttribute, password));
							_taggedGot++;
						}
					}
				}
			}
			if (!taggedSearch.FilterOk || val == null || _taggedItemCursor >= val.Count)
			{
				RecycleEntry(taggedSearch);
				_taggedTaskCursor++;
				_taggedItemCursor = 0;
			}
		}
		return false;
	}

	private static void RecycleEntry(TaggedSearch e)
	{
		if (e == null || e.Recycled)
		{
			return;
		}
		e.Recycled = true;
		try
		{
			Task<List<LobbyInfo>> task = e.Task;
			if (IsUsable(task))
			{
				List<LobbyInfo> result = task.Result;
				if (result != null)
				{
					LobbyInfo val = null;
					try
					{
						EOSLobbyManager instance = EOSLobbyManager.Instance;
						val = ((instance != null) ? instance.CurrentLobbyInfo : null);
					}
					catch
					{
					}
					for (int i = 0; i < result.Count; i++)
					{
						LobbyInfo val2 = result[i];
						if (val2 == null)
						{
							continue;
						}
						try
						{
							if (val != null && ((Il2CppObjectBase)val2).Pointer == ((Il2CppObjectBase)val).Pointer)
							{
								DebugLog.Debug("[Tag] 跳过释放:该结果是游戏当前所在房间(避免 use-after-free)");
								continue;
							}
							LobbyDetails lobbyDetails = val2.lobbyDetails;
							if ((Handle)(object)lobbyDetails != (Handle)null)
							{
								lobbyDetails.Release();
								val2.lobbyDetails = null;
							}
						}
						catch (Exception ex)
						{
							DebugLog.Debug("[Tag] 释放房间详情句柄失败: " + ex.Message);
						}
					}
				}
			}
		}
		catch (Exception ex2)
		{
			DebugLog.Warn("[Tag] 回收结果详情句柄失败(EOS 句柄可能泄漏): " + ex2.Message);
		}
		try
		{
			if ((Handle)(object)e.Search != (Handle)null)
			{
				e.Search.Release();
			}
		}
		catch (Exception ex3)
		{
			DebugLog.Warn("[Tag] 释放搜索句柄失败(EOS 句柄可能泄漏): " + ex3.Message);
		}
		e.Search = null;
	}

	public static List<RoomInfo> GetRoomsSnapshot()
	{
		List<RoomInfo> list = (from r in Cache.Values
			where !string.IsNullOrEmpty(r.JoinCode)
			select new RoomInfo
			{
				JoinCode = r.JoinCode,
				WorldName = r.WorldName,
				HostName = r.HostName,
				CurrentPlayers = r.CurrentPlayers,
				MaxPlayers = r.MaxPlayers,
				Tag = r.Tag,
				Password = r.Password,
				HasPasswordAttribute = r.HasPasswordAttribute
			}).OrderBy<RoomInfo, string>((RoomInfo r) => r.WorldName, StringComparer.OrdinalIgnoreCase).ToList();
		DebugLog.Debug($"[LobbyScanner] 本轮共 {list.Count} 个模组房(批次总耗时 {BatchWatch.Elapsed.TotalSeconds:F2}s)");
		return list;
	}

	private static void AddOrMerge(RoomInfo room)
	{
		if (string.IsNullOrEmpty(room.JoinCode))
		{
			return;
		}
		if (Cache.TryGetValue(room.JoinCode, out var value))
		{
			if (!string.IsNullOrEmpty(room.WorldName))
			{
				value.WorldName = room.WorldName;
			}
			if (!string.IsNullOrEmpty(room.HostName))
			{
				value.HostName = room.HostName;
			}
			value.Tag = room.Tag;
			value.Password = room.Password;
			value.HasPasswordAttribute = room.HasPasswordAttribute;
			if (value.MaxPlayers < 0 && room.MaxPlayers >= 0)
			{
				value.MaxPlayers = room.MaxPlayers;
				value.CurrentPlayers = room.CurrentPlayers;
			}
		}
		else
		{
			Cache[room.JoinCode] = room;
		}
		UiStyle.EnqueueRoomCharsForPrebake(RoomChars(room));
	}

	private static IEnumerable<uint> RoomChars(RoomInfo room)
	{
		if (!string.IsNullOrEmpty(room.WorldName))
		{
			foreach (uint item in UiStyle.CodePoints(room.WorldName))
			{
				yield return item;
			}
		}
		if (!string.IsNullOrEmpty(room.HostName))
		{
			foreach (uint item2 in UiStyle.CodePoints(room.HostName))
			{
				yield return item2;
			}
		}
		if (!room.HasPasswordAttribute || string.IsNullOrEmpty(room.Password))
		{
			yield break;
		}
		foreach (uint item3 in UiStyle.CodePoints(room.Password))
		{
			yield return item3;
		}
	}
}
internal static class ModState
{
	public static int TaggedRounds = 1;

	public static bool EnableRoomTag = true;

	public static bool AutoFillPassword = true;

	public static ConfigEntry<bool> EnableRoomTagEntry;

	public static bool DevMode = false;

	public static PublicLobbyPanel Panel;

	public static TitleMenu TitleMenu;

	public static GameObject StartButtons;
}
internal static class ModText
{
	public enum LangGroup
	{
		Latin,
		Chinese,
		Japanese,
		Korean,
		Russian
	}

	public static int LangIndex = 0;

	private static readonly Dictionary<string, string[]> Texts = new Dictionary<string, string[]>
	{
		{
			"title",
			new string[15]
			{
				"Public Lobbies", "Salons publics", "Stanze pubbliche", "Öffentliche Räume", "Salas públicas", "公开房间", "公開ルーム", "Публичные комнаты", "공개 방", "Salas públicas",
				"Publiczne pokoje", "Genel odalar", "Veřejné místnosti", "Salas públicas", "公開房間"
			}
		},
		{
			"search_ph",
			new string[15]
			{
				"Search: world / host", "Rechercher : monde / hôte", "Cerca: mondo / host", "Suche: Welt / Gastgeber", "Buscar: mundo / anfitrión", "搜索:世界名 / 房主名", "検索:ワールド名 / ホスト名", "Поиск: мир / хост", "검색: 월드명 / 호스트명", "Buscar: mundo / anfitrião",
				"Szukaj: świat / gospodarz", "Ara: dünya / sunucu", "Hledat: svět / hostitel", "Buscar: mundo / anfitrión", "搜尋:世界名稱 / 房主名稱"
			}
		},
		{
			"lang_only_off",
			new string[15]
			{
				"Latin only: OFF", "Latin uniquement: OFF", "Solo latino: OFF", "Nur Latein: AUS", "Solo latín: OFF", "仅中文: 关", "日本語のみ: オフ", "RU только: ВЫКЛ", "한국어만: 꺼짐", "Só latim: OFF",
				"Tylko łacina: WYŁ", "Sadece Latin: KAPALI", "Jen latinka: VYP", "Solo latín: OFF", "僅中文: 關"
			}
		},
		{
			"lang_only_on",
			new string[15]
			{
				"Latin only: ON", "Latin uniquement: ON", "Solo latino: ON", "Nur Latein: AN", "Solo latín: ON", "仅中文: 开", "日本語のみ: オン", "RU только: ВКЛ", "한국어만: 켜짐", "Só latim: ON",
				"Tylko łacina: WŁ", "Sadece Latin: AÇIK", "Jen latinka: ZAP", "Solo latín: ON", "僅中文: 開"
			}
		},
		{
			"refresh",
			new string[15]
			{
				"Refresh", "Actualiser", "Aggiorna", "Aktualisieren", "Actualizar", "刷新", "更新", "Обновить", "새로고침", "Atualizar",
				"Odśwież", "Yenile", "Obnovit", "Actualizar", "重新整理"
			}
		},
		{
			"searching",
			new string[15]
			{
				"Searching…", "Recherche en cours…", "Ricerca in corso…", "Suche läuft…", "Buscando…", "搜索中…", "検索中…", "Поиск…", "검색 중…", "Procurando…",
				"Szukanie…", "Aranıyor…", "Hledání…", "Buscando…", "搜尋中…"
			}
		},
		{
			"searching_base",
			new string[15]
			{
				"Searching", "Recherche en cours", "Ricerca", "Suche", "Buscando", "搜索中", "検索中", "Поиск", "검색 중", "Procurando",
				"Szukanie", "Aranıyor", "Hledání", "Buscando", "搜尋中"
			}
		},
		{
			"loading",
			new string[15]
			{
				"Loading…", "Chargement…", "Caricamento…", "Wird geladen…", "Cargando…", "加载中…", "読み込み中…", "Загрузка…", "불러오는 중…", "Carregando…",
				"Ładowanie…", "Yükleniyor…", "Načítání…", "Cargando…", "載入中…"
			}
		},
		{
			"join",
			new string[15]
			{
				"Join Room", "Rejoindre la salle", "Entra nella stanza", "Raum beitreten", "Unirse a la sala", "加入房间", "ルームに参加", "Войти в комнату", "방 참가", "Entrar na sala",
				"Dołącz do pokoju", "Odaya katıl", "Připojit se", "Unirse a la sala", "加入房間"
			}
		},
		{
			"back",
			new string[15]
			{
				"Back", "Retour", "Indietro", "Zurück", "Volver", "返回", "戻る", "Назад", "뒤로", "Voltar",
				"Wstecz", "Geri", "Zpět", "Volver", "返回"
			}
		},
		{
			"no_rooms",
			new string[15]
			{
				"No rooms found: EOS search may be rate-limited. Please wait a moment and refresh again.", "Aucune salle trouvée : la recherche EOS est peut-être limitée. Attendez un instant puis actualisez.", "Nessuna stanza trovata: la ricerca EOS potrebbe essere limitata. Attendi e aggiorna.", "Keine Räume gefunden: die EOS-Suche ist evtl. limitiert. Bitte warten und aktualisieren.", "No se encontraron salas: la búsqueda de EOS puede estar limitada. Espere y actualice.", "未搜索到房间:可能触发了 EOS 搜索限流,请等待片刻后再刷新。", "ルームが見つかりませんでした。EOS の検索制限の可能性があります。少し待ってから再更新してください。", "Комнаты не найдены: возможно, ограничение поиска EOS. Подождите и обновите.", "방을 찾지 못했습니다. EOS 검색 제한일 수 있습니다. 잠시 후 다시 새로고침하세요.", "Nenhuma sala encontrada: a busca EOS pode estar limitada. Aguarde e atualize.",
				"Nie znaleziono pokoi: wyszukiwanie EOS może być ograniczone. Poczekaj i odśwież.", "Oda bulunamadı: EOS araması sınırlanmış olabilir. Bekleyip yenileyin.", "Žádné místnosti nenalezeny: vyhledávání EOS může být omezeno. Počkejte a obnovte.", "No se encontraron salas: la búsqueda de EOS puede estar limitada. Espere y actualice.", "未搜尋到房間:可能觸發了 EOS 搜尋限流,請稍候再重新整理。"
			}
		},
		{
			"rooms_count",
			new string[15]
			{
				"{0} public rooms (click a card, then Join Room)", "{0} salons publics (cliquez, puis rejoindre)", "{0} stanze pubbliche (clicca, poi entra)", "{0} öffentliche Räume (klicken, dann beitreten)", "{0} salas públicas (clic, luego unirse)", "共 {0} 个公开房间(单击卡片选中,点「加入房间」进房)", "公開ルーム {0} 件(カードをクリックして選択、「ルームに参加」で入室)", "{0} публичных комнат (клик, затем войти)", "공개 방 {0}개 (카드를 클릭하여 선택, '방 참가'로 입장)", "{0} salas públicas (clique, depois entre)",
				"{0} publicznych pokoi (kliknij, potem dołącz)", "{0} genel oda (tıklayın, sonra katılın)", "{0} veřejných místností (klikněte, pak se připojte)", "{0} salas públicas (clic, luego unirse)", "共 {0} 個公開房間(按一下卡片選取,點「加入房間」進房)"
			}
		},
		{
			"selected",
			new string[15]
			{
				"Selected: {0} (code {1}). Press Join Room.", "Sélectionné : {0} (code {1}). Rejoignez.", "Selezionata: {0} (codice {1}). Entra.", "Ausgewählt: {0} (Code {1}). Beitreten.", "Seleccionada: {0} (código {1}). Unirse.", "已选中:{0}(码 {1})。点「加入房间」进房。", "選択中:{0}(コード {1})。「ルームに参加」で入室。", "Выбрано: {0} (код {1}). Войти.", "선택됨: {0} (코드 {1}). '방 참가'를 눌러 입장하세요.", "Selecionada: {0} (código {1}). Entre.",
				"Wybrano: {0} (kod {1}). Dołącz.", "Seçildi: {0} (kod {1}). Katılın.", "Vybráno: {0} (kód {1}). Připojit se.", "Seleccionada: {0} (código {1}). Unirse.", "已選取:{0}(碼 {1})。點「加入房間」進房。"
			}
		},
		{
			"select_first",
			new string[15]
			{
				"Select a room first.", "Sélectionnez d'abord une salle.", "Seleziona prima una stanza.", "Wählen Sie zuerst einen Raum.", "Seleccione una sala primero.", "请先单击选中一个房间。", "先にルームをクリックして選択してください。", "Сначала выберите комнату.", "먼저 방을 클릭하여 선택하세요.", "Selecione uma sala primeiro.",
				"Najpierw wybierz pokój.", "Önce bir oda seçin.", "Nejprve vyberte místnost.", "Seleccione una sala primero.", "請先按一下選取一個房間。"
			}
		},
		{
			"join_failed",
			new string[15]
			{
				"Join failed: {0}", "Échec de la connexion : {0}", "Errore di ingresso: {0}", "Beitritt fehlgeschlagen: {0}", "Error al unirse: {0}", "加入失败:{0}", "参加に失敗しました:{0}", "Ошибка входа: {0}", "참가 실패: {0}", "Falha ao entrar: {0}",
				"Błąd dołączenia: {0}", "Katılma hatası: {0}", "Chyba připojení: {0}", "Error al unirse: {0}", "加入失敗:{0}"
			}
		},
		{
			"showing",
			new string[15]
			{
				"Showing {0}/{1} rooms", "{0}/{1} salons", "{0}/{1} stanze", "{0}/{1} Räume", "{0}/{1} salas", "显示 {0}/{1} 个房间", "{0}/{1} 件表示", "Показано {0}/{1} комнат", "{0}/{1}개 표시", "Exibindo {0}/{1} salas",
				"Pokazano {0}/{1} pokoi", "{0}/{1} oda gösteriliyor", "Zobrazeno {0}/{1} místností", "{0}/{1} salas", "顯示 {0}/{1} 個房間"
			}
		},
		{
			"showing_lang",
			new string[15]
			{
				"Showing {0}/{1} rooms ({2} only {3})", "{0}/{1} salons ({2} seul {3})", "{0}/{1} stanze (solo {2} {3})", "{0}/{1} Räume (nur {2} {3})", "{0}/{1} salas (solo {2} {3})", "显示 {0}/{1} 个房间(仅{2} {3})", "{0}/{1} 件表示({2}のみ {3})", "Показано {0}/{1} комнат (только {2} {3})", "{0}/{1}개 표시 ({2}만 {3})", "{0}/{1} salas (só {2} {3})",
				"{0}/{1} pokoi (tylko {2} {3})", "{0}/{1} oda (sadece {2} {3})", "{0}/{1} místností (jen {2} {3})", "{0}/{1} salas (solo {2} {3})", "顯示 {0}/{1} 個房間(僅{2} {3})"
			}
		},
		{
			"lang_on",
			new string[15]
			{
				"ON", "ON", "ON", "AN", "ON", "开", "オン", "ВКЛ", "켜짐", "ON",
				"WŁ", "AÇIK", "ZAP", "ON", "開"
			}
		},
		{
			"lang_off",
			new string[15]
			{
				"OFF", "OFF", "OFF", "AUS", "OFF", "关", "オフ", "ВЫКЛ", "꺼짐", "OFF",
				"WYŁ", "KAPALI", "VYP", "OFF", "關"
			}
		},
		{
			"lang_group_name",
			new string[15]
			{
				"Latin", "latin", "latino", "Latein", "latín", "中文", "日本語", "RU", "한국어", "latim",
				"łacina", "Latin", "latinka", "latín", "中文"
			}
		},
		{
			"host",
			new string[15]
			{
				"Host: {0}", "Hôte : {0}", "Host: {0}", "Gastgeber: {0}", "Anfitrión: {0}", "房主:{0}", "ホスト:{0}", "Хост: {0}", "호스트: {0}", "Anfitrião: {0}",
				"Gospodarz: {0}", "Sunucu: {0}", "Hostitel: {0}", "Anfitrión: {0}", "房主:{0}"
			}
		},
		{
			"code",
			new string[15]
			{
				"Code {0}", "Code {0}", "Codice {0}", "Code {0}", "Código {0}", "码 {0}", "コード {0}", "Код {0}", "코드 {0}", "Código {0}",
				"Kod {0}", "Kod {0}", "Kód {0}", "Código {0}", "碼 {0}"
			}
		},
		{
			"host_label",
			new string[15]
			{
				"Host: ", "Hôte : ", "Host: ", "Gastgeber: ", "Anfitrión: ", "房主:", "ホスト:", "Хост: ", "호스트: ", "Anfitrião: ",
				"Gospodarz: ", "Sunucu: ", "Hostitel: ", "Anfitrión: ", "房主:"
			}
		},
		{
			"code_label",
			new string[15]
			{
				"Code ", "Code ", "Codice ", "Code ", "Código ", "码 ", "コード ", "Код ", "코드 ", "Código ",
				"Kod ", "Kod ", "Kód ", "Código ", "碼 "
			}
		},
		{
			"unknown_world",
			new string[15]
			{
				"Unnamed World", "Monde sans nom", "Mondo senza nome", "Unbenannte Welt", "Mundo sin nombre", "未命名世界", "名前のないワールド", "Безымянный мир", "이름 없는 월드", "Mundo sem nome",
				"Świat bez nazwy", "Adsız dünya", "Svět beze jména", "Mundo sin nombre", "未命名世界"
			}
		},
		{
			"unknown_host",
			new string[15]
			{
				"Unknown host", "Hôte inconnu", "Host sconosciuto", "Unbekannter Gastgeber", "Anfitrión desconocido", "未知房主", "不明なホスト", "Неизвестный хост", "알 수 없는 호스트", "Anfitrião desconhecido",
				"Nieznany gospodarz", "Bilinmeyen sunucu", "Neznámý hostitel", "Anfitrión desconocido", "未知房主"
			}
		},
		{
			"pw_label",
			new string[15]
			{
				" · Password: ", " · Mot de passe : ", " · Password: ", " · Passwort: ", " · Contraseña: ", " · 密码:", " ・パスワード:", " · Пароль: ", " · 비밀번호: ", " · Senha: ",
				" · Hasło: ", " · Şifre: ", " · Heslo: ", " · Contraseña: ", " · 密碼:"
			}
		},
		{
			"pw_unavailable",
			new string[15]
			{
				"unavailable", "indisponible", "non disponibile", "nicht verfügbar", "no disponible", "不可用", "利用不可", "недоступен", "사용 불가", "indisponível",
				"niedostępne", "kullanılamaz", "nedostupné", "no disponible", "不可用"
			}
		},
		{
			"pw_none",
			new string[15]
			{
				"(none)", "(aucun)", "(nessuna)", "(keins)", "(ninguna)", "(无)", "(なし)", "(нет)", "(없음)", "(nenhuma)",
				"(brak)", "(yok)", "(žádné)", "(ninguna)", "(無)"
			}
		},
		{
			"tag_off",
			new string[15]
			{
				"Room tag: OFF", "Étiquette salle: OFF", "Etichetta stanza: OFF", "Raum-Markierung: AUS", "Etiqueta de sala: OFF", "模组房标签: 关", "ルームタグ: オフ", "Метка комнаты: ВЫКЛ", "방 태그: 꺼짐", "Etiqueta da sala: OFF",
				"Znacznik pokoju: WYŁ", "Oda etiketi: KAPALI", "Značka místnosti: VYP", "Etiqueta de sala: OFF", "模組房標籤: 關"
			}
		},
		{
			"tag_on",
			new string[15]
			{
				"Room tag: ON", "Étiquette salle: ON", "Etichetta stanza: ON", "Raum-Markierung: AN", "Etiqueta de sala: ON", "模组房标签: 开", "ルームタグ: オン", "Метка комнаты: ВКЛ", "방 태그: 켜짐", "Etiqueta da sala: ON",
				"Znacznik pokoju: WŁ", "Oda etiketi: AÇIK", "Značka místnosti: ZAP", "Etiqueta de sala: ON", "模組房標籤: 開"
			}
		},
		{
			"tag_tip",
			new string[15]
			{
				"ON: your room is tagged and its password published (public data, readable by anyone) so players with this mod can join it from the Public Lobbies list. OFF: nothing is published.", "Activé : votre salon est étiqueté et son mot de passe publié (données publiques, lisibles par tous) afin que les joueurs avec ce mod puissent le rejoindre depuis la liste Salons publics. Désactivé : rien n'est publié.", "Attivo: la tua stanza viene etichettata e la sua password pubblicata (dati pubblici, leggibili da tutti) così i giocatori con questa mod possono entrare dall'elenco Stanze pubbliche. Disattivo: nulla viene pubblicato.", "An: Dein Raum wird markiert und sein Passwort veröffentlicht (öffentliche Daten, für jeden lesbar), damit Spieler mit dieser Mod ihn über die Liste Öffentliche Räume beitreten können. Aus: Es wird nichts veröffentlicht.", "Activado: tu sala se etiqueta y su contraseña se publica (datos públicos, legibles por cualquiera) para que los jugadores con este mod puedan unirse desde la lista Salas públicas. Desactivado: no se publica nada.", "开启:你的房间自动打模组房标签并公开当前密码(公开数据,任何人可读),其他装了本模组的玩家可在「公开房间」列表查看并加入。关闭:不发布任何信息。", "オン:作成した部屋にタグを付け、現在のパスワードを公開します(公開データ、誰でも読み取り可能)。このMODを入れたプレイヤーが「公開ルーム」リストから参加できます。オフ:何も公開しません。", "Вкл.: ваша комната помечается и её пароль публикуется (открытые данные, доступны всем), чтобы игроки с этим модом могли войти из списка «Публичные комнаты». Выкл.: ничего не публикуется.", "켜기: 방에 태그가 붙고 현재 비밀번호가 공개됩니다(공개 데이터, 누구나 읽을 수 있음). 이 모드를 설치한 플레이어가 '공개 방' 목록에서 참가할 수 있습니다. 끄기: 아무것도 공개하지 않습니다.", "Ligado: sua sala é marcada e a senha publicada (dados públicos, legíveis por qualquer um) para que jogadores com este mod possam entrar pela lista Salas públicas. Desligado: nada é publicado.",
				"Włączone: twój pokój jest oznaczany, a jego hasło publikowane (dane publiczne, czytelne dla każdego), by gracze z tym modem mogli dołączyć z listy Publiczne pokoje. Wyłączone: nic nie jest publikowane.", "Açık: odanız etiketlenir ve şifresi yayınlanır (herkesin okuyabileceği genel veri); bu modu kuran oyuncular Genel Odalar listesinden katılabilir. Kapalı: hiçbir şey yayınlanmaz.", "Zapnuto: vaše místnost je označena a její heslo zveřejněno (veřejná data, čitelná kýmkoli), aby hráči s tímto modem mohli vstoupit ze seznamu Veřejné místnosti. Vypnuto: nic se nezveřejňuje.", "Activado: tu sala se etiqueta y su contraseña se publica (datos públicos, legibles por cualquiera) para que los jugadores con este mod puedan unirse desde la lista Salas públicas. Desactivado: no se publica nada.", "開啟:你的房間自動加模組房標籤並公開目前密碼(公開資料,任何人可讀),其他安裝了本模組的玩家可在「公開房間」列表查看並加入。關閉:不發布任何資訊。"
			}
		}
	};

	private static readonly HashSet<string> _warnedKeys = new HashSet<string>();

	private static readonly HashSet<char> SimplifiedOnlyChars = new HashSet<char>(new char[81]
	{
		'们', '这', '说', '时', '还', '个', '么', '让', '进', '过',
		'边', '给', '现', '从', '对', '车', '东', '买', '长', '问',
		'没', '谁', '两', '写', '习', '汉', '话', '语', '读', '书',
		'电', '视', '脑', '动', '开', '关', '门', '间', '场', '块',
		'环', '压', '厌', '历', '厅', '发', '马', '鱼', '鸟', '广',
		'龙', '你', '无', '兴', '办', '帮', '师', '帅', '卫', '业',
		'乡', '头', '欢', '乐', '齐', '华', '丽', '亲', '贝', '见',
		'页', '风', '飞', '银', '红', '蓝', '绿', '键', '码', '号',
		'酱'
	});

	private static readonly HashSet<char> JapaneseOnlyKanji = new HashSet<char>(new char[51]
	{
		'込', '辻', '峠', '畑', '働', '凪', '凧', '匂', '俣', '俵',
		'栃', '榊', '畠', '蛍', '噂', '桜', '広', '図', '価', '拡',
		'囲', '渋', '沢', '縦', '続', '検', '権', '険', '緑', '訳',
		'鉄', '歩', '辺', '売', '駅', '竜', '暦', '齢', '円', '児',
		'毎', '対', '変', '帰', '帯', '斎', '壊', '拠', '処', '従',
		'塀'
	});

	private static bool _init;

	private static TMP_Text _cachedSettingsTmp;

	private static LocalizationManager _cachedLm;

	private static float _nextPollTime;

	public static LangGroup CurrentGroup
	{
		get
		{
			switch (LangIndex)
			{
			case 5:
			case 14:
				return LangGroup.Chinese;
			case 6:
				return LangGroup.Japanese;
			case 7:
				return LangGroup.Russian;
			case 8:
				return LangGroup.Korean;
			default:
				return LangGroup.Latin;
			}
		}
	}

	public static string T(string key)
	{
		if (Texts.TryGetValue(key, out var value))
		{
			if (value.Length != 15 && _warnedKeys.Add(key))
			{
				DebugLog.Warn($"[本地化] 文案 key '{key}' 数组长度 {value.Length} != 15,缺失语言将按末项截断");
			}
			return value[Math.Min(LangIndex, value.Length - 1)];
		}
		DebugLog.Warn("[本地化] 未找到文案 key: " + key);
		return key;
	}

	public static string T(string key, params object[] args)
	{
		try
		{
			return string.Format(T(key), args);
		}
		catch (FormatException)
		{
			return T(key);
		}
	}

	public static bool RoomInGroup(string worldName, string hostName)
	{
		switch (CurrentGroup)
		{
		case LangGroup.Chinese:
			if (IsNonChineseLike(worldName))
			{
				return false;
			}
			if (IsChineseLike(worldName))
			{
				return true;
			}
			if (IsNonChineseLike(hostName))
			{
				return false;
			}
			return IsChineseLike(hostName);
		case LangGroup.Japanese:
			if (!HasJapaneseSignal(worldName) || HasSimplifiedOnly(worldName))
			{
				if (HasJapaneseSignal(hostName))
				{
					return !HasSimplifiedOnly(hostName);
				}
				return false;
			}
			return true;
		default:
			if (!InGroup(worldName))
			{
				return InGroup(hostName);
			}
			return true;
		}
	}

	private static bool IsNonChineseLike(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return false;
		}
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		bool flag = false;
		bool flag2 = false;
		bool flag3 = false;
		foreach (char c in s)
		{
			if (IsHiragana(c))
			{
				num++;
			}
			else if (IsKatakana(c))
			{
				num2++;
			}
			else if (IsJapaneseMark(c))
			{
				flag = true;
			}
			else if (JapaneseOnlyKanji.Contains(c))
			{
				flag2 = true;
			}
			else if (SimplifiedOnlyChars.Contains(c))
			{
				num3++;
			}
			else if (IsHangulChar(c))
			{
				flag3 = true;
			}
		}
		if (flag3)
		{
			return true;
		}
		if (num3 >= 2)
		{
			return false;
		}
		if (num2 >= 1 || flag || flag2)
		{
			return true;
		}
		if (num >= 2)
		{
			return true;
		}
		if (num == 1 && num3 == 0)
		{
			return true;
		}
		return false;
	}

	private static bool IsChineseLike(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return false;
		}
		foreach (char c in s)
		{
			if (SimplifiedOnlyChars.Contains(c) || IsHanChar(c))
			{
				return true;
			}
		}
		return false;
	}

	private static bool HasJapaneseSignal(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return false;
		}
		foreach (char c in s)
		{
			if (IsHiragana(c) || IsKatakana(c) || IsJapaneseMark(c) || JapaneseOnlyKanji.Contains(c))
			{
				return true;
			}
		}
		return false;
	}

	private static bool HasSimplifiedOnly(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return false;
		}
		foreach (char item in s)
		{
			if (SimplifiedOnlyChars.Contains(item))
			{
				return true;
			}
		}
		return false;
	}

	private static bool IsHiragana(char c)
	{
		if (c >= '\u3040')
		{
			return c <= 'ゟ';
		}
		return false;
	}

	private static bool IsKatakana(char c)
	{
		if ((c < '゠' || c > 'ヿ') && (c < '・' || c > '゚'))
		{
			if (c >= 'ㇰ')
			{
				return c <= 'ㇿ';
			}
			return false;
		}
		return true;
	}

	private static bool IsJapaneseMark(char c)
	{
		if (c != '々' && c != '〆' && c != '〻' && c != '〼')
		{
			return c == '〽';
		}
		return true;
	}

	private static bool IsHangulChar(char c)
	{
		if ((c < '가' || c > '힣') && (c < 'ᄀ' || c > 'ᇿ'))
		{
			if (c >= '\u3130')
			{
				return c <= '\u318f';
			}
			return false;
		}
		return true;
	}

	private static bool IsHanChar(char c)
	{
		if ((c < '㐀' || c > '䶿') && (c < '一' || c > '鿿'))
		{
			if (c >= '豈')
			{
				return c <= '\ufaff';
			}
			return false;
		}
		return true;
	}

	private static bool InGroup(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return false;
		}
		for (int i = 0; i < s.Length; i++)
		{
			if (MatchGroup(s[i]))
			{
				return true;
			}
		}
		return false;
	}

	private static bool MatchGroup(char c)
	{
		switch (CurrentGroup)
		{
		case LangGroup.Korean:
			if ((c < '가' || c > '힣') && (c < 'ᄀ' || c > 'ᇿ'))
			{
				if (c >= '\u3130')
				{
					return c <= '\u318f';
				}
				return false;
			}
			return true;
		case LangGroup.Russian:
			if (c >= 'Ѐ')
			{
				return c <= 'ӿ';
			}
			return false;
		default:
			if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
			{
				if (c >= 'À')
				{
					return c <= 'ɏ';
				}
				return false;
			}
			return true;
		}
	}

	public static bool Poll()
	{
		if (Time.time < _nextPollTime)
		{
			return false;
		}
		_nextPollTime = Time.time + 1f;
		int num = DetectLangIndex();
		if (!_init || num != LangIndex)
		{
			_init = true;
			LangIndex = num;
			DebugLog.Info($"[本地化] 游戏语言 → 索引 {num}");
			return true;
		}
		return false;
	}

	private static int DetectLangIndex()
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected I4, but got Unknown
		try
		{
			if (_cachedLm == null)
			{
				_cachedLm = LocalizationManager.instance;
			}
			if (_cachedLm != null)
			{
				int num = (int)_cachedLm.currentLanguage;
				if (num >= 1 && num <= 15)
				{
					return num - 1;
				}
			}
		}
		catch (Exception)
		{
			_cachedLm = null;
		}
		try
		{
			if ((Object)(object)ModState.TitleMenu == (Object)null)
			{
				return LangIndex;
			}
			if ((Object)(object)_cachedSettingsTmp == (Object)null)
			{
				Transform val = FindChildByName(((Component)ModState.TitleMenu).transform, "SettingsButton");
				if ((Object)(object)val == (Object)null)
				{
					return LangIndex;
				}
				_cachedSettingsTmp = ((Component)val).GetComponentInChildren<TMP_Text>(true);
				if ((Object)(object)_cachedSettingsTmp == (Object)null)
				{
					return LangIndex;
				}
			}
			string text = _cachedSettingsTmp.text;
			if (ContainsRange(text, '\u3040', 'ヿ'))
			{
				return 6;
			}
			if (ContainsRange(text, '가', '\ud7af'))
			{
				return 8;
			}
			if (ContainsHan(text))
			{
				return 5;
			}
			return 0;
		}
		catch (Exception)
		{
			_cachedSettingsTmp = null;
			return LangIndex;
		}
	}

	private static bool ContainsHan(string s)
	{
		foreach (char c in s)
		{
			if (c >= '一' && c <= '鿿')
			{
				return true;
			}
		}
		return false;
	}

	private static bool ContainsRange(string s, char lo, char hi)
	{
		foreach (char c in s)
		{
			if (c >= lo && c <= hi)
			{
				return true;
			}
		}
		return false;
	}

	internal static Transform FindChildByName(Transform root, string name)
	{
		if ((Object)(object)root == (Object)null)
		{
			return null;
		}
		if (((Object)root).name == name)
		{
			return root;
		}
		int childCount = root.childCount;
		for (int i = 0; i < childCount; i++)
		{
			Transform val = FindChildByName(root.GetChild(i), name);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return null;
	}
}
internal static class PasswordAutoFill
{
	private const float ArmedSeconds = 30f;

	private static string _password;

	private static string _joinCode;

	private static float _expireAt;

	public static void Apply(Harmony harmony)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		try
		{
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(PasswordMenu), "OnEnable", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				DebugLog.Warn("[加入] 找不到 PasswordMenu.OnEnable,自动填入密码不可用(仍可手动输入)");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PasswordAutoFill).GetMethod("PasswordMenuEnabledPostfix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			DebugLog.Info("[加入] 已 patch PasswordMenu.OnEnable(公开密码自动填入)");
		}
		catch (Exception ex)
		{
			DebugLog.Error("[加入] 自动填入密码 patch 失败: " + ex);
		}
	}

	public static void Arm(string joinCode, string password)
	{
		if (!ModState.AutoFillPassword || string.IsNullOrEmpty(password))
		{
			Clear();
			return;
		}
		_password = password;
		_joinCode = joinCode;
		_expireAt = Time.realtimeSinceStartup + 30f;
		DebugLog.Debug("[加入] 已预置房间 " + joinCode + " 的公开密码,若弹出密码页将自动填入(值不记录)");
	}

	public static void Clear()
	{
		_password = null;
		_joinCode = null;
		_expireAt = 0f;
	}

	public static void PasswordMenuEnabledPostfix(PasswordMenu __instance)
	{
		try
		{
			if (_password == null)
			{
				return;
			}
			if (!ModState.AutoFillPassword)
			{
				Clear();
				return;
			}
			if (Time.realtimeSinceStartup > _expireAt)
			{
				DebugLog.Debug("[加入] 预置密码已超时作废(目标 " + _joinCode + "),密码页保持空白");
				Clear();
				return;
			}
			if (!TargetMatches())
			{
				DebugLog.Debug("[加入] 当前连接目标与预置房间 " + _joinCode + " 不一致,作废预置(密码页保持空白)");
				Clear();
				return;
			}
			string password = _password;
			string joinCode = _joinCode;
			Clear();
			TMP_InputField val = (((Object)(object)__instance != (Object)null) ? __instance.passwordField : null);
			if ((Object)(object)val == (Object)null)
			{
				DebugLog.Warn("[加入] 密码页输入框不可用,未能自动填入(请手动输入)");
				return;
			}
			val.SetText(password, true);
			try
			{
				val.caretPosition = password.Length;
				val.stringPosition = password.Length;
			}
			catch (Exception ex)
			{
				DebugLog.Debug("[加入] 设置光标位置失败(不影响已填入的密码): " + ex.Message);
			}
			DebugLog.Info("[加入] 密码页已自动填入房间 " + joinCode + " 的公开密码(值不记录),请点确认加入");
		}
		catch (Exception ex2)
		{
			DebugLog.Warn("[加入] 自动填入密码失败(请手动输入): " + ex2.Message);
		}
	}

	private static bool TargetMatches()
	{
		try
		{
			NetworkManager singleton = NetworkManager.singleton;
			if ((Object)(object)singleton == (Object)null)
			{
				return true;
			}
			string networkAddress = singleton.networkAddress;
			if (string.IsNullOrEmpty(networkAddress) || string.IsNullOrEmpty(_joinCode))
			{
				return true;
			}
			return string.Equals(networkAddress.Trim(), _joinCode.Trim(), StringComparison.OrdinalIgnoreCase);
		}
		catch (Exception ex)
		{
			DebugLog.Debug("[加入] 读取当前连接目标失败(按预置继续): " + ex.Message);
			return true;
		}
	}
}
[BepInPlugin("com.bigwalk.publiclobbies", "BigWalk Public Lobbies", "1.5.1")]
public class Plugin : BasePlugin
{
	public const string GUID = "com.bigwalk.publiclobbies";

	public const string Name = "BigWalk Public Lobbies";

	public const string Version = "1.5.1";

	internal static ManualLogSource Log;

	private Harmony _harmony;

	public override void Load()
	{
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Expected O, but got Unknown
		Log = ((BasePlugin)this).Log;
		DebugLog.Verbose = ((BasePlugin)this).Config.Bind<bool>("Debug", "Verbose", false, "打印详细诊断日志(EOS 搜索、UI 结构探测、布局等)").Value;
		ConfigEntry<int> taggedRounds = ((BasePlugin)this).Config.Bind<int>("Search", "TaggedRounds", 1, "一次刷新执行的定向搜索(模组房 bwpl_open)轮数:单轮上限 200,模组房总数远小于 200,1 轮即全量,多轮仅容错;0 = 关闭搜索(不发任何请求,立即显示空列表);最大 10(过大会单帧连发触发 EOS 限流)");
		ModState.TaggedRounds = NormalizeTaggedRounds(taggedRounds.Value);
		taggedRounds.SettingChanged += delegate
		{
			ModState.TaggedRounds = NormalizeTaggedRounds(taggedRounds.Value);
		};
		ModState.EnableRoomTagEntry = ((BasePlugin)this).Config.Bind<bool>("Mod", "EnableRoomTag", true, "建房时自动给房间打模组房标签并公开当前房间密码(EOS Lobby 公开属性 bwpl_open=\"1\" / bwpl_password,永久固定常量):开启后其他装了本模组的玩家可在「公开房间」列表看到你的房间及其密码(密码是公开元数据,任何搜索者可读,仅方便手动输入,加入仍需原版密码验证)。游戏内改密码会自动同步到列表。关闭后:之后创建的房间不再打标签,改密也不再同步;已发布到当前房间的属性不会追溯移除(重开房间即消失)");
		ModState.EnableRoomTag = ModState.EnableRoomTagEntry.Value;
		ModState.EnableRoomTagEntry.SettingChanged += delegate
		{
			ModState.EnableRoomTag = ModState.EnableRoomTagEntry.Value;
		};
		ConfigEntry<bool> devMode = ((BasePlugin)this).Config.Bind<bool>("Dev", "DevMode", false, "开发者选项:开启后在任意场景按 F5 打开/关闭「公开房间」,用于测试自己的房间是否被识别为模组房");
		ModState.DevMode = devMode.Value;
		devMode.SettingChanged += delegate
		{
			ModState.DevMode = devMode.Value;
		};
		ConfigEntry<bool> autoFill = ((BasePlugin)this).Config.Bind<bool>("Join", "AutoFillPassword", true, "从「公开房间」列表加入带公开密码(bwpl_password)的房间时,自动把该密码填进原版密码页:只填入不提交,仍由你点原版确认按钮加入(可先核对/修改)。密码本就显示在列表里,此项仅省去手动输入;认证流程与原版完全一致。关闭后密码页保持空白,需手动输入");
		ModState.AutoFillPassword = autoFill.Value;
		autoFill.SettingChanged += delegate
		{
			ModState.AutoFillPassword = autoFill.Value;
		};
		((BasePlugin)this).AddComponent<PublicLobbyMod>();
		try
		{
			_harmony = new Harmony("com.bigwalk.publiclobbies");
			RoomTagPatch.Apply(_harmony);
			PasswordAutoFill.Apply(_harmony);
		}
		catch (Exception ex)
		{
			DebugLog.Error("Harmony 初始化失败: " + ex);
		}
		ModState.Panel = new PublicLobbyPanel();
		ModState.Panel.Hide();
		DebugLog.Info($"{"BigWalk Public Lobbies"} v{"1.5.1"} 已加载(Verbose 日志={DebugLog.Verbose})。");
	}

	public override bool Unload()
	{
		try
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			PasswordAutoFill.Clear();
			if (ModState.Panel != null)
			{
				ModState.Panel.Destroy();
			}
			return true;
		}
		catch (Exception ex)
		{
			DebugLog.Warn("Unload 清理失败: " + ex.Message);
			return false;
		}
	}

	private static int NormalizeTaggedRounds(int value)
	{
		if (value >= 0)
		{
			if (value <= 10)
			{
				return value;
			}
			return 10;
		}
		return 0;
	}
}
public class PublicLobbyMod : MonoBehaviour
{
	private TitleMenu _titleMenu;

	private bool _buttonInjected;

	private GameObject _buttonTemplate;

	private GameObject _injectedButton;

	private TMP_Text _injectedTmp;

	private float _titleRetryTime;

	private float _templateRetryTime;

	private GameObject _hostSwitchGo;

	private readonly List<TMP_Text> _hostSwitchTmps = new List<TMP_Text>();

	private readonly List<TMP_Text> _hostTipTmps = new List<TMP_Text>();

	private bool _hostSwitchInjected;

	private bool _hostSwitchSelInjected;

	private bool _hostSwitchHcInjected;

	private float _hostSwitchLogTime;

	private float _hostSwitchFindTime;

	private bool _fontKickDone;

	private void Update()
	{
		if (!_fontKickDone)
		{
			_fontKickDone = true;
			UiStyle.EnsureFallbackFonts();
		}
		if (ModText.Poll())
		{
			try
			{
				UiStyle.RefreshFontMapping();
				UiStyle.EnsureFallbackFonts();
				ModState.Panel?.RefreshAllTexts(markCardDirty: true);
				RefreshMainButtonText();
				RefreshHostSwitchText();
			}
			catch (Exception ex)
			{
				DebugLog.Warn("[UI] 语言切换刷新异常: " + ex.Message);
			}
		}
		UiStyle.TickSpecialFontCreation();
		UiStyle.TickGlyphPreload();
		if (ModState.Panel != null)
		{
			ModState.Panel.Tick();
		}
		LobbyScanner.TickOrphanRecycle();
		RoomPasswordSync.Tick();
		if (ModState.Panel != null && ModState.Panel.IsShown && Input.GetKeyDown((KeyCode)27))
		{
			ModState.Panel.Hide();
		}
		if (ModState.DevMode && Input.GetKeyDown((KeyCode)286))
		{
			if (ModState.Panel != null && ModState.Panel.IsShown)
			{
				DebugLog.Info("[模组] 开发者模式:F5 关闭公开房间");
				ModState.Panel.Hide();
			}
			else
			{
				DebugLog.Info("[模组] 开发者模式:F5 打开公开房间");
				ModState.Panel?.Show();
			}
		}
		UpdateHostSwitch();
		if (_buttonInjected)
		{
			if ((Object)(object)_injectedButton == (Object)null)
			{
				DebugLog.Info("[模组] 公开房间按钮已被销毁(场景切换),等待重新注入");
				_buttonInjected = false;
				_injectedTmp = null;
				UiStyle.ButtonTextTemplate = null;
			}
			return;
		}
		if ((Object)(object)_titleMenu == (Object)null)
		{
			if (Time.time < _titleRetryTime)
			{
				return;
			}
			_titleRetryTime = Time.time + 0.5f;
			_titleMenu = Object.FindObjectOfType<TitleMenu>();
			if ((Object)(object)_titleMenu == (Object)null)
			{
				ModState.TitleMenu = null;
				ModState.StartButtons = null;
				return;
			}
		}
		ModState.TitleMenu = _titleMenu;
		if ((Object)(object)ModState.StartButtons == (Object)null)
		{
			Transform val = ModText.FindChildByName(((Component)_titleMenu).transform, "StartButtons");
			if ((Object)(object)val != (Object)null)
			{
				ModState.StartButtons = ((Component)val).gameObject;
				DebugLog.Debug("[页面] 找到主菜单按钮容器 StartButtons");
			}
		}
		if ((Object)(object)_buttonTemplate == (Object)null)
		{
			if (Time.time < _templateRetryTime)
			{
				return;
			}
			_templateRetryTime = Time.time + 1f;
			try
			{
				_buttonTemplate = FindJoinMenuButton(_titleMenu);
			}
			catch (Exception ex2)
			{
				DebugLog.Warn("[模组] 查找「加入游戏」模板按钮失败: " + ex2.Message);
				_buttonTemplate = null;
			}
		}
		if (!((Object)(object)_buttonTemplate == (Object)null))
		{
			TryInjectButton();
		}
	}

	private static GameObject FindJoinMenuButton(TitleMenu titleMenu)
	{
		Il2CppArrayBase<Button> componentsInChildren = ((Component)titleMenu).GetComponentsInChildren<Button>(true);
		foreach (Button item in componentsInChildren)
		{
			try
			{
				if (item.onClick != null && ((UnityEventBase)item.onClick).GetPersistentEventCount() != 0 && ((UnityEventBase)item.onClick).GetPersistentMethodName(0) == "GoToJoinMenu")
				{
					Object persistentTarget = ((UnityEventBase)item.onClick).GetPersistentTarget(0);
					if (persistentTarget != (Object)null && persistentTarget is TitleMenu)
					{
						return ((Component)item).gameObject;
					}
				}
			}
			catch (Exception ex)
			{
				string text = (((Object)(object)item != (Object)null) ? ((Object)item).name : "?");
				DebugLog.Debug("[模组] 读取按钮持久事件失败(" + text + "): " + ex.Message);
			}
		}
		foreach (Button item2 in componentsInChildren)
		{
			if (((Component)item2).gameObject.activeSelf)
			{
				return ((Component)item2).gameObject;
			}
		}
		return null;
	}

	private void TryInjectButton()
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		UiStyle.Capture(_buttonTemplate);
		GameObject val = UiStyle.CreateTextButton(((Component)_titleMenu).transform, "PublicLobbiesButton", ModText.T("title"), 26f, UiStyle.TextNormal, delegate
		{
			ModState.Panel.Show();
		});
		if ((Object)(object)val == (Object)null)
		{
			DebugLog.Error("创建主菜单「公开房间」按钮失败");
			return;
		}
		_injectedButton = val;
		_injectedTmp = val.GetComponentInChildren<TMP_Text>(true);
		if ((Object)(object)_injectedTmp != (Object)null)
		{
			UiStyle.ButtonTextTemplate = ((Component)_injectedTmp).gameObject;
		}
		RectTransform component = val.GetComponent<RectTransform>();
		if ((Object)(object)component != (Object)null)
		{
			component.anchorMin = new Vector2(1f, 0.5f);
			component.anchorMax = new Vector2(1f, 0.5f);
			component.pivot = new Vector2(1f, 0.5f);
			component.sizeDelta = new Vector2(320f, 76f);
			component.anchoredPosition = new Vector2(-230f, -60f);
		}
		_buttonInjected = true;
		DebugLog.Info("[模组] 已注入「公开房间」按钮(右侧,挂在 TitleMenu 下)");
	}

	private void RefreshMainButtonText()
	{
		if ((Object)(object)_injectedTmp != (Object)null)
		{
			UiStyle.ApplyFont(_injectedTmp);
			_injectedTmp.text = ModText.T("title");
		}
	}

	private void UpdateHostSwitch()
	{
		if ((Object)(object)_titleMenu == (Object)null)
		{
			return;
		}
		if ((Object)(object)_hostSwitchGo == (Object)null)
		{
			_hostSwitchInjected = false;
			_hostSwitchSelInjected = false;
			_hostSwitchHcInjected = false;
			_hostSwitchTmps.Clear();
			_hostTipTmps.Clear();
		}
		if (_hostSwitchInjected)
		{
			return;
		}
		if ((Object)(object)UiStyle.ButtonTextTemplate == (Object)null)
		{
			if ((Object)(object)UiStyle.Font == (Object)null)
			{
				UiStyle.Capture(null);
			}
		}
		else
		{
			if (Time.time < _hostSwitchFindTime)
			{
				return;
			}
			_hostSwitchFindTime = Time.time + 1f;
			try
			{
				HostMenuSelect val = Object.FindObjectOfType<HostMenuSelect>(true);
				HostMenuConfirm val2 = Object.FindObjectOfType<HostMenuConfirm>(true);
				if ((Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null)
				{
					if (Time.time > _hostSwitchLogTime)
					{
						_hostSwitchLogTime = Time.time + 10f;
						DebugLog.Warn("[模组] 未找到 HostMenuSelect/HostMenuConfirm(主菜单未就绪?),等待重试");
					}
					return;
				}
				if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null && !_hostSwitchSelInjected)
				{
					InjectHostSwitch(((Component)val).transform);
					_hostSwitchSelInjected = true;
				}
				if ((Object)(object)val2 != (Object)null && (Object)(object)((Component)val2).gameObject != (Object)null && !_hostSwitchHcInjected)
				{
					InjectHostSwitch(((Component)val2).transform);
					_hostSwitchHcInjected = true;
				}
				_hostSwitchInjected = _hostSwitchSelInjected && _hostSwitchHcInjected;
				if (_hostSwitchInjected)
				{
					DebugLog.Info("[模组] 已在「建立主机」页(选择页+确认页)左侧注入模组房标签开关");
				}
			}
			catch (Exception ex)
			{
				DebugLog.Warn("[模组] 注入「建立主机」标签开关失败: " + ex.Message);
			}
		}
	}

	private void InjectHostSwitch(Transform parent)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: 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_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = UiStyle.CreateOverlayButton(parent, "RoomTagToggle", ToggleRoomTag);
		if (!((Object)(object)val == (Object)null))
		{
			RectTransform component = val.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.anchorMin = new Vector2(0.03f, 0.52f);
				component.anchorMax = new Vector2(0.17f, 0.58f);
				component.offsetMin = Vector2.zero;
				component.offsetMax = Vector2.zero;
				component.pivot = new Vector2(0.5f, 0.5f);
			}
			TMP_Text val2 = UiStyle.OverlayLabel(val, ModText.T(ModState.EnableRoomTag ? "tag_on" : "tag_off"), 16f, UiStyle.TextNormal);
			if ((Object)(object)val2 != (Object)null)
			{
				_hostSwitchTmps.Add(val2);
			}
			else
			{
				DebugLog.Warn("[模组] host 开关标签文本创建失败(OverlayLabel 返回 null)");
			}
			TMP_Text val3 = UiStyle.CreateLabel("RoomTagTip", parent, ModText.T("tag_tip"), 30f, (TextAlignmentOptions)513, new Color(0.12f, 0.14f, 0.18f, 1f), allowWrap: true);
			if ((Object)(object)val3 == (Object)null)
			{
				DebugLog.Warn("[模组] host 开关提示文本创建失败(CreateLabel 返回 null)");
			}
			else
			{
				val3.overflowMode = (TextOverflowModes)0;
				RectTransform rectTransform = val3.rectTransform;
				rectTransform.anchorMin = new Vector2(0.03f, 0.3f);
				rectTransform.anchorMax = new Vector2(0.3f, 0.52f);
				rectTransform.offsetMin = Vector2.zero;
				rectTransform.offsetMax = Vector2.zero;
				_hostTipTmps.Add(val3);
			}
			if ((Object)(object)_hostSwitchGo == (Object)null)
			{
				_hostSwitchGo = val;
			}
			DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(31, 3);
			defaultInterpolatedStringHandler.AppendLiteral("[模组] host 开关已注入:标签文本=");
			defaultInterpolatedStringHandler.AppendFormatted((Object)(object)val2 != (Object)null);
			defaultInterpolatedStringHandler.AppendLiteral(",提示文本=");
			defaultInterpolatedStringHandler.AppendFormatted((Object)(object)val3 != (Object)null);
			defaultInterpolatedStringHandler.AppendLiteral(",字体=");
			TMP_FontAsset font = UiStyle.Font;
			defaultInterpolatedStringHandler.AppendFormatted(((font != null) ? ((Object)font).name : null) ?? "null");
			DebugLog.Debug(defaultInterpolatedStringHandler.ToStringAndClear());
		}
	}

	private void ToggleRoomTag()
	{
		ModState.EnableRoomTag = !ModState.EnableRoomTag;
		if (ModState.EnableRoomTagEntry != null)
		{
			ModState.EnableRoomTagEntry.Value = ModState.EnableRoomTag;
		}
		RefreshHostSwitchText();
		DebugLog.Info($"[模组] 模组房标签开关 → {ModState.EnableRoomTag}");
	}

	private void RefreshHostSwitchText()
	{
		string text = ModText.T(ModState.EnableRoomTag ? "tag_on" : "tag_off");
		string text2 = ModText.T("tag_tip");
		for (int i = 0; i < _hostSwitchTmps.Count; i++)
		{
			if ((Object)(object)_hostSwitchTmps[i] != (Object)null)
			{
				UiStyle.ApplyFont(_hostSwitchTmps[i]);
				_hostSwitchTmps[i].text = text;
			}
		}
		for (int j = 0; j < _hostTipTmps.Count; j++)
		{
			if ((Object)(object)_hostTipTmps[j] != (Object)null)
			{
				UiStyle.ApplyFont(_hostTipTmps[j]);
				_hostTipTmps[j].text = text2;
			}
		}
	}
}
public class PublicLobbyPanel
{
	private sealed class CardSlot
	{
		public int Index = -1;

		public RoomInfo Room;

		public readonly RectTransform Rect;

		public readonly Image Image;

		public readonly Button Button;

		public readonly TMP_Text NameT;

		public readonly TMP_Text HostT;

		public readonly TMP_Text MetaT;

		private readonly PublicLobbyPanel _panel;

		public CardSlot(PublicLobbyPanel panel, RectTransform rect, Image image, Button button, TMP_Text nameT, TMP_Text hostT, TMP_Text metaT)
		{
			_panel = panel;
			Rect = rect;
			Image = image;
			Button = button;
			NameT = nameT;
			HostT = hostT;
			MetaT = metaT;
		}

		public void OnClick()
		{
			if (_panel != null && Room != null)
			{
				_panel.SelectRoom(Room);
			}
		}
	}

	private GameObject _root;

	private RectTransform _content;

	private TMP_InputField _searchInput;

	private TMP_Text _statusText;

	private TMP_Text _titleTmp;

	private TMP_Text _langOnlyTmp;

	private TMP_Text _refreshTmp;

	private TMP_Text _joinTmp;

	private TMP_Text _backTmp;

	private TMP_Text _searchPh;

	private TMP_Text _searchingText;

	private string _statusKey;

	private object[] _statusArgs;

	private GameObject _joinButton;

	private GameObject _langOnlyButton;

	private GameObject _windowGo;

	private Scrollbar _scrollbarCmp;

	private ScrollRect _scrollRect;

	private RectTransform _viewportRt;

	private readonly List<RoomInfo> _rooms = new List<RoomInfo>();

	private string _lastSelectedKey;

	private readonly List<GameObject> _hiddenMenuButtons = new List<GameObject>();

	private string _filter = "";

	private bool _langOnly;

	private int _activeRoomCount;

	private bool _scanning;

	private int _lastDots = -1;

	private bool _needsFinalRender;

	private RoomInfo _selected;

	private bool _uiBuilt;

	private bool _buttonsHidden;

	private bool _loadedOnce;

	private int _lastSceneIndex = int.MinValue;

	private float _fallbackEnsureTime;

	private bool _uiModeOwned;

	private float _filterRebuildTime;

	private const float CardHeight = 68f;

	private const float CardSpacing = 5f;

	private const float CardStride = 73f;

	private const int SlotAboveBuffer = 2;

	private const int SlotBelowBuffer = 2;

	private readonly List<RoomInfo> _filtered = new List<RoomInfo>();

	private readonly List<CardSlot> _slotPool = new List<CardSlot>();

	private int _windowFirst = -1;

	private int _windowLast = -1;

	private bool _langFilterDirty;

	private Dictionary<uint, string> _pendingGlyphCheck;

	public bool IsShown
	{
		get
		{
			if ((Object)(object)_root != (Object)null)
			{
				return _root.activeSelf;
			}
			return false;
		}
	}

	public bool IsScanning => _scanning;

	public void Show()
	{
		try
		{
			EnsureUi();
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			UiStyle.EnsureFallbackFonts();
			_root.SetActive(true);
			HideMainMenuButtons();
			EnterUiMode();
			if (!_loadedOnce)
			{
				_loadedOnce = true;
				StartScan();
			}
			RefreshAllTexts();
			if (_langFilterDirty)
			{
				_langFilterDirty = false;
				if (_langOnly && _rooms.Count > 0)
				{
					int num = RebuildWindow();
					SetStatus("showing_lang", num, _activeRoomCount, ModText.T("lang_group_name"), ModText.T("lang_on"));
				}
			}
		}
		catch (Exception ex)
		{
			DebugLog.Error("[页面] Show 失败: " + ex);
		}
	}

	public void Hide()
	{
		if ((Object)(object)_root != (Object)null)
		{
			_root.SetActive(false);
		}
		ExitUiMode();
		RestoreMainMenuButtons();
		if (_scanning)
		{
			_scanning = false;
			LobbyScanner.ResetBatch(startSearches: false);
			if (_rooms.Count == 0)
			{
				_loadedOnce = false;
			}
		}
	}

	public void Destroy()
	{
		try
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
				_windowGo = null;
			}
			PasswordAutoFill.Clear();
			_slotPool.Clear();
			_filtered.Clear();
			_pendingGlyphCheck = null;
			_windowFirst = -1;
			_windowLast = -1;
			_uiBuilt = false;
		}
		catch (Exception ex)
		{
			DebugLog.Warn("[页面] Destroy 失败: " + ex.Message);
		}
	}

	private void EnterUiMode()
	{
		try
		{
			if ((Object)(object)WorldManager.instance != (Object)null)
			{
				WorldManager.SetToUIMode();
				_uiModeOwned = true;
				DebugLog.Debug("[页面] 已进入游戏 UI 模式(SetToUIMode)");
			}
			else
			{
				_uiModeOwned = false;
			}
		}
		catch (Exception ex)
		{
			_uiModeOwned = false;
			DebugLog.Warn("[页面] EnterUiMode 失败: " + ex.Message);
		}
	}

	private void KeepUiMode()
	{
		try
		{
			WorldManager instance = WorldManager.instance;
			if ((Object)(object)instance != (Object)null && !instance.inUI)
			{
				WorldManager.SetToUIMode();
				DebugLog.Debug("[页面] 检测到游戏输入模式被切回,重新 SetToUIMode 保持");
			}
		}
		catch (Exception ex)
		{
			DebugLog.Debug("[页面] KeepUiMode 失败: " + ex.Message);
		}
	}

	private void ExitUiMode()
	{
		if (!_uiModeOwned)
		{
			return;
		}
		_uiModeOwned = false;
		try
		{
			WorldManager.SetToGameMode();
			DebugLog.Debug("[页面] 已退出游戏 UI 模式(SetToGameMode)");
		}
		catch (Exception ex)
		{
			DebugLog.Warn("[页面] ExitUiMode 失败: " + ex.Message);
		}
	}

	private void HideMainMenuButtons()
	{
		if (_buttonsHidden)
		{
			return;
		}
		_buttonsHidden = true;
		try
		{
			if ((Object)(object)ModState.StartButtons != (Object)null && ModState.StartButtons.activeSelf)
			{
				ModState.StartButtons.SetActive(false);
				DebugLog.Debug("[页面] 已隐藏 StartButtons(背景保留)");
			}
			else
			{
				if (!((Object)(object)ModState.TitleMenu != (Object)null))
				{
					return;
				}
				_hiddenMenuButtons.Clear();
				foreach (Button componentsInChild in ((Component)ModState.TitleMenu).GetComponentsInChildren<Button>(true))
				{
					if ((Object)(object)componentsInChild != (Object)null && ((Component)componentsInChild).gameObject.activeSelf && !IsOurButton(componentsInChild))
					{
						((Component)componentsInChild).gameObject.SetActive(false);
						_hiddenMenuButtons.Add(((Component)componentsInChild).gameObject);
					}
				}
				if (_hiddenMenuButtons.Count > 0)
				{
					DebugLog.Debug($"[页面] fallback:隐藏了 {_hiddenMenuButtons.Count} 个主菜单按钮");
				}
			}
		}
		catch (Exception ex)
		{
			DebugLog.Warn("[页面] 隐藏主菜单按钮失败: " + ex.Message);
		}
	}

	private static bool IsOurButton(Button b)
	{
		Canvas componentInParent = ((Component)b).GetComponentInParent<Canvas>(true);
		if ((Object)(object)componentInParent != (Object)null)
		{
			return ((Object)componentInParent).name == "PublicLobbyPanel";
		}
		return false;
	}

	private void RestoreMainMenuButtons()
	{
		if (!_buttonsHidden)
		{
			return;
		}
		_buttonsHidden = false;
		try
		{
			if ((Object)(object)ModState.StartButtons != (Object)null)
			{
				ModState.StartButtons.SetActive(true);
				DebugLog.Debug("[页面] 已恢复 StartButtons");
				return;
			}
			foreach (GameObject hiddenMenuButton in _hiddenMenuButtons)
			{
				if ((Object)(object)hiddenMenuButton != (Object)null)
				{
					hiddenMenuButton.SetActive(true);
				}
			}
			_hiddenMenuButtons.Clear();
		}
		catch (Exception ex)
		{
			DebugLog.Warn("[页面] 恢复主菜单按钮失败: " + ex.Message);
		}
	}

	private void EnsureUi()
	{
		if (_uiBuilt)
		{
			return;
		}
		try
		{
			BuildUi();
			_uiBuilt = true;
		}
		catch (Exception ex)
		{
			DebugLog.Error("构建公开房间页面失败: " + ex);
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
				_windowGo = null;
			}
		}
	}

	private void BuildUi()
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		//IL_030a: Unknown result type (might be due to invalid IL or missing references)
		//IL_034a: Unknown result type (might be due to invalid IL or missing references)
		//IL_03df: Unknown result type (might be due to invalid IL or missing references)
		//IL_0443: Unknown result type (might be due to invalid IL or missing references)
		UiStyle.Capture(null);
		GameObject val = (_root = new GameObject("PublicLobbyPanel"));
		Object.DontDestroyOnLoad((Object)(object)val);
		val.AddComponent<Canvas>().renderMode = (RenderMode)0;
		val.AddComponent<CanvasScaler>().uiScaleMode = (ScaleMode)1;
		val.AddComponent<GraphicRaycaster>();
		_windowGo = ((Component)NewImage("Window", val.transform, UiStyle.PanelBackground)).gameObject;
		_windowGo.GetComponent<Image>().sprite = UiStyle.ButtonSprite;
		_windowGo.GetComponent<Image>().type = (Type)1;
		SetAnchors(_windowGo.GetComponent<RectTransform>(), 0.08f, 0.05f, 0.92f, 0.95f);
		SetAnchors((_titleTmp = UiStyle.CreateLabel("Title", _windowGo.transform, ModText.T("title"), 26f, (TextAlignmentOptions)514, UiStyle.TextNormal)).rectTransform, 0.06f, 0.87f, 0.94f, 0.94f);
		_searchInput = UiStyle.CreateInput(_windowGo.transform, ModText.T("search_ph"));
		Graphic placeholder = _searchInput.placeholder;
		TMP_Text val2 = (TMP_Text)(object)((placeholder is TMP_Text) ? placeholder : null);
		if (val2 != null)
		{
			_searchPh = val2;
		}
		SetAnchors(((Component)_searchInput).GetComponent<RectTransform>(), 0.06f, 0.8f, 0.5f, 0.86f);
		((UnityEventBase)_searchInput.onValueChanged).RemoveAllListeners();
		((UnityEvent<string>)(object)_searchInput.onValueChanged).AddListener(UiDelegates.Ua1(OnSearchChanged));
		_langOnlyButton = UiStyle.CreateOverlayButton(_windowGo.transform, "BtnLangOnly", ToggleLangOnly);
		SetAnchors(_langOnlyButton.GetComponent<RectTransform>(), 0.52f, 0.8f, 0.64f, 0.86f);
		_langOnlyTmp = UiStyle.OverlayLabel(_langOnlyButton, ModText.T("lang_only_off"), 16f, UiStyle.TextNormal);
		GameObject val3 = UiStyle.CreateOverlayButton(_windowGo.transform, "BtnRefresh", StartScan);
		SetAnchors(val3.GetComponent<RectTransform>(), 0.66f, 0.8f, 0.78f, 0.86f);
		_refreshTmp = UiStyle.OverlayLabel(val3, ModText.T("refresh"), 16f, UiStyle.TextNormal);
		_content = BuildScrollView(_windowGo.transform);
		_searchingText = UiStyle.CreateLabel("SearchingHint", _windowGo.transform, ModText.T("searching"), 24f, (TextAlignmentOptions)514, UiStyle.TextNormal);
		RectTransform rectTransform = _searchingText.rectTransform;
		rectTransform.anchorMin = new Vector2(0.5f, 0.45f);
		rectTransform.anchorMax = new Vector2(0.5f, 0.45f);
		rectTransform.pivot = new Vector2(0.5f, 0.5f);
		rectTransform.sizeDelta = new Vector2(700f, 60f);
		rectTransform.anchoredPosition = Vector2.zero;
		((Component)_searchingText).gameObject.SetActive(false);
		_statusText = UiStyle.CreateLabel("Status", _windowGo.transform, ModText.T("loading"), 15f, (TextAlignmentOptions)513, UiStyle.TextDim, allowWrap: true);
		SetAnchors(_statusText.rectTransform, 0.06f, 0.02f, 0.66f, 0.075f);
		_joinButton = UiStyle.CreateOverlayButton(_windowGo.transform, "BtnJoin", JoinSelected);
		SetAnchors(_joinButton.GetComponent<RectTransform>(), 0.68f, 0.015f, 0.84f, 0.075f);
		_joinTmp = UiStyle.OverlayLabel(_joinButton, ModText.T("join"), 18f, UiStyle.TextNormal);
		GameObject val4 = UiStyle.CreateOverlayButton(_windowGo.transform, "BtnBack", Hide);
		SetAnchors(val4.GetComponent<RectTransform>(), 0.86f, 0.015f, 0.97f, 0.075f);
		_backTmp = UiStyle.OverlayLabel(val4, ModText.T("back"), 16f, UiStyle.TextNormal);
		Hide();
	}

	private void ApplyPanelFonts()
	{
		UiStyle.ApplyFont(_titleTmp);
		UiStyle.ApplyFont(_langOnlyTmp);
		UiStyle.ApplyFont(_refreshTmp);
		UiStyle.ApplyFont(_joinTmp);
		UiStyle.ApplyFont(_backTmp);
		UiStyle.ApplyFont(_searchPh);
		UiStyle.ApplyFont(_searchingText);
		UiStyle.ApplyFont(_statusText);
	}

	public void RefreshAllTexts(bool markCardDirty = false)
	{
		ApplyPanelFonts();
		if ((Object)(object)_titleTmp != (Object)null)
		{
			_titleTmp.text = ModText.T("title");
		}
		if ((Object)(object)_langOnlyTmp != (Object)null)
		{
			_langOnlyTmp.text = ModText.T(_langOnly ? "lang_only_on" : "lang_only_off");
		}
		if ((Object)(object)_refreshTmp != (Object)null)
		{
			_refreshTmp.text = ModText.T("refresh");
		}
		if ((Object)(object)_joinTmp != (Object)null)
		{
			_joinTmp.text = ModText.T("join");
		}
		if ((Object)(object)_backTmp != (Object)null)
		{
			_backTmp.text = ModText.T("back");
		}
		if ((Object)(object)_searchPh != (Object)null)
		{
			_searchPh.text = ModText.T("search_ph");
		}
		ApplyStatus();
		if (markCardDirty)
		{
			for (int i = 0; i < _slotPool.Count; i++)
			{
				CardSlot cardSlot = _slotPool[i];
				if (cardSlot.Index >= 0)
				{
					UpdateSlotTexts(cardSlot);
				}
			}
		}
		if (markCardDirty && _langOnly && _rooms.Count > 0)
		{
			if (IsShown)
			{
				int num = RebuildWindow();
				SetStatus("showing_lang", num, _activeRoomCount, ModText.T("lang_group_name"), ModText.T("lang_on"));
			}
			else
			{
				_langFilterDirty = true;
			}
		}
	}

	private Image NewImage(string name, Transform parent, Color color)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name);
		val.AddComponent<RectTransform>();
		Image val2 = val.AddComponent<Image>();
		val.transform.SetParent(parent, false);
		((Graphic)val2).color = color;
		((Graphic)val2).raycastTarget = true;
		return val2;
	}

	private RectTransform BuildScrollView(Transform parent)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Expected O, but got Unknown
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Expected O, but got Unknown
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0233: Unknown result type (might be due to invalid IL or missing references)
		//IL_0242: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		//IL_024e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0261: Unknown result type (might be due to invalid IL or missing references)
		//IL_027f: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02de: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("ScrollView");
		val.AddComponent<RectTransform>();
		val.transform.SetParent(parent, false);
		Image obj = val.AddComponent<Image>();
		((Graphic)obj).color = new Color(0.03f, 0.035f, 0.05f, 1f);
		obj.sprite = UiStyle.ButtonSprite;
		obj.type = (Type)1;
		ScrollRect val2 = val.AddComponent<ScrollRect>();
		GameObject val3 = new GameObject("Viewport");
		val3.AddComponent<RectTransform>();
		val3.transform.SetParent(val.transform, false);
		Image val4 = val3.AddComponent<Image>();
		((Graphic)val4).color = new Color(0f, 0f, 0f, 0.25f);
		val3.AddComponent<RectMask2D>();
		UiStyle.Stretch(((Graphic)val4).rectTransform, 0f, 22f, 0f, 0f);
		GameObject val5 = new GameObject("Content");
		val5.AddComponent<RectTransform>();
		val5.transform.SetParent(val3.transform, false);
		RectTransform component = val5.GetComponent<RectTransform>();
		component.anchorMin = new Vector2(0f, 1f);
		component.anchorMax = new Vector2(1f, 1f);
		component.pivot = new Vector2(0.5f, 1f);
		component.sizeDelta = new Vector2(0f, 0f);
		val2.viewport = ((Graphic)val4).rectTransform;
		val2.content = component;
		val2.horizontal = false;
		val2.vertical = true;
		val2.movementType = (MovementType)2;
		val2.scrollSensitivity = 30f;
		GameObject val6 = new GameObject("Scrollbar");
		val6.AddComponent<RectTransform>();
		val6.transform.SetParent(val.transform, false);
		((Graphic)val6.AddComponent<Image>()).color = new Color(0.18f, 0.2f, 0.26f, 0.7f);
		RectTransform component2 = val6.GetComponent<RectTransform>();
		component2.anchorMin = new Vector2(1f, 0f);
		component2.anchorMax = new Vector2(1f, 1f);
		component2.pivot = new Vector2(1f, 0.5f);
		component2.offsetMin = new Vector2(-16f, 2f);
		component2.offsetMax = new Vector2(-6f, -2f);
		GameObject val7 = new GameObject("Handle");
		val7.AddComponent<RectTransform>();
		val7.transform.SetParent(val6.transform, false);
		Image val8 = val7.AddComponent<Image>();
		((Graphic)val8).color = new Color(0.85f, 0.88f, 0.95f, 0.95f);
		RectTransform component3 = val7.GetComponent<RectTransform>();
		component3.anchorMin = new Vector2(0f, 0f);
		component3.anchorMax = new Vector2(1f, 1f);
		component3.pivot = new Vector2(0.5f, 0.5f);
		component3.offsetMin = new Vector2(2f, 2f);
		component3.offsetMax = new Vector2(-2f, -2f);
		Scrollbar val9 = val6.AddComponent<Scrollbar>();
		val9.direction = (Direction)2;
		((Selectable)val9).targetGraphic = (Graphic)(object)val8;
		val9.handleRect = component3;
		val9.value = 1f;
		val9.size = 0.15f;
		((UnityEvent<float>)(object)val9.onValueChanged).AddListener(UiDelegates.Ua1f(delegate(float v)
		{
			if ((Object)(object)_scrollRect != (Object)null)
			{
				_scrollRect.verticalNormalizedPosition = v;
			}
		}));
		_scrollRect = val2;
		_scrollbarCmp = val9;
		_viewportRt = ((Graphic)val4).rectTransform;
		SetAnchors(val.GetComponent<RectTransform>(), 0.06f, 0.09f, 0.94f, 0.78f);
		return component;
	}

	private static void SetAnchors(RectTransform rt, float minX, float minY, float maxX, float maxY)
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		rt.anchorMin = new Vector2(minX, minY);
		rt.anchorMax = new Vector2(maxX, maxY);
		rt.offsetMin = Vector2.zero;
		rt.offsetMax = Vector2.zero;
		rt.pivot = new Vector2(0.5f, 0.5f);
	}

	public void Tick()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		int lastSceneIndex = _lastSceneIndex;
		Scene activeScene = SceneManager.GetActiveScene();
		if (lastSceneIndex != ((Scene)(ref activeScene)).buildIndex)
		{
			activeScene = SceneManager.GetActiveScene();
			_lastSceneIndex = ((Scene)(ref activeScene)).buildIndex;
			if ((Object)(object)_root != (Object)null && _root.activeSelf)
			{
				Hide();
			}
		}
		if ((Object)(object)_root != (Object)null && _root.activeSelf)
		{
			KeepUiMode();
		}
		if ((Object)(object)_root != (Object)null && _root.activeSelf && Time.unscaledTime - _fallbackEnsureTime > 3f)
		{
			_fallbackEnsureTime = Time.unscaledTime;
			UiStyle.EnsureFallbackFonts();
		}
		if (_filterRebuildTime > 0f && Time.unscaledTime >= _filterRebuildTime && (Object)(object)_root != (Object)null && _root.activeSelf)
		{
			_filterRebuildTime = 0f;
			int num = RebuildWindow();
			if (_activeRoomCount > 0)
			{
				SetStatus("showing", num, _activeRoomCount);
			}
		}
		if ((Object)(object)_scrollbarCmp != (Object)null && (Object)(object)_scrollRect != (Object)null)
		{
			float verticalNormalizedPosition = _scrollRect.verticalNormalizedPosition;
			if (Mathf.Abs(verticalNormalizedPosition - _scrollbarCmp.value) > 0.001f)
			{
				_scrollbarCmp.value = verticalNormalizedPosition;
			}
		}
		if ((Object)(object)_scrollbarCmp != (Object)null && (Object)(object)_viewportRt != (Object)null && (Object)(object)_content != (Object)null)
		{
			Rect rect = _viewportRt.rect;
			float height = ((Rect)(ref rect)).height;
			if (height > 0f)
			{
				rect = _content.rect;
				float height2 = ((Rect)(ref rect)).height;
				float num2 = Mathf.Clamp(height / Mathf.Max(height2, height), 0.15f, 1f);
				if (Mathf.Abs(num2 - _scrollbarCmp.size) > 0.001f)
				{
					_scrollbarCmp.size = num2;
				}
			}
		}
		if ((Object)(object)_root != (Object)null && _root.activeSelf)
		{
			SyncWindow();
		}
		if (_pendingGlyphCheck != null && UiStyle.IsBasePrebakeDone && !UiStyle.IsRoomPrebakeBusy)
		{
			UiStyle.ReportMissingGlyphs(_pendingGlyphCheck.Keys, _pendingGlyphCheck);
			_pendingGlyphCheck = null;
		}
		UpdateSearchingAnimation();
		if (_scanning)
		{
			try
			{
				if (LobbyScanner.TryFinishBatch())
				{
					_scanning = false;
					_needsFinalRender = true;
				}
			}
			catch (Exception ex)
			{
				DebugLog.Warn("[页面] 扫描驱动异常,已终止本轮: " + ex.Message);
				_scanning = false;
				_needsFinalRender = true;
			}
		}
		if (_needsFinalRender && !_scanning && (Object)(object)_root != (Object)null && _root.activeSelf)
		{
			_needsFinalRender = false;
			if ((Object)(object)_searchingText != (Object)null)
			{
				((Component)_searchingText).gameObject.SetActive(false);
			}
			_rooms.Clear();
			_rooms.AddRange(LobbyScanner.GetRoomsSnapshot());
			int num3 = RebuildWindow();
			UiStyle.EnqueueRoomCharsForPrebake(CollectWindowRoomChars());
			UiStyle.EnqueueRoomCharsForPrebake(CollectListRoomChars());
			if (num3 > 0)
			{
				_pendingGlyphCheck = CollectMissingGlyphSamples();
			}
			if (num3 == 0)
			{
				SetStatus("no_rooms");
			}
			else
			{
				SetStatus("rooms_count", num3);
			}
		}
		UiStyle.TickRoomPrebake(6f);
	}

	private void UpdateSearchingAnimation()
	{
		if (!((Object)(object)_searchingText == (Object)null) && ((Component)_searchingText).gameObject.activeSelf)
		{
			int num = (int)(Time.time * 2f) % 3 + 1;
			if (num != _lastDots)
			{
				_lastDots = num;
				_searchingText.text = ModText.T("searching_base") + new string('.', num);
			}
		}
	}

	private void StartScan()
	{
		if (!_scanning)
		{
			_scanning = true;
			_selected = null;
			_langFilterDirty = false;
			_lastSelectedKey = null;
			_rooms.Clear();
			_pendingGlyphCheck = null;
			RebuildWindow();
			if ((Object)(object)_searchingText != (Object)null)
			{
				((Component)_searchingText).gameObject.SetActive(true);
			}
			SetStatus("");
			_needsFinalRender = true;
			DebugLog.Info($"[页面] 刷新:{ModState.TaggedRounds} 轮定向搜索(bwpl_open,每轮上限 200;每次刷新全新结果)");
			LobbyScanner.ResetBatch();
		}
	}

	private bool FilterPass(RoomInfo room, string filter)
	{
		if (filter.Length > 0 && !(room.WorldName ?? "").ToLowerInvariant().Contains(filter) && !(room.HostName ?? "").ToLowerInvariant().Contains(filter))
		{
			return false;
		}
		if (_langOnly && !ModText.RoomInGroup(room.WorldName ?? "", room.HostName ?? ""))
		{
			return false;
		}
		return true;
	}

	private int BuildFilteredList()
	{
		string filter = (_filter ?? "").Trim().ToLowerInvariant();
		_activeRoomCount = 0;
		_filtered.Clear();
		foreach (RoomInfo room in _rooms)
		{
			if (room.MaxPlayers <= 0 || room.CurrentPlayers < room.MaxPlayers)
			{
				_activeRoomCount++;
				if (FilterPass(room, filter))
				{
					_filtered.Add(room);
				}
			}
		}
		return _filtered.Count;
	}

	private int RebuildWindow()
	{
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_content == (Object)null)
		{
			return 0;
		}
		int count = _filtered.Count;
		int num = BuildFilteredList();
		float verticalNormalizedPosition = 1f;
		if (count > 0 && num > 0 && (Object)(object)_scrollRect != (Object)null && (Object)(object)_viewportRt != (Object)null)
		{
			Rect rect = _viewportRt.rect;
			float height = ((Rect)(ref rect)).height;
			float num2 = Mathf.Max(0f, (float)count * 73f - 5f - height);
			float num3 = (1f - _scrollRect.verticalNormalizedPosition) * num2;
			float num4 = Mathf.Max(0f, (float)num * 73f - 5f - height);
			if (num4 > 0f)
			{
				verticalNormalizedPosition = 1f - Mathf.Clamp01(num3 / num4);
			}
		}
		SetContentHeight(num);
		if ((Object)(object)_scrollRect != (Object)null)
		{
			_scrollRect.verticalNormalizedPosition = verticalNormalizedPosition;
		}
		_windowFirst = -1;
		_windowLast = -1;
		SyncWindow();
		return num;
	}

	private void SetContentHeight(int count)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		float num = ((count == 0) ? 0f : ((float)count * 73f - 5f));
		if (Mathf.Abs(_content.sizeDelta.y - num) > 0.01f)
		{
			_content.sizeDelta = new Vector2(0f, num);
		}
	}

	private void SyncWindow()
	{
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_root == (Object)null || !_root.activeSelf || (Object)(object)_content == (Object)null || (Object)(object)_scrollRect == (Object)null || (Object)(object)_viewportRt == (Object)null)
		{
			return;
		}
		int count = _filtered.Count;
		int num;
		int num2;
		if (count == 0)
		{
			num = 0;
			num2 = 0;
		}
		else
		{
			Rect rect = _viewportRt.rect;
			float height = ((Rect)(ref rect)).height;
			float num3 = Mathf.Max(0f, (float)count * 73f - 5f - height);
			float num4 = (1f - _scrollRect.verticalNormalizedPosition) * num3;
			num = Mathf.Max(0, (int)Mathf.Floor(num4 / 73f) - 2);
			num2 = Mathf.Min(count, (int)Mathf.Ceil((num4 + height) / 73f) + 2);
		}
		if (num == _windowFirst && num2 == _windowLast)
		{
			return;
		}
		for (int i = 0; i < _slotPool.Count; i++)
		{
			CardSlot cardSlot = _slotPool[i];
			if (cardSlot.Index >= 0 && (cardSlot.Index < num || cardSlot.Index >= num2 || cardSlot.Room != _filtered[cardSlot.Index]))
			{
				RecycleSlot(cardSlot);
			}
		}
		EnsureSlotPool(num2 - num);
		for (int j = num; j < num2; j++)
		{
			bool flag = false;
			for (int k = 0; k < _slotPool.Count; k++)
			{
				if (_slotPool[k].Index == j)
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				CardSlot cardSlot2 = TakeFreeSlot();
				if (cardSlot2 == null)
				{
					cardSlot2 = CreateSlot();
					_slotPool.Add(cardSlot2);
				}
				AssignSlot(cardSlot2, j, _filtered[j]);
			}
		}
		_windowFirst = num;
		_windowLast = num2;
	}

	private void AssignSlot(CardSlot s, int index, RoomInfo room)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		s.Index = index;
		s.Room = room;
		s.Rect.sizeDelta = new Vector2(0f, 68f);
		s.Rect.anchorMin = new Vector2(0f, 1f);
		s.Rect.anchorMax = new Vector2(1f, 1f);
		s.Rect.pivot = new Vector2(0.5f, 1f);
		s.Rect.anchoredPosition = new Vector2(0f, (float)(-index) * 73f);
		UpdateSlotTexts(s);
		((Graphic)s.Image).color = ((room.Key == _lastSelectedKey) ? UiStyle.CardSelected : UiStyle.CardBackground);
		((Component)s.Rect).gameObject.SetActive(true);
	}

	private void RecycleSlot(CardSlot s)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		s.Index = -1;
		s.Room = null;
		s.Rect.anchoredPosition = Vector2.zero;
		s.Rect.sizeDelta = Vector2.zero;
		((Component)s.Rect).gameObject.SetActive(false);
	}

	private void EnsureSlotPool(int required)
	{
		while (_slotPool.Count < required)
		{
			_slotPool.Add(CreateSlot());
		}
	}

	private CardSlot TakeFreeSlot()
	{
		for (int i = 0; i < _slotPool.Count; i++)
		{
			if (_slotPool[i].Index < 0)
			{
				return _slotPool[i];
			}
		}
		return null;
	}

	private CardSlot CreateSlot()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		Image val = NewImage("Card_", (Transform)(object)_content, UiStyle.CardBackground);
		val.sprite = UiStyle.ButtonSprite;
		val.type = (Type)1;
		((Graphic)val).rectTransform.sizeDelta = new Vector2(0f, 68f);
		TMP_Text val2 = UiStyle.CreateLabel("Name", ((Component)val).transform, "", 18f, (TextAlignmentOptions)513, UiStyle.TextNormal);
		val2.richText = false;
		val2.overflowMode = (TextOverflowModes)1;
		val2.verticalAlignment = (VerticalAlignmentOptions)512;
		UiStyle.Stretch(val2.rectTransform, 14f, 14f, 2f, 46f);
		TMP_Text val3 = UiStyle.CreateLabel("Host", ((Component)val).transform, "", 14f, (TextAlignmentOptions)513, UiStyle.TextDim);
		val3.overflowMode = (TextOverflowModes)1;
		val3.verticalAlignment = (VerticalAlignmentOptions)512;
		UiStyle.Stretch(val3.rectTransform, 14f, 14f, 24f, 24f);
		TMP_Text val4 = UiStyle.CreateLabel("Meta", ((Component)val).transform, "", 13f, (TextAlignmentOptions)513, UiStyle.TextDim);
		val4.overflowMode = (TextOverflowModes)1;
		val4.verticalAlignment = (VerticalAlignmentOptions)512;
		UiStyle.Stretch(val4.rectTransform, 14f, 14f, 46f, 2f);
		Button val5 = ((Component)val).gameObject.AddComponent<Button>();
		((Selectable)val5).targetGraphic = (Graphic)(object)val;
		CardSlot cardSlot = new CardSlot(this, ((Graphic)val).rectTransform, val, val5, val2, val3, val4);
		((UnityEvent)val5.onClick).AddListener(UiDelegates.Ua(cardSlot.OnClick));
		((Component)val).gameObject.SetActive(false);
		return cardSlot;
	}

	private void UpdateSlotTexts(CardSlot s)
	{
		try
		{
			RoomInfo room = s.Room;
			if (room == null)
			{
				return;
			}
			UiStyle.ApplyFont(s.NameT);
			UiStyle.ApplyFont(s.HostT);
			UiStyle.ApplyFont(s.MetaT);
			if ((Object)(object)s.NameT != (Object)null)
			{
				string text = NameTextFor(room);
				if (s.NameT.text != text)
				{
					s.NameT.text = text;
				}
			}
			if ((Object)(object)s.HostT != (Object)null)
			{
				string text2 = HostTextFor(room);
				if (s.HostT.text != text2)
				{
					s.HostT.text = text2;
				}
			}
			if ((Object)(object)s.MetaT != (Object)null)
			{
				string text3 = RoomMetaLine(room);
				if (s.MetaT.text != text3)
				{
					s.MetaT.text = text3;
				}
			}
		}
		catch (Exception ex)
		{
			DebugLog.Debug("[UI] 槽位文本更新失败(" + s?.Room?.Key + "): " + ex.Message);
		}
	}

	private void RecolorSelection()
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < _slotPool.Count; i++)
		{
			CardSlot cardSlot = _slotPool[i];
			if (cardSlot.Index >= 0)
			{
				((Graphic)cardSlot.Image).color = ((cardSlot.Room != null && cardSlot.Room.Key == _lastSelectedKey) ? UiStyle.CardSelected : UiStyle.CardBackground);
			}
		}
	}

	private IEnumerable<uint> CollectWindowRoomChars()
	{
		int num = Mathf.Max(0, _windowFirst);
		int last = Mathf.Min(_filtered.Count, _windowLast);
		for (int i = num; i < last; i++)
		{
			RoomInfo roomInfo = _filtered[i];
			if (roomInfo == null)
			{
				continue;
			}
			foreach (uint item in RoomCharsForPrebake(roomInfo))
			{
				yield return item;
			}
		}
	}

	private IEnumerable<uint> CollectListRoomChars()
	{
		foreach (RoomInfo room in _rooms)
		{
			if (room == null)
			{
				continue;
			}
			foreach (uint item in RoomCharsForPrebake(room))
			{
				yield return item;
			}
		}
	}

	private static IEnumerable<uint> RoomCharsForPrebake(RoomInfo room)
	{
		if (!string.IsNullOrEmpty(room.WorldName))
		{
			foreach (uint item in UiStyle.CodePoints(room.WorldName))
			{
				yield return item;
			}
		}
		if (!string.IsNullOrEmpty(room.HostName))
		{
			foreach (uint item2 in UiStyle.CodePoints(room.HostName))
			{
				yield return item2;
			}
		}
		if (!room.HasPasswordAttribute || string.IsNullOrEmpty(room.Password))
		{
			yield break;
		}
		foreach (uint item3 in UiStyle.CodePoints(room.Password))
		{
			yield return item3;
		}
	}

	private Dictionary<uint, string> CollectMissingGlyphSamples()
	{
		Dictionary<uint, string> dictionary = new Dictionary<uint, string>();
		foreach (RoomInfo item in _filtered)
		{
			string worldName = item.WorldName;
			if (!string.IsNullOrEmpty(worldName))
			{
				for (int i = 0; i < worldName.Length; i++)
				{
					uint key = worldName[i];
					if (char.IsHighSurrogate(worldName[i]) && i + 1 < worldName.Length && char.IsLowSurrogate(worldName[i + 1]))
					{
						key = (uint)((worldName[i] - 55296) * 1024 + (worldName[i + 1] - 56320) + 65536);
						i++;
					}
					if (!dictionary.ContainsKey(key))
					{
						dictionary[key] = worldName;
					}
				}
			}
			string hostName = item.HostName;
			if (string.IsNullOrEmpty(hostName))
			{
				continue;
			}
			for (int j = 0; j < hostName.Length; j++)
			{
				uint key2 = hostName[j];
				if (char.IsHighSurrogate(hostName[j]) && j + 1 < hostName.Length && char.IsLowSurrogate(hostName[j + 1]))
				{
					key2 = (uint)((hostName[j] - 55296) * 1024 + (hostName[j + 1] - 56320) + 65536);
					j++;
				}
				if (!dictionary.ContainsKey(key2))
				{
					dictionary[key2] = hostName;
				}
			}
		}
		return dictionary;
	}

	private static string NameTextFor(RoomInfo room)
	{
		return string.Concat(string.IsNullOrEmpty(room.WorldName) ? ModText.T("unknown_world") : room.WorldName, str2: (!room.HasPasswordAttribute || room.Password == null) ? ModText.T("pw_unavailable") : ((room.Password.Length != 0) ? SanitizePassword(room.Password) : ModText.T("pw_none")), str1: ModText.T("pw_label"));
	}

	private static string SanitizePassword(string password)
	{
		bool flag = false;
		foreach (char c in password)
		{
			if (c < ' ' || c == '\u007f')
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return password;
		}
		StringBuilder stringBuilder = new StringBuilder(password.Length);
		foreach (char c2 in password)
		{
			if (c2 < ' ')
			{
				stringBuilder.Append((char)(9216 + c2));
			}
			else if (c2 == '\u007f')
			{
				stringBuilder.Append('␡');
			}
			else
			{
				stringBuilder.Append(c2);
			}
		}
		return stringBuilder.ToString();
	}

	private static string HostTextFor(RoomInfo room)
	{
		return ModText.T("host_label") + (string.IsNullOrEmpty(room.HostName) ? ModText.T("unknown_host") : room.HostName);
	}

	private static string RoomDisplayName(RoomInfo room)
	{
		if (!string.IsNullOrEmpty(room.WorldName))
		{
			return room.WorldName;
		}
		if (!string.IsNullOrEmpty(room.HostName))
		{
			return ModText.T("host", room.HostName);
		}
		return ModText.T("code", room.JoinCode);
	}

	private static string RoomMetaLine(RoomInfo room)
	{
		string text = "";
		if (room.MaxPlayers > 0)
		{
			text = ((room.CurrentPlayers >= 0) ? $"{room.CurrentPlayers}/{room.MaxPlayers}" : $"?/{room.MaxPlayers}");
		}
		string text2 = (string.IsNullOrEmpty(room.JoinCode) ? "" : (ModText.T("code_label") + room.JoinCode));
		if (text.Length > 0 && text2.Length > 0)
		{
			return text + " · " + text2;
		}
		if (text.Length <= 0)
		{
			return text2;
		}
		return text;
	}

	private void SelectRoom(RoomInfo room)
	{
		_selected = room;
		_lastSelectedKey = room.Key;
		RecolorSelection();
		SetStatus("selected", RoomDisplayName(room), room.JoinCode);
	}

	private void JoinSelected()
	{
		if (_selected == null)
		{
			SetStatus("select_first");
			return;
		}
		try
		{
			string joinCode = _selected.JoinCode;
			PasswordAutoFill.Arm(joinCode, _selected.HasPasswordAttribute ? _selected.Password : null);
			JoinMenu val = Object.FindObjectOfType<JoinMenu>(true);
			if ((Object)(object)val != (Object)null)
			{
				DebugLog.Info("正在加入公开房间 " + joinCode + "(原版 JoinMenu.ConnectTo 完整链路)…");
				Hide();
				val.ConnectTo(joinCode);
			}
			else
			{
				DebugLog.Warn("[加入] 未找到原版 JoinMenu 实例,回退直接 SetTransportAndConnect(无错误界面)");
				NetworkMinder.SetTransportAndConnect(joinCode);
				Hide();
			}
		}
		catch (Exception ex)
		{
			DebugLog.Error("加入房间失败: " + ex);
			PasswordAutoFill.Clear();
			if (!IsShown)
			{
				Show();
			}
			SetStatus("join_failed", ex.Message);
		}
	}

	private void ToggleLangOnly()
	{
		_langOnly = !_langOnly;
		DebugLog.Info($"[页面] 仅本语言开关 → {_langOnly}(语言组={ModText.CurrentGroup})");
		RefreshAllTexts();
		int num = RebuildWindow();
		SetStatus("showing_lang", num, _activeRoomCount, ModText.T("lang_group_name"), ModText.T(_langOnly ? "lang_on" : "lang_off"));
	}

	private void OnSearchChanged(string value)
	{
		_filter = value ?? "";
		_filterRebuildTime = Time.unscaledTime + 0.15f;
	}

	private void SetStatus(string key, params object[] args)
	{
		_statusKey = key;
		_statusArgs = args ?? new object[0];
		ApplyStatus();
	}

	private void ApplyStatus()
	{
		if (!((Object