Decompiled source of Room Hub v2.6.8

RoomHub.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PEAKLib.UI;
using PEAKLib.UI.Elements;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.UI.Modal;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RoomHub")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+77d72fe7aa78cbcd5bd93ba2ab02923e6b3c14a4")]
[assembly: AssemblyProduct("RoomHub")]
[assembly: AssemblyTitle("RoomHub")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RoomHub
{
	public enum FriendGameMode
	{
		Joinable,
		InviteOnly,
		Solo,
		MainMenu,
		Unknown
	}
	public readonly struct FriendRoomInfo
	{
		public readonly ulong SteamId;

		public readonly string Name;

		public readonly ulong LobbyId;

		public readonly FriendGameMode Mode;

		public FriendRoomInfo(ulong steamId, string name, ulong lobbyId, FriendGameMode mode)
		{
			SteamId = steamId;
			Name = name ?? "";
			LobbyId = lobbyId;
			Mode = mode;
		}
	}
	internal static class FriendRoomsService
	{
		public const uint PeakAppId = 3527290u;

		private const float RefreshIntervalSec = 10f;

		private static Callback<PersonaStateChange_t>? _personaChanged;

		private static Callback<FriendRichPresenceUpdate_t>? _richPresenceUpdate;

		private static float _nextRefresh;

		private static List<FriendRoomInfo>? _cache;

		private static float _lastNotify;

		private const float NotifyMinIntervalSec = 1.5f;

		private static bool _inited;

		private static readonly Dictionary<ulong, float> _lastRpRequest = new Dictionary<ulong, float>();

		private const float RpRequestMinIntervalSec = 30f;

		public static event Action? Changed;

		public static void Init()
		{
			if (_inited)
			{
				return;
			}
			try
			{
				_personaChanged = Callback<PersonaStateChange_t>.Create((DispatchDelegate<PersonaStateChange_t>)OnPersonaChanged);
				_richPresenceUpdate = Callback<FriendRichPresenceUpdate_t>.Create((DispatchDelegate<FriendRichPresenceUpdate_t>)OnFriendRichPresenceUpdate);
				_inited = true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("FriendRoomsService init failed: " + ex.Message));
			}
		}

		public static IReadOnlyList<FriendRoomInfo> GetFriends()
		{
			RefreshIfDue(force: false);
			IReadOnlyList<FriendRoomInfo> cache = _cache;
			return cache ?? Array.Empty<FriendRoomInfo>();
		}

		private static void RefreshIfDue(bool force)
		{
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: 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_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			if (!_inited || (!force && _cache != null && Time.unscaledTime < _nextRefresh))
			{
				return;
			}
			_nextRefresh = Time.unscaledTime + 10f;
			try
			{
				List<FriendRoomInfo> list = new List<FriendRoomInfo>();
				int friendCount = SteamFriends.GetFriendCount((EFriendFlags)4);
				FriendGameInfo_t val = default(FriendGameInfo_t);
				for (int i = 0; i < friendCount; i++)
				{
					CSteamID friendByIndex = SteamFriends.GetFriendByIndex(i, (EFriendFlags)4);
					if (friendByIndex == CSteamID.Nil || friendByIndex.m_SteamID == 0L)
					{
						continue;
					}
					bool flag = false;
					ulong lobbyId = 0uL;
					FriendGameMode mode = FriendGameMode.MainMenu;
					if (SteamFriends.GetFriendGamePlayed(friendByIndex, ref val) && ((CGameID)(ref val.m_gameID)).AppID().m_AppId == 3527290)
					{
						flag = true;
						try
						{
							float unscaledTime = Time.unscaledTime;
							if (!_lastRpRequest.TryGetValue(friendByIndex.m_SteamID, out var value) || unscaledTime - value >= 30f)
							{
								_lastRpRequest[friendByIndex.m_SteamID] = unscaledTime;
								SteamFriends.RequestFriendRichPresence(friendByIndex);
							}
						}
						catch
						{
						}
						if (((CSteamID)(ref val.m_steamIDLobby)).IsValid() && val.m_steamIDLobby.m_SteamID != 0L)
						{
							lobbyId = val.m_steamIDLobby.m_SteamID;
							mode = FriendGameMode.Joinable;
						}
						else
						{
							int num = 0;
							try
							{
								num = SteamFriends.GetFriendRichPresenceKeyCount(friendByIndex);
							}
							catch
							{
							}
							if (num == 0)
							{
								mode = FriendGameMode.Unknown;
							}
							else
							{
								try
								{
									string friendRichPresence = SteamFriends.GetFriendRichPresence(friendByIndex, "steam_display");
									string friendRichPresence2 = SteamFriends.GetFriendRichPresence(friendByIndex, "players");
									string friendRichPresence3 = SteamFriends.GetFriendRichPresence(friendByIndex, "max_players");
									mode = (string.Equals(friendRichPresence, "#Status_MainMenu", StringComparison.Ordinal) ? FriendGameMode.MainMenu : (string.Equals(friendRichPresence3, "1", StringComparison.Ordinal) ? FriendGameMode.Solo : (string.Equals(friendRichPresence2, "1", StringComparison.Ordinal) ? FriendGameMode.MainMenu : ((!string.IsNullOrEmpty(friendRichPresence3)) ? FriendGameMode.InviteOnly : FriendGameMode.MainMenu))));
								}
								catch
								{
								}
							}
						}
					}
					string name = SteamFriends.GetFriendPersonaName(friendByIndex) ?? "";
					if (flag)
					{
						list.Add(new FriendRoomInfo(friendByIndex.m_SteamID, name, lobbyId, mode));
					}
				}
				_cache = list.OrderByDescending((FriendRoomInfo f) => f.LobbyId != 0).ThenBy<FriendRoomInfo, string>((FriendRoomInfo f) => f.Name, StringComparer.OrdinalIgnoreCase).ToList();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("FriendRoomsService refresh failed: " + ex.Message));
			}
		}

		private static void OnPersonaChanged(PersonaStateChange_t cb)
		{
			_cache = null;
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - _lastNotify < 1.5f)
			{
				return;
			}
			_lastNotify = unscaledTime;
			try
			{
				FriendRoomsService.Changed?.Invoke();
			}
			catch
			{
			}
		}

		private static void OnFriendRichPresenceUpdate(FriendRichPresenceUpdate_t cb)
		{
			_cache = null;
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - _lastNotify < 1.5f)
			{
				return;
			}
			_lastNotify = unscaledTime;
			try
			{
				FriendRoomsService.Changed?.Invoke();
			}
			catch
			{
			}
		}
	}
	internal sealed class HistoryPageController
	{
		private const string SearchViewId = "__search__";

		private const float FriendPanelWidth = 400f;

		private const float ListWidth = 800f;

		private const float RoomRowHeight = 120f;

		private const float PlayerRowHeight = 110f;

		private const float DoubleClickSeconds = 0.4f;

		private PeakCustomPage? _page;

		private PeakScrollableContent? _list;

		private PeakScrollableContent? _friendList;

		private PeakText? _titleText;

		private PeakText? _hintText;

		private PeakText? _hintText2;

		private PeakText? _deleteBanner;

		private PeakText? _friendPanelTitle;

		private PeakMenuButton? _trashButton;

		private PeakMenuButton? _backButton;

		private PeakMenuButton? _searchButton;

		private PeakMenuButton? _closeButton;

		private PeakTextInput? _searchInput;

		private bool _deleteMode;

		private string? _viewingLobbyId;

		private string? _searchQuery;

		private string? _lastClickLobbyId;

		private float _lastClickTime;

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

		private HistoryRowFactory? _rows;

		private HistoryRowFactory? _friendRows;

		private bool _cursorCaptured;

		private CursorLockMode _prevLockMode = (CursorLockMode)1;

		private bool _prevCursorVisible;

		private static readonly MethodInfo? MenuWindowOpen = typeof(MenuWindow).GetMethod("Open", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo? MenuWindowClose = typeof(MenuWindow).GetMethod("Close", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private bool _openedViaMenuWindow;

		public bool HasLivePage
		{
			get
			{
				if ((Object)(object)_page == (Object)null)
				{
					return false;
				}
				Component page = (Component)(object)_page;
				return (Object)(object)page != (Object)null;
			}
		}

		public bool IsPageOpen
		{
			get
			{
				if (!HasLivePage || !_openedViaMenuWindow)
				{
					return false;
				}
				try
				{
					return ((Component)_page).gameObject.activeInHierarchy;
				}
				catch
				{
					return false;
				}
			}
		}

		public void Hide()
		{
			if (!HasLivePage)
			{
				_openedViaMenuWindow = false;
				ReleaseGameplayInputBlock();
				return;
			}
			try
			{
				CloseMenuWindow();
			}
			catch
			{
			}
			_openedViaMenuWindow = false;
			try
			{
				if (HasLivePage && ((Component)_page).gameObject.activeInHierarchy)
				{
					((MenuWindow)_page).Hide();
				}
			}
			catch
			{
			}
			ReleaseGameplayInputBlock();
		}

		public void Discard()
		{
			try
			{
				if (HasLivePage)
				{
					CloseMenuWindow();
				}
			}
			catch
			{
			}
			ReleaseGameplayInputBlock();
			try
			{
				if (HasLivePage)
				{
					Object.Destroy((Object)(object)((Component)_page).gameObject);
				}
			}
			catch
			{
			}
			_page = null;
			_list = null;
			_friendList = null;
			_friendPanelTitle = null;
			_titleText = null;
			_hintText = null;
			_hintText2 = null;
			_deleteBanner = null;
			_trashButton = null;
			_backButton = null;
			_closeButton = null;
			_searchButton = null;
			_searchInput = null;
			_rows = null;
			_friendRows = null;
			_avatarRefreshTargets.Clear();
			_deleteMode = false;
			_viewingLobbyId = null;
			_searchQuery = null;
			_openedViaMenuWindow = false;
			LocalizationSync.Unregister(OnGameLanguageChanged);
			UnsubscribeExternalEvents();
		}

		public void Open()
		{
			_deleteMode = false;
			_viewingLobbyId = null;
			HistoryUiFont.CaptureFromMenuButton();
			EnsurePage();
			if (!HasLivePage)
			{
				return;
			}
			ShowRoomsView();
			try
			{
				_page.BlocksPlayerInput = true;
				_page.ShowCursorWhileOpen = true;
				_page.CloseOnUICancel = false;
				_page.CloseOnPause = false;
				if (!OpenMenuWindow())
				{
					if (!HistoryUi.IsTitleScene)
					{
						Plugin.Log.LogError((object)"MenuWindow.Open unavailable in-game — aborting open.");
						Discard();
						return;
					}
					((MenuWindow)_page).Show();
					_openedViaMenuWindow = true;
					Plugin.Log.LogWarning((object)"MenuWindow.Open unavailable — using Show on title.");
				}
				else
				{
					_openedViaMenuWindow = true;
				}
				CaptureGameplayInputBlock();
				EnsurePageBlocksRaycasts();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"History page Open failed: {arg}");
				Discard();
			}
		}

		private bool OpenMenuWindow()
		{
			if (!HasLivePage || MenuWindowOpen == null)
			{
				return false;
			}
			try
			{
				MenuWindowOpen.Invoke(_page, null);
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("MenuWindow.Open invoke failed: " + ex.Message));
				return false;
			}
		}

		private void CloseMenuWindow()
		{
			if (!HasLivePage)
			{
				return;
			}
			try
			{
				if (MenuWindowClose != null)
				{
					MenuWindowClose.Invoke(_page, null);
				}
				else
				{
					((MenuWindow)_page).Hide();
				}
			}
			catch
			{
				try
				{
					((MenuWindow)_page).Hide();
				}
				catch
				{
				}
			}
		}

		private void CaptureGameplayInputBlock()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!_cursorCaptured && !HistoryUi.IsTitleScene)
			{
				_prevLockMode = (CursorLockMode)1;
				_prevCursorVisible = false;
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				_cursorCaptured = true;
			}
		}

		private void ReleaseGameplayInputBlock()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (!_cursorCaptured)
			{
				return;
			}
			_cursorCaptured = false;
			try
			{
				Cursor.lockState = _prevLockMode;
				Cursor.visible = _prevCursorVisible;
			}
			catch
			{
			}
		}

		public void TickInputBlock()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (!IsPageOpen || HistoryUi.IsTitleScene)
			{
				if (_cursorCaptured && !IsPageOpen)
				{
					ReleaseGameplayInputBlock();
				}
				return;
			}
			if (!_cursorCaptured)
			{
				CaptureGameplayInputBlock();
			}
			if ((int)Cursor.lockState != 0)
			{
				Cursor.lockState = (CursorLockMode)0;
			}
			if (!Cursor.visible)
			{
				Cursor.visible = true;
			}
		}

		private void EnsurePageBlocksRaycasts()
		{
			if (!HasLivePage)
			{
				return;
			}
			try
			{
				GameObject gameObject = ((Component)_page).gameObject;
				Canvas component = gameObject.GetComponent<Canvas>();
				if ((Object)(object)component != (Object)null && component.sortingOrder < 500)
				{
					component.sortingOrder = 500;
				}
				if ((Object)(object)gameObject.GetComponent<GraphicRaycaster>() == (Object)null)
				{
					gameObject.AddComponent<GraphicRaycaster>();
				}
				Image[] componentsInChildren = gameObject.GetComponentsInChildren<Image>(true);
				foreach (Image val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && (((Object)((Component)val).gameObject).name == "Background" || (Object)(object)((Component)val).transform.parent == (Object)(object)gameObject.transform))
					{
						((Graphic)val).raycastTarget = true;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("EnsurePageBlocksRaycasts: " + ex.Message));
			}
		}

		public void HandleBackOrClose()
		{
			if (IsPageOpen)
			{
				if (_viewingLobbyId != null)
				{
					ShowRoomsView();
				}
				else if (_deleteMode)
				{
					_deleteMode = false;
					RefreshCurrentView();
				}
				else
				{
					Hide();
				}
			}
		}

		public void TickAvatarRefresh()
		{
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			if (!IsPageOpen || Time.frameCount % 45 != 0)
			{
				return;
			}
			for (int num = _avatarRefreshTargets.Count - 1; num >= 0; num--)
			{
				GameObject val = _avatarRefreshTargets[num];
				if ((Object)(object)val == (Object)null)
				{
					_avatarRefreshTargets.RemoveAt(num);
				}
				else
				{
					Image component = val.GetComponent<Image>();
					if ((Object)(object)component == (Object)null || !((Object)val).name.StartsWith("Avatar_", StringComparison.Ordinal))
					{
						_avatarRefreshTargets.RemoveAt(num);
					}
					else
					{
						string steamIdStr = ((Object)val).name.Substring("Avatar_".Length);
						Texture2D avatar = SteamAvatarService.GetAvatar(steamIdStr);
						if (!((Object)(object)avatar == (Object)null))
						{
							for (int num2 = val.transform.childCount - 1; num2 >= 0; num2--)
							{
								Transform child = val.transform.GetChild(num2);
								if (((Object)child).name != "Border")
								{
									Object.Destroy((Object)(object)((Component)child).gameObject);
								}
							}
							if ((Object)(object)component.sprite != (Object)null)
							{
								Sprite sprite = component.sprite;
								component.sprite = null;
								Object.Destroy((Object)(object)sprite);
							}
							component.sprite = Sprite.Create(avatar, new Rect(0f, 0f, (float)((Texture)avatar).width, (float)((Texture)avatar).height), new Vector2(0.5f, 0.5f));
							((Graphic)component).color = Color.white;
							component.preserveAspect = true;
							_avatarRefreshTargets.RemoveAt(num);
						}
					}
				}
			}
		}

		private void EnsurePage()
		{
			//IL_04f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_050c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0520: Unknown result type (might be due to invalid IL or missing references)
			//IL_053b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0545: Expected O, but got Unknown
			//IL_056f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0583: Unknown result type (might be due to invalid IL or missing references)
			//IL_0597: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: 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_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0397: Expected O, but got Unknown
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Expected O, but got Unknown
			//IL_067a: Unknown result type (might be due to invalid IL or missing references)
			//IL_069e: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_06da: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0462: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_077c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0790: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_08cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_08f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_090f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0919: Expected O, but got Unknown
			if (!HasLivePage)
			{
				if ((Object)(object)_page != (Object)null)
				{
					Discard();
				}
				UiButtonFactory.EnsureTemplate();
				_page = MenuAPI.CreatePageWithBackground("RoomHub Page");
				_page.CloseOnUICancel = false;
				_page.BlocksPlayerInput = true;
				_page.ShowCursorWhileOpen = true;
				EnsurePageBlocksRaycasts();
				_titleText = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(Loc.T("ROOMHISTORY_PAGE_TITLE")).SetFontSize(44f), ((Component)_page).transform), new Vector2(0.5f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -36f));
				HistoryUiFont.Apply(_titleText);
				HistoryUiFont.ForceCenteredLabel(_titleText, 900f, 50f, new Vector2(0f, -36f));
				_hintText = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText("").SetFontSize(20f).SetColor(new Color(1f, 0.92f, 0.35f)), ((Component)_page).transform), new Vector2(0.5f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -88f));
				HistoryUiFont.Apply(_hintText);
				HistoryUiFont.ForceCenteredLabel(_hintText, 880f, 36f, new Vector2(0f, -88f));
				_hintText2 = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText("").SetFontSize(18f).SetColor(new Color(0.75f, 0.9f, 1f)), ((Component)_page).transform), new Vector2(0.5f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -118f));
				HistoryUiFont.Apply(_hintText2);
				HistoryUiFont.ForceCenteredLabel(_hintText2, 880f, 32f, new Vector2(0f, -118f));
				_deleteBanner = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(Loc.T("ROOMHISTORY_DELETE_BANNER")).SetFontSize(22f).SetColor(new Color(1f, 0.35f, 0.3f)), ((Component)_page).transform), new Vector2(0.5f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -88f));
				HistoryUiFont.Apply(_deleteBanner);
				HistoryUiFont.ForceCenteredLabel(_deleteBanner, 900f, 40f, new Vector2(0f, -88f));
				((Component)_deleteBanner).gameObject.SetActive(false);
				_backButton = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe("← " + Loc.T("ROOMHISTORY_BACK")), ((Component)_page).transform), new Vector2(0f, 1f)), new Vector2(0f, 1f)), new Vector2(36f, -36f)).SetWidth(170f).OnClick(new UnityAction(ShowRoomsView));
				((Component)_backButton).gameObject.SetActive(false);
				_trashButton = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_DELETE_MODE_ON")), ((Component)_page).transform), new Vector2(1f, 1f)), new Vector2(1f, 1f)), new Vector2(-28f, -36f)).SetWidth(200f).OnClick(new UnityAction(ToggleDeleteMode));
				try
				{
					_searchInput = ElementExtensions.SetSize<PeakTextInput>(ElementExtensions.SetPosition<PeakTextInput>(ElementExtensions.SetPivot<PeakTextInput>(ElementExtensions.SetAnchorMinMax<PeakTextInput>(ElementExtensions.ParentTo<PeakTextInput>(MenuAPI.CreateTextInput("RoomHubSearchInput"), ((Component)_page).transform), new Vector2(1f, 1f)), new Vector2(1f, 1f)), new Vector2(-240f, -100f)), new Vector2(220f, 56f)).SetPlaceholder(Loc.T("ROOMHISTORY_SEARCH_PLACEHOLDER"));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Search input create failed (game not fully ready?): " + ex.Message));
					_searchInput = null;
				}
				_searchButton = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_SEARCH")), ((Component)_page).transform), new Vector2(1f, 1f)), new Vector2(1f, 1f)), new Vector2(-28f, -100f)).SetWidth(140f).OnClick(new UnityAction(RunPlayerSearch));
				_list = ElementExtensions.SetSize<PeakScrollableContent>(ElementExtensions.SetPosition<PeakScrollableContent>(ElementExtensions.SetPivot<PeakScrollableContent>(ElementExtensions.SetAnchorMinMax<PeakScrollableContent>(ElementExtensions.ParentTo<PeakScrollableContent>(MenuAPI.CreateScrollableContent("RoomHubList"), ((Component)_page).transform), new Vector2(0.5f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -200f)), new Vector2(800f, 600f));
				VerticalLayoutGroup component = ((Component)_list.Content).GetComponent<VerticalLayoutGroup>();
				if ((Object)(object)component != (Object)null)
				{
					((HorizontalOrVerticalLayoutGroup)component).spacing = 8f;
					((LayoutGroup)component).childAlignment = (TextAnchor)1;
					((HorizontalOrVerticalLayoutGroup)component).childControlWidth = false;
					((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false;
					((HorizontalOrVerticalLayoutGroup)component).childControlHeight = false;
					((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false;
				}
				_rows = new HistoryRowFactory((Transform)(object)_list.Content, 800f, 120f, 110f, _avatarRefreshTargets, RefreshCurrentView, ShowPlayersView, OnRoomRowClicked);
				_friendPanelTitle = ElementExtensions.SetSize<PeakText>(ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(Loc.T("ROOMHISTORY_FRIENDS_TITLE")).SetFontSize(32f).SetColor(new Color(1f, 0.92f, 0.35f)), ((Component)_page).transform), new Vector2(0f, 1f)), new Vector2(0.5f, 1f)), new Vector2(240f, -100f)), new Vector2(400f, 40f));
				HistoryUiFont.Apply(_friendPanelTitle);
				if ((Object)(object)_friendPanelTitle.TextMesh != (Object)null)
				{
					((TMP_Text)_friendPanelTitle.TextMesh).alignment = (TextAlignmentOptions)514;
					((TMP_Text)_friendPanelTitle.TextMesh).enableAutoSizing = true;
					((TMP_Text)_friendPanelTitle.TextMesh).fontSizeMin = 20f;
					((TMP_Text)_friendPanelTitle.TextMesh).fontSizeMax = 36f;
				}
				_friendList = ElementExtensions.SetSize<PeakScrollableContent>(ElementExtensions.SetPosition<PeakScrollableContent>(ElementExtensions.SetPivot<PeakScrollableContent>(ElementExtensions.SetAnchorMinMax<PeakScrollableContent>(ElementExtensions.ParentTo<PeakScrollableContent>(MenuAPI.CreateScrollableContent("RoomHubFriendList"), ((Component)_page).transform), new Vector2(0f, 1f)), new Vector2(0f, 1f)), new Vector2(40f, -150f)), new Vector2(400f, 650f));
				VerticalLayoutGroup component2 = ((Component)_friendList.Content).GetComponent<VerticalLayoutGroup>();
				if ((Object)(object)component2 != (Object)null)
				{
					((HorizontalOrVerticalLayoutGroup)component2).spacing = 8f;
					((LayoutGroup)component2).childAlignment = (TextAnchor)1;
					((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = false;
					((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = false;
					((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = false;
					((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false;
				}
				((Component)_friendList).gameObject.SetActive(false);
				_friendRows = new HistoryRowFactory((Transform)(object)_friendList.Content, 400f, 120f, 110f, _avatarRefreshTargets, RefreshCurrentView, ShowPlayersView, OnRoomRowClicked);
				JoinGate.StateChanged += OnExternalStateChanged;
				FriendRoomsService.Changed += OnExternalStateChanged;
				LocalizationSync.Register(OnGameLanguageChanged);
				_closeButton = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_CLOSE")), ((Component)_page).transform), new Vector2(0.5f, 0f)), new Vector2(0.5f, 0f)), new Vector2(0f, 80f)).SetWidth(220f).OnClick((UnityAction)delegate
				{
					_deleteMode = false;
					Hide();
				});
			}
		}

		private void OnGameLanguageChanged()
		{
			try
			{
				RefreshStaticChrome();
				if (IsPageOpen)
				{
					RefreshCurrentView();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("OnGameLanguageChanged: " + ex.Message));
			}
		}

		private void RefreshStaticChrome()
		{
			PeakText? friendPanelTitle = _friendPanelTitle;
			if ((Object)(object)((friendPanelTitle != null) ? friendPanelTitle.TextMesh : null) != (Object)null)
			{
				((TMP_Text)_friendPanelTitle.TextMesh).text = Loc.T("ROOMHISTORY_FRIENDS_TITLE");
				HistoryUiFont.Apply(_friendPanelTitle);
				((TMP_Text)_friendPanelTitle.TextMesh).ForceMeshUpdate(true, false);
			}
			if ((Object)(object)_closeButton != (Object)null)
			{
				try
				{
					_closeButton.SetText(Loc.T("ROOMHISTORY_CLOSE"));
				}
				catch
				{
				}
			}
		}

		private void ToggleDeleteMode()
		{
			if (_viewingLobbyId == null)
			{
				_deleteMode = !_deleteMode;
				RefreshCurrentView();
			}
		}

		private void ShowRoomsView()
		{
			_viewingLobbyId = null;
			_searchQuery = null;
			_deleteMode = false;
			RefreshCurrentView();
		}

		private void ShowPlayersView(string lobbyId)
		{
			_viewingLobbyId = lobbyId;
			_searchQuery = null;
			_deleteMode = false;
			RefreshCurrentView();
		}

		private void UnsubscribeExternalEvents()
		{
			JoinGate.StateChanged -= OnExternalStateChanged;
			FriendRoomsService.Changed -= OnExternalStateChanged;
		}

		private void OnExternalStateChanged()
		{
			if (!IsPageOpen)
			{
				return;
			}
			try
			{
				RefreshCurrentView();
			}
			catch
			{
			}
		}

		private void RunPlayerSearch()
		{
			string text = "";
			try
			{
				PeakTextInput? searchInput = _searchInput;
				if ((Object)(object)((searchInput != null) ? searchInput.InputField : null) != (Object)null)
				{
					text = _searchInput.InputField.text ?? "";
				}
			}
			catch
			{
			}
			text = text.Trim();
			if (text.Length == 0)
			{
				ShowRoomsView();
				return;
			}
			_searchQuery = text;
			_viewingLobbyId = "__search__";
			_deleteMode = false;
			RefreshCurrentView();
		}

		private void RefreshCurrentView()
		{
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_045b: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_057e: Unknown result type (might be due to invalid IL or missing references)
			//IL_052e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0545: Unknown result type (might be due to invalid IL or missing references)
			//IL_0649: Unknown result type (might be due to invalid IL or missing references)
			//IL_0678: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0407: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06fc: Expected O, but got Unknown
			//IL_0751: Unknown result type (might be due to invalid IL or missing references)
			//IL_0758: Expected O, but got Unknown
			if ((Object)(object)_list == (Object)null || (Object)(object)_page == (Object)null || _rows == null)
			{
				return;
			}
			_avatarRefreshTargets.Clear();
			bool flag = _viewingLobbyId == "__search__";
			bool flag2 = _viewingLobbyId != null && !flag;
			bool flag3 = _viewingLobbyId != null;
			bool flag4 = !flag3;
			if ((Object)(object)_backButton != (Object)null)
			{
				((Component)_backButton).gameObject.SetActive(flag3);
				try
				{
					_backButton.SetText("← " + Loc.T("ROOMHISTORY_BACK"));
				}
				catch
				{
				}
			}
			if ((Object)(object)_trashButton != (Object)null)
			{
				((Component)_trashButton).gameObject.SetActive(flag4);
				try
				{
					_trashButton.SetText(_deleteMode ? Loc.T("ROOMHISTORY_DELETE_MODE_OFF") : Loc.T("ROOMHISTORY_DELETE_MODE_ON"));
					_trashButton.SetWidth(200f);
					_trashButton.SetColor(_deleteMode ? new Color(0.55f, 0.12f, 0.12f) : new Color(0.22f, 0.18f, 0.15f), true);
				}
				catch
				{
				}
			}
			if ((Object)(object)_searchButton != (Object)null)
			{
				((Component)_searchButton).gameObject.SetActive(flag4);
				try
				{
					_searchButton.SetText(Loc.T("ROOMHISTORY_SEARCH"));
				}
				catch
				{
				}
			}
			if ((Object)(object)_searchInput != (Object)null)
			{
				((Component)_searchInput).gameObject.SetActive(flag4);
				try
				{
					if (flag4)
					{
						_searchInput.SetPlaceholder(Loc.T("ROOMHISTORY_SEARCH_PLACEHOLDER"));
					}
				}
				catch
				{
				}
			}
			PeakText? titleText = _titleText;
			if ((Object)(object)((titleText != null) ? titleText.TextMesh : null) != (Object)null)
			{
				if (flag)
				{
					((TMP_Text)_titleText.TextMesh).text = Loc.T("ROOMHISTORY_SEARCH_TITLE");
				}
				else if (flag2)
				{
					((TMP_Text)_titleText.TextMesh).text = Loc.T("ROOMHISTORY_PLAYERS_TITLE");
				}
				else
				{
					((TMP_Text)_titleText.TextMesh).text = Loc.T("ROOMHISTORY_PAGE_TITLE");
				}
				HistoryUiFont.Apply(_titleText);
				HistoryUiFont.ForceCenteredLabel(_titleText, 900f, 50f, new Vector2(0f, -36f));
				((TMP_Text)_titleText.TextMesh).ForceMeshUpdate(true, false);
			}
			bool flag5 = _deleteMode && flag4;
			PeakText? hintText = _hintText;
			if ((Object)(object)((hintText != null) ? hintText.TextMesh : null) != (Object)null)
			{
				if (flag5)
				{
					((TMP_Text)_hintText.TextMesh).text = "";
					((Component)_hintText).gameObject.SetActive(false);
				}
				else
				{
					((Component)_hintText).gameObject.SetActive(true);
					if (flag)
					{
						((TMP_Text)_hintText.TextMesh).text = (string.IsNullOrEmpty(_searchQuery) ? Loc.T("ROOMHISTORY_SEARCH_HINT") : ("\"" + _searchQuery + "\""));
						((Graphic)_hintText.TextMesh).color = new Color(0.85f, 0.9f, 1f);
					}
					else if (flag2)
					{
						RoomRecord room = Plugin.Store.GetRoom(_viewingLobbyId);
						string text = ((room == null || string.IsNullOrWhiteSpace(room.HostName)) ? Loc.T("ROOMHISTORY_UNKNOWN_HOST") : room.HostName);
						int num = room?.Players.Count ?? 0;
						string text2 = Loc.Tf("ROOMHISTORY_ROOM_TITLE", text);
						if (num > 0)
						{
							text2 = text2 + "  ·  " + Loc.Tf("ROOMHISTORY_PLAYER_COUNT", num);
						}
						((TMP_Text)_hintText.TextMesh).text = text2;
						((Graphic)_hintText.TextMesh).color = new Color(0.85f, 0.9f, 1f);
					}
					else
					{
						((TMP_Text)_hintText.TextMesh).text = Loc.T("ROOMHISTORY_HINT_JOIN_VIEW") + "  ·  " + Loc.T("ROOMHISTORY_HINT_DOUBLECLICK");
						((Graphic)_hintText.TextMesh).color = new Color(1f, 0.92f, 0.35f);
					}
					HistoryUiFont.Apply(_hintText);
					HistoryUiFont.ForceCenteredLabel(_hintText, 880f, 36f, new Vector2(0f, -88f));
					((TMP_Text)_hintText.TextMesh).ForceMeshUpdate(true, false);
				}
			}
			PeakText? hintText2 = _hintText2;
			if ((Object)(object)((hintText2 != null) ? hintText2.TextMesh : null) != (Object)null)
			{
				if (flag5 || flag3)
				{
					((TMP_Text)_hintText2.TextMesh).text = "";
					((Component)_hintText2).gameObject.SetActive(false);
				}
				else
				{
					((Component)_hintText2).gameObject.SetActive(true);
					if (HistoryUi.IsTitleScene)
					{
						((TMP_Text)_hintText2.TextMesh).text = Loc.Tf("ROOMHISTORY_HINT_HOTKEY", Plugin.ModConfig.QuickJoinHotkey.Value, Plugin.ModConfig.ModMenuHotkey.Value);
					}
					else
					{
						((TMP_Text)_hintText2.TextMesh).text = Loc.Tf("ROOMHISTORY_HINT_HOTKEY_INGAME", Plugin.ModConfig.ModMenuHotkey.Value);
					}
					HistoryUiFont.Apply(_hintText2);
					HistoryUiFont.ForceCenteredLabel(_hintText2, 880f, 32f, new Vector2(0f, -118f));
					((TMP_Text)_hintText2.TextMesh).ForceMeshUpdate(true, false);
				}
			}
			if ((Object)(object)_deleteBanner != (Object)null)
			{
				bool flag6 = flag5;
				((Component)_deleteBanner).gameObject.SetActive(flag6);
				if (flag6 && (Object)(object)_deleteBanner.TextMesh != (Object)null)
				{
					((TMP_Text)_deleteBanner.TextMesh).text = Loc.T("ROOMHISTORY_DELETE_BANNER");
					((Graphic)_deleteBanner.TextMesh).color = new Color(1f, 0.35f, 0.3f);
					HistoryUiFont.Apply(_deleteBanner);
					HistoryUiFont.ForceCenteredLabel(_deleteBanner, 900f, 40f, new Vector2(0f, -88f));
					((TMP_Text)_deleteBanner.TextMesh).ForceMeshUpdate(true, false);
				}
			}
			RectTransform component = ((Component)_list).GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.anchoredPosition = new Vector2(0f, flag5 ? (-210f) : (-200f));
			}
			if ((Object)(object)_list != (Object)null)
			{
				foreach (Transform item in (Transform)_list.Content)
				{
					Transform val = item;
					DestroyRowSafely(((Component)val).gameObject);
				}
			}
			if ((Object)(object)_friendList != (Object)null)
			{
				foreach (Transform item2 in (Transform)_friendList.Content)
				{
					Transform val2 = item2;
					DestroyRowSafely(((Component)val2).gameObject);
				}
				((Component)_friendList).gameObject.SetActive(flag4);
			}
			if ((Object)(object)_friendPanelTitle != (Object)null)
			{
				((Component)_friendPanelTitle).gameObject.SetActive(flag4);
			}
			if (flag)
			{
				PopulateSearchResults(_searchQuery ?? "");
				return;
			}
			if (flag2)
			{
				PopulatePlayers(_viewingLobbyId);
				return;
			}
			PopulateFriendPanel();
			PopulateRooms();
		}

		private static void DestroyRowSafely(GameObject row)
		{
			if ((Object)(object)row == (Object)null)
			{
				return;
			}
			try
			{
				Image[] componentsInChildren = row.GetComponentsInChildren<Image>(true);
				foreach (Image val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sprite == (Object)null) && ((Object)((Component)val).gameObject).name.StartsWith("Avatar", StringComparison.Ordinal))
					{
						Sprite sprite = val.sprite;
						val.sprite = null;
						Object.Destroy((Object)(object)sprite);
					}
				}
			}
			catch
			{
			}
			Object.Destroy((Object)(object)row);
		}

		private void PopulateFriendPanel()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<FriendRoomInfo> friends = FriendRoomsService.GetFriends();
			if (friends.Count == 0)
			{
				PeakText peakText = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(Loc.T("ROOMHISTORY_FRIENDS_EMPTY")).SetFontSize(22f).SetColor(Color.yellow), (Transform)(object)_friendList.Content), new Vector2(0.5f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -40f));
				HistoryUiFont.Apply(peakText);
				return;
			}
			foreach (FriendRoomInfo item in friends)
			{
				_friendRows.CreateFriendRow(item);
			}
		}

		private void PopulateSearchResults(string query)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<PlayerSearchHit> readOnlyList = Plugin.Store.SearchPlayers(query);
			if (readOnlyList.Count == 0)
			{
				PeakText peakText = ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(Loc.T("ROOMHISTORY_SEARCH_EMPTY")).SetFontSize(28f).SetColor(Color.yellow), (Transform)(object)_list.Content);
				HistoryUiFont.Apply(peakText);
				return;
			}
			foreach (PlayerSearchHit item in readOnlyList)
			{
				_rows.CreateSearchHitRow(item);
			}
		}

		private void PopulateRooms()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<RoomRecord> rooms = Plugin.Store.GetRooms();
			if (rooms.Count == 0)
			{
				PeakText peakText = ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(Loc.T("ROOMHISTORY_EMPTY_ROOMS")).SetFontSize(28f).SetColor(Color.yellow), (Transform)(object)_list.Content);
				HistoryUiFont.Apply(peakText);
				return;
			}
			foreach (RoomRecord item in rooms)
			{
				_rows.CreateRoomRow(item, _deleteMode);
			}
		}

		private void PopulatePlayers(string lobbyId)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
			RoomRecord room = Plugin.Store.GetRoom(lobbyId);
			if (room == null || room.Players.Count == 0)
			{
				PeakText val = ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(Loc.T("ROOMHISTORY_EMPTY_PLAYERS")).SetFontSize(28f).SetColor(Color.yellow), (Transform)(object)_list.Content);
				HistoryUiFont.Apply(val);
				RectTransform component = ((Component)val).GetComponent<RectTransform>();
				if ((Object)(object)component != (Object)null)
				{
					ElementExtensions.SetAnchorMinMax<PeakText>(val, new Vector2(0.5f, 1f));
					ElementExtensions.SetPivot<PeakText>(val, new Vector2(0.5f, 1f));
					ElementExtensions.SetPosition<PeakText>(val, new Vector2(0f, -20f));
				}
				return;
			}
			foreach (PlayerRecord item in from x in room.Players
				orderby x.WasHost descending, x.LastSeenUtcTicks descending
				select x)
			{
				_rows.CreatePlayerRow(item);
			}
		}

		private void OnRoomRowClicked(RoomRecord room)
		{
			float unscaledTime = Time.unscaledTime;
			bool flag = _lastClickLobbyId != null && string.Equals(_lastClickLobbyId, room.LobbyId, StringComparison.Ordinal) && unscaledTime - _lastClickTime <= 0.4f;
			_lastClickLobbyId = room.LobbyId;
			_lastClickTime = unscaledTime;
			if (flag)
			{
				if (!HistoryUi.IsTitleScene && HistoryUi.IsInLiveLobby)
				{
					Plugin.Log.LogDebug((object)"Double-click join ignored: already in live lobby.");
					return;
				}
				_lastClickLobbyId = null;
				RoomJoiner.TryJoin(room);
			}
		}
	}
	internal sealed class HistoryRowFactory
	{
		private readonly Transform _listContent;

		private readonly float _listWidth;

		private readonly float _roomRowHeight;

		private readonly float _playerRowHeight;

		private readonly List<GameObject> _avatarRefreshTargets;

		private readonly Action _refreshView;

		private readonly Action<string> _showPlayers;

		private readonly Action<RoomRecord> _onRoomRowClicked;

		public HistoryRowFactory(Transform listContent, float listWidth, float roomRowHeight, float playerRowHeight, List<GameObject> avatarRefreshTargets, Action refreshView, Action<string> showPlayers, Action<RoomRecord> onRoomRowClicked)
		{
			_listContent = listContent;
			_listWidth = listWidth;
			_roomRowHeight = roomRowHeight;
			_playerRowHeight = playerRowHeight;
			_avatarRefreshTargets = avatarRefreshTargets;
			_refreshView = refreshView;
			_showPlayers = showPlayers;
			_onRoomRowClicked = onRoomRowClicked;
		}

		public void CreateRoomRow(RoomRecord room, bool deleteMode)
		{
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Expected O, but got Unknown
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Expected O, but got Unknown
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: 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_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Expected O, but got Unknown
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Expected O, but got Unknown
			string text = (string.IsNullOrWhiteSpace(room.HostName) ? Loc.T("ROOMHISTORY_UNKNOWN_HOST") : room.HostName);
			string item = Loc.Tf("ROOMHISTORY_ROOM_TITLE", text);
			string text2 = Loc.Tf("ROOMHISTORY_JOINED_AGO", Loc.Relative(room.JoinedAtUtc));
			PeakButton val = ElementExtensions.ParentTo<PeakButton>(MenuAPI.CreateButton("Room_" + room.LobbyId), _listContent).SetHeight(_roomRowHeight);
			PinRowSize(((Component)val).gameObject, _listWidth, _roomRowHeight, pinMinHeight: true);
			if (deleteMode)
			{
				Image componentInChildren = ((Component)val).GetComponentInChildren<Image>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					((Graphic)componentInChildren).color = new Color(0.55f, 0.12f, 0.12f, 0.95f);
				}
			}
			PeakText text3 = val.Text;
			if ((Object)(object)((text3 != null) ? text3.TextMesh : null) != (Object)null)
			{
				((TMP_Text)val.Text.TextMesh).text = "";
			}
			CreateAvatarBox(((Component)val).transform, room.HostSteamId, text, new Vector2(12f, 0f), 72f);
			List<string> list = new List<string>
			{
				item,
				"<size=20><color=#CCCCCC>" + text2 + "</color></size>"
			};
			if (!string.IsNullOrWhiteSpace(room.LobbyName))
			{
				list.Add("<size=18><color=#DDDD88>" + Loc.Tf("ROOMHISTORY_LOBBY_NAME", room.LobbyName) + "</color></size>");
			}
			if (room.Players.Count > 0)
			{
				list.Add("<size=18><color=#88CCFF>" + Loc.Tf("ROOMHISTORY_PLAYER_COUNT", room.Players.Count) + "</color></size>");
			}
			if (deleteMode)
			{
				list.Add("<size=18><color=#FF8888>" + Loc.T("ROOMHISTORY_DELETE_ROOM") + "</color></size>");
			}
			string body = string.Join("\n", list);
			CreateBodyText(((Component)val).transform, body);
			if (deleteMode)
			{
				val.OnClick((UnityAction)delegate
				{
					Plugin.Store.DeleteRoom(room.LobbyId);
					_refreshView();
				});
				return;
			}
			val.OnClick((UnityAction)delegate
			{
				_onRoomRowClicked(room);
			});
			bool flag = HistoryUi.IsTitleScene || !HistoryUi.IsInLiveLobby;
			if (flag && !JoinGate.IsJoinAllowed(room.LobbyId, room.JoinedAtUtcTicks))
			{
				flag = false;
			}
			if (flag)
			{
				ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_JOIN")), ((Component)val).transform), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(-16f, 22f)).SetWidth(150f).OnClick((UnityAction)delegate
				{
					RoomJoiner.TryJoin(room);
				});
			}
			ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_VIEW")), ((Component)val).transform), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(-16f, flag ? (-28f) : 0f)).SetWidth(150f).OnClick((UnityAction)delegate
			{
				_showPlayers(room.LobbyId);
			});
		}

		public void CreatePlayerRow(PlayerRecord player)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			string text = (string.IsNullOrWhiteSpace(player.DisplayName) ? Loc.T("ROOMHISTORY_UNKNOWN_PLAYER") : player.DisplayName);
			PeakButton val = ElementExtensions.ParentTo<PeakButton>(MenuAPI.CreateButton("Player_" + player.SteamId), _listContent).SetHeight(_playerRowHeight);
			PinRowSize(((Component)val).gameObject, _listWidth, _playerRowHeight, pinMinHeight: false);
			PeakText text2 = val.Text;
			if ((Object)(object)((text2 != null) ? text2.TextMesh : null) != (Object)null)
			{
				((TMP_Text)val.Text.TextMesh).text = "";
			}
			CreateAvatarBox(((Component)val).transform, player.SteamId, text, new Vector2(12f, 0f), 72f);
			List<string> list = new List<string>();
			if (player.WasHost)
			{
				list.Add("<color=#FFD070>" + Loc.T("ROOMHISTORY_HOST_BADGE") + "</color>");
			}
			bool flag = SteamAvatarService.IsFriend(player.SteamId);
			if (flag)
			{
				list.Add("<color=#88CCFF>" + Loc.T("ROOMHISTORY_ALREADY_FRIEND") + "</color>");
			}
			string text3 = text + ((list.Count > 0) ? ("  " + string.Join(" ", list)) : "");
			string text4 = "<size=18><color=#CCCCCC>" + Loc.Tf("ROOMHISTORY_MET_AGO", Loc.Relative(player.LastSeenUtc)) + "</color></size>";
			CreateBodyText(((Component)val).transform, text3 + "\n" + text4);
			AddPlayerActionButtons(((Component)val).transform, player.SteamId, flag);
		}

		public void CreateSearchHitRow(PlayerSearchHit hit)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			string text = (string.IsNullOrWhiteSpace(hit.DisplayName) ? Loc.T("ROOMHISTORY_UNKNOWN_PLAYER") : hit.DisplayName);
			PeakButton val = ElementExtensions.ParentTo<PeakButton>(MenuAPI.CreateButton("Search_" + hit.SteamId), _listContent).SetHeight(_playerRowHeight);
			PinRowSize(((Component)val).gameObject, _listWidth, _playerRowHeight, pinMinHeight: false);
			PeakText text2 = val.Text;
			if ((Object)(object)((text2 != null) ? text2.TextMesh : null) != (Object)null)
			{
				((TMP_Text)val.Text.TextMesh).text = "";
			}
			CreateAvatarBox(((Component)val).transform, hit.SteamId, text, new Vector2(12f, 0f), 72f);
			List<string> list = new List<string>();
			if (hit.WasHost)
			{
				list.Add("<color=#FFD070>" + Loc.T("ROOMHISTORY_HOST_BADGE") + "</color>");
			}
			bool flag = SteamAvatarService.IsFriend(hit.SteamId);
			if (flag)
			{
				list.Add("<color=#88CCFF>" + Loc.T("ROOMHISTORY_ALREADY_FRIEND") + "</color>");
			}
			string text3 = text + ((list.Count > 0) ? ("  " + string.Join(" ", list)) : "");
			string text4 = "<size=18><color=#CCCCCC>" + Loc.Tf("ROOMHISTORY_MET_AGO", Loc.Relative(hit.LastSeenUtc)) + "</color></size>  <size=18><color=#DDDD88>" + Loc.Tf("ROOMHISTORY_MET_IN_ROOM", hit.RoomHostName) + "</color></size>";
			CreateBodyText(((Component)val).transform, text3 + "\n" + text4);
			AddPlayerActionButtons(((Component)val).transform, hit.SteamId, flag);
		}

		private void AddPlayerActionButtons(Transform row, string steamId, bool friend)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			string text = SteamAvatarService.LocalSteamIdString();
			if (text != null && string.Equals(text, steamId, StringComparison.Ordinal))
			{
				return;
			}
			string sid = steamId;
			bool canShowInvite = HistoryUi.CanShowInvite;
			if (canShowInvite)
			{
				PeakMenuButton inviteBtn = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_INVITE")), row), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(-16f, 22f)).SetWidth(150f);
				inviteBtn.OnClick((UnityAction)delegate
				{
					//IL_006a: Unknown result type (might be due to invalid IL or missing references)
					if (!SteamInviteService.InvitePlayer(sid))
					{
						return;
					}
					try
					{
						if ((Object)(object)inviteBtn.Button != (Object)null)
						{
							((Selectable)inviteBtn.Button).interactable = false;
						}
						if ((Object)(object)((PeakLocalizableElement)inviteBtn).Text != (Object)null)
						{
							((Graphic)((PeakLocalizableElement)inviteBtn).Text).color = new Color(0.6f, 0.6f, 0.6f, 1f);
						}
						inviteBtn.SetText(Loc.T("ROOMHISTORY_INVITED"));
					}
					catch
					{
					}
				});
			}
			string text2 = (friend ? Loc.T("ROOMHISTORY_ALREADY_FRIEND") : Loc.T("ROOMHISTORY_ADD_FRIEND"));
			PeakMenuButton val = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(text2), row), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(-16f, canShowInvite ? (-28f) : 0f)).SetWidth(150f);
			if (!friend)
			{
				val.OnClick((UnityAction)delegate
				{
					SteamAvatarService.OpenAddFriend(sid);
				});
				return;
			}
			try
			{
				if ((Object)(object)val.Button != (Object)null)
				{
					((Selectable)val.Button).interactable = false;
				}
				if ((Object)(object)((PeakLocalizableElement)val).Text != (Object)null)
				{
					((Graphic)((PeakLocalizableElement)val).Text).color = new Color(0.6f, 0.6f, 0.6f, 1f);
				}
			}
			catch
			{
			}
		}

		public void CreateFriendRow(FriendRoomInfo friend)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Expected O, but got Unknown
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Expected O, but got Unknown
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			string text = (string.IsNullOrWhiteSpace(friend.Name) ? Loc.T("ROOMHISTORY_UNKNOWN_PLAYER") : friend.Name);
			PeakButton val = ElementExtensions.ParentTo<PeakButton>(MenuAPI.CreateButton("Friend_" + friend.SteamId), _listContent).SetHeight(_playerRowHeight);
			PinRowSize(((Component)val).gameObject, _listWidth, _playerRowHeight, pinMinHeight: false);
			PeakText text2 = val.Text;
			if ((Object)(object)((text2 != null) ? text2.TextMesh : null) != (Object)null)
			{
				((TMP_Text)val.Text.TextMesh).text = "";
			}
			CreateAvatarBox(((Component)val).transform, friend.SteamId.ToString(), text, new Vector2(12f, 0f), 72f);
			FriendGameMode mode = friend.Mode;
			string text3 = mode switch
			{
				FriendGameMode.Joinable => "<color=#88FF88>" + Loc.T("ROOMHISTORY_FRIEND_IN_GAME") + "</color>", 
				FriendGameMode.Solo => "<color=#A0A0A0>" + Loc.T("ROOMHISTORY_FRIEND_SOLO") + "</color>", 
				FriendGameMode.InviteOnly => "<color=#A0A0A0>" + Loc.T("ROOMHISTORY_FRIEND_INVITE_ONLY") + "</color>", 
				FriendGameMode.Unknown => "<color=#A0A0A0>" + Loc.T("ROOMHISTORY_FRIEND_UNKNOWN") + "</color>", 
				_ => "<color=#88FF88>" + Loc.T("ROOMHISTORY_FRIEND_MAIN_MENU") + "</color>", 
			};
			string text4 = text;
			string text5 = "<size=20>" + text3 + "</size>";
			CreateBodyText(((Component)val).transform, text4 + "\n" + text5);
			bool flag = mode == FriendGameMode.Joinable && (HistoryUi.IsTitleScene || !HistoryUi.IsInLiveLobby);
			bool flag2 = mode == FriendGameMode.MainMenu && HistoryUi.IsInLiveLobby;
			if (flag)
			{
				string lobbyId = friend.LobbyId.ToString();
				ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_JOIN")), ((Component)val).transform), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(-16f, 0f)).SetWidth(150f).OnClick((UnityAction)delegate
				{
					RoomJoiner.TryJoinLobbyId(lobbyId, hidePage: true);
				});
				return;
			}
			if (flag2)
			{
				string sid = friend.SteamId.ToString();
				PeakMenuButton inviteBtn = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(UiButtonFactory.CreateMenuButtonSafe(Loc.T("ROOMHISTORY_INVITE")), ((Component)val).transform), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(-16f, 0f)).SetWidth(150f);
				inviteBtn.OnClick((UnityAction)delegate
				{
					//IL_0065: Unknown result type (might be due to invalid IL or missing references)
					if (!SteamInviteService.InvitePlayer(sid))
					{
						return;
					}
					try
					{
						if ((Object)(object)inviteBtn.Button != (Object)null)
						{
							((Selectable)inviteBtn.Button).interactable = false;
						}
						if ((Object)(object)((PeakLocalizableElement)inviteBtn).Text != (Object)null)
						{
							((Graphic)((PeakLocalizableElement)inviteBtn).Text).color = new Color(0.6f, 0.6f, 0.6f, 1f);
						}
						inviteBtn.SetText(Loc.T("ROOMHISTORY_INVITED"));
					}
					catch
					{
					}
				});
				return;
			}
			string text6 = mode switch
			{
				FriendGameMode.MainMenu => Loc.T("ROOMHISTORY_FRIEND_MAIN_MENU"), 
				FriendGameMode.Unknown => Loc.T("ROOMHISTORY_FRIEND_UNKNOWN"), 
				_ => Loc.T("ROOMHISTORY_FRIEND_IN_GAME"), 
			};
			try
			{
				PeakText val2 = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(text6), ((Component)val).transform), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(-16f, 0f)).SetFontSize(20f);
				HistoryUiFont.Apply(val2);
				if ((Object)(object)val2.TextMesh != (Object)null)
				{
					((Graphic)val2.TextMesh).color = new Color(0.6f, 0.6f, 0.6f, 1f);
				}
			}
			catch
			{
			}
		}

		private void CreateBodyText(Transform parent, string body)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			PeakText val = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(body), parent), new Vector2(0f, 0.5f)), new Vector2(0f, 0.5f)), new Vector2(96f, 0f)).SetFontSize(24f);
			HistoryUiFont.Apply(val);
			if ((Object)(object)val.TextMesh != (Object)null)
			{
				((Graphic)val.TextMesh).color = Color.white;
				((TMP_Text)val.TextMesh).richText = true;
				((Graphic)val.TextMesh).raycastTarget = false;
				((TMP_Text)val.TextMesh).text = body;
				((TMP_Text)val.TextMesh).ForceMeshUpdate(true, false);
			}
		}

		private void CreateAvatarBox(Transform parent, string steamId, string displayName, Vector2 pos, float size)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: 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_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(string.IsNullOrEmpty(steamId) ? "AvatarPlaceholder" : ("Avatar_" + steamId), new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 0.5f);
			component.anchorMax = val2;
			component.anchorMin = val2;
			component.pivot = new Vector2(0f, 0.5f);
			component.anchoredPosition = pos;
			component.sizeDelta = new Vector2(size, size);
			Image component2 = val.GetComponent<Image>();
			((Graphic)component2).color = new Color(0.18f, 0.14f, 0.11f, 1f);
			((Graphic)component2).raycastTarget = false;
			Texture2D val3 = null;
			if (!string.IsNullOrEmpty(steamId))
			{
				val3 = SteamAvatarService.GetAvatar(steamId);
				if ((Object)(object)val3 == (Object)null)
				{
					_avatarRefreshTargets.Add(val);
				}
			}
			if ((Object)(object)val3 != (Object)null)
			{
				component2.sprite = Sprite.Create(val3, new Rect(0f, 0f, (float)((Texture)val3).width, (float)((Texture)val3).height), new Vector2(0.5f, 0.5f));
				((Graphic)component2).color = Color.white;
				component2.preserveAspect = true;
				return;
			}
			string text = "?";
			if (!string.IsNullOrWhiteSpace(displayName))
			{
				text = displayName.Trim().Substring(0, 1);
			}
			PeakText val4 = ElementExtensions.SetPosition<PeakText>(ElementExtensions.SetPivot<PeakText>(ElementExtensions.SetAnchorMinMax<PeakText>(ElementExtensions.ParentTo<PeakText>(MenuAPI.CreateText(text), val.transform), new Vector2(0.5f, 0.5f)), new Vector2(0.5f, 0.5f)), Vector2.zero).SetFontSize(size * 0.4f);
			HistoryUiFont.Apply(val4);
			if ((Object)(object)val4.TextMesh != (Object)null)
			{
				((TMP_Text)val4.TextMesh).alignment = (TextAlignmentOptions)514;
				((Graphic)val4.TextMesh).color = Color.white;
				((TMP_Text)val4.TextMesh).text = text;
			}
		}

		private static void PinRowSize(GameObject go, float width, float height, bool pinMinHeight)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = go.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.sizeDelta = new Vector2(width, height);
			}
			LayoutElement val = go.GetComponent<LayoutElement>() ?? go.AddComponent<LayoutElement>();
			val.preferredWidth = width;
			val.minWidth = width;
			val.preferredHeight = height;
			if (pinMinHeight)
			{
				val.minHeight = height;
			}
		}
	}
	public sealed class HistoryStore
	{
		private const int FormatVersion = 3;

		private const char Sep = '\t';

		private static readonly Regex RichTextTag = new Regex("<[^>]+>", RegexOptions.Compiled);

		private readonly string _filePath;

		private readonly string _legacyReTracePath;

		private readonly string _legacyQuickRejoinPath;

		private readonly PluginConfig _config;

		private readonly object _lock = new object();

		private List<RoomRecord> _rooms = new List<RoomRecord>();

		private bool _dirty;

		private float _lastDirtyUnscaledTime;

		public string StoragePath => _filePath;

		public HistoryStore(string configPath, PluginConfig config)
		{
			_config = config;
			string text = Path.Combine(configPath, "RoomHub");
			Directory.CreateDirectory(text);
			_filePath = Path.Combine(text, "history.tsv");
			_legacyReTracePath = Path.Combine(configPath, "ReTrace", "history.tsv");
			_legacyQuickRejoinPath = Path.Combine(configPath, "QuickRejoin", "recent_rooms.tsv");
		}

		public IReadOnlyList<RoomRecord> GetRooms()
		{
			lock (_lock)
			{
				PruneUnlocked(saveIfChanged: true);
				return _rooms.OrderByDescending((RoomRecord r) => r.JoinedAtUtcTicks).Take(Math.Max(1, _config.MaxRecentRooms.Value)).Select(CloneRoom)
					.ToList();
			}
		}

		public RoomRecord? GetMostRecent()
		{
			IReadOnlyList<RoomRecord> rooms = GetRooms();
			if (rooms.Count <= 0)
			{
				return null;
			}
			return rooms[0];
		}

		public RoomRecord? GetRoom(string lobbyId)
		{
			lock (_lock)
			{
				RoomRecord roomRecord = _rooms.FirstOrDefault((RoomRecord x) => string.Equals(x.LobbyId, lobbyId, StringComparison.Ordinal));
				return (roomRecord == null) ? null : CloneRoom(roomRecord);
			}
		}

		public IReadOnlyList<PlayerSearchHit> SearchPlayers(string query)
		{
			string text = (query ?? "").Trim();
			if (text.Length == 0)
			{
				return Array.Empty<PlayerSearchHit>();
			}
			lock (_lock)
			{
				PruneUnlocked(saveIfChanged: true);
				Dictionary<string, PlayerSearchHit> dictionary = new Dictionary<string, PlayerSearchHit>(StringComparer.Ordinal);
				foreach (RoomRecord room in _rooms)
				{
					string roomHostName = (string.IsNullOrWhiteSpace(room.HostName) ? room.LobbyId : room.HostName);
					foreach (PlayerRecord player in room.Players)
					{
						if (!string.IsNullOrWhiteSpace(player.SteamId) && player.DisplayName.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 && (!dictionary.TryGetValue(player.SteamId, out var value) || player.LastSeenUtcTicks > value.LastSeenUtcTicks))
						{
							dictionary[player.SteamId] = new PlayerSearchHit
							{
								SteamId = player.SteamId,
								DisplayName = player.DisplayName,
								WasHost = player.WasHost,
								LastSeenUtcTicks = player.LastSeenUtcTicks,
								LobbyId = room.LobbyId,
								RoomHostName = roomHostName
							};
						}
					}
				}
				return dictionary.Values.OrderByDescending((PlayerSearchHit h) => h.LastSeenUtcTicks).ToList();
			}
		}

		public void UpsertRoom(string lobbyId, string hostSteamId, string hostName, string lobbyName)
		{
			if (string.IsNullOrWhiteSpace(lobbyId))
			{
				return;
			}
			long ticks = DateTime.UtcNow.Ticks;
			long num = 0L;
			lock (_lock)
			{
				RoomRecord roomRecord = _rooms.FirstOrDefault((RoomRecord r) => string.Equals(r.LobbyId, lobbyId, StringComparison.Ordinal));
				if (roomRecord != null)
				{
					num = roomRecord.JoinedAtUtcTicks;
					roomRecord.JoinedAtUtcTicks = ticks;
					if (!string.IsNullOrWhiteSpace(hostSteamId))
					{
						roomRecord.HostSteamId = hostSteamId.Trim();
					}
					if (!string.IsNullOrWhiteSpace(hostName))
					{
						roomRecord.HostName = Sanitize(hostName);
					}
					if (!string.IsNullOrWhiteSpace(lobbyName))
					{
						roomRecord.LobbyName = Sanitize(lobbyName);
					}
					_rooms.Remove(roomRecord);
					_rooms.Insert(0, roomRecord);
				}
				else
				{
					_rooms.Insert(0, new RoomRecord
					{
						LobbyId = lobbyId.Trim(),
						HostSteamId = (hostSteamId ?? "").Trim(),
						HostName = Sanitize(hostName),
						LobbyName = Sanitize(lobbyName),
						JoinedAtUtcTicks = ticks
					});
				}
				PruneUnlocked(saveIfChanged: false);
				SaveUnlocked();
			}
			if (num > 0)
			{
				long num2 = (ticks - num) / 600000000;
				Plugin.Log.LogInfo((object)("Room saved (rejoin): lobby=" + lobbyId + ", host='" + hostName + "', " + $"joinedAt refreshed (+{num2} min since previous) → {_filePath}"));
			}
			else
			{
				Plugin.Log.LogInfo((object)("Room saved (new): lobby=" + lobbyId + ", host='" + hostName + "' → " + _filePath));
			}
		}

		public void TouchRoomJoinedNow(string lobbyId, string reason)
		{
			if (string.IsNullOrWhiteSpace(lobbyId))
			{
				return;
			}
			long ticks = DateTime.UtcNow.Ticks;
			long num = 0L;
			bool flag = false;
			lock (_lock)
			{
				RoomRecord roomRecord = _rooms.FirstOrDefault((RoomRecord r) => string.Equals(r.LobbyId, lobbyId, StringComparison.Ordinal));
				if (roomRecord == null)
				{
					return;
				}
				num = roomRecord.JoinedAtUtcTicks;
				if (ticks > roomRecord.JoinedAtUtcTicks)
				{
					roomRecord.JoinedAtUtcTicks = ticks;
					_rooms.Remove(roomRecord);
					_rooms.Insert(0, roomRecord);
					flag = true;
					MarkDirtyUnlocked();
				}
			}
			if (flag)
			{
				long num2 = ((num > 0) ? ((ticks - num) / 10000000) : 0);
				Plugin.Log.LogInfo((object)$"Room joinedAt touched ({reason}): lobby={lobbyId}, +{num2}s since previous write");
			}
		}

		public void UpsertPlayer(string lobbyId, string steamId, string displayName, bool wasHost)
		{
			if (string.IsNullOrWhiteSpace(lobbyId) || string.IsNullOrWhiteSpace(steamId))
			{
				return;
			}
			lock (_lock)
			{
				RoomRecord roomRecord = _rooms.FirstOrDefault((RoomRecord r) => string.Equals(r.LobbyId, lobbyId, StringComparison.Ordinal));
				if (roomRecord == null)
				{
					return;
				}
				long ticks = DateTime.UtcNow.Ticks;
				string sid = steamId.Trim();
				PlayerRecord playerRecord = roomRecord.Players.FirstOrDefault((PlayerRecord x) => string.Equals(x.SteamId, sid, StringComparison.Ordinal));
				if (playerRecord == null)
				{
					playerRecord = new PlayerRecord
					{
						SteamId = sid,
						DisplayName = Sanitize(displayName),
						WasHost = false,
						FirstSeenUtcTicks = ticks,
						LastSeenUtcTicks = ticks
					};
					roomRecord.Players.Add(playerRecord);
				}
				else
				{
					if (!string.IsNullOrWhiteSpace(displayName))
					{
						playerRecord.DisplayName = Sanitize(displayName);
					}
					playerRecord.LastSeenUtcTicks = ticks;
				}
				if (wasHost)
				{
					ApplySoleHostUnlocked(roomRecord, sid, playerRecord.DisplayName);
				}
				int num = 20;
				if (roomRecord.Players.Count > num)
				{
					roomRecord.Players.Sort((PlayerRecord a, PlayerRecord b) => b.LastSeenUtcTicks.CompareTo(a.LastSeenUtcTicks));
					roomRecord.Players.RemoveRange(num, roomRecord.Players.Count - num);
				}
				MarkDirtyUnlocked();
			}
		}

		public void SetCurrentHost(string lobbyId, string hostSteamId, string? hostDisplayName)
		{
			if (string.IsNullOrWhiteSpace(lobbyId) || string.IsNullOrWhiteSpace(hostSteamId))
			{
				return;
			}
			lock (_lock)
			{
				RoomRecord roomRecord = _rooms.FirstOrDefault((RoomRecord r) => string.Equals(r.LobbyId, lobbyId, StringComparison.Ordinal));
				if (roomRecord != null)
				{
					ApplySoleHostUnlocked(roomRecord, hostSteamId.Trim(), hostDisplayName);
					MarkDirtyUnlocked();
				}
			}
		}

		private static void ApplySoleHostUnlocked(RoomRecord room, string hostSteamId, string? hostDisplayName)
		{
			foreach (PlayerRecord player in room.Players)
			{
				player.WasHost = string.Equals(player.SteamId, hostSteamId, StringComparison.Ordinal);
			}
			room.HostSteamId = hostSteamId;
			if (!string.IsNullOrWhiteSpace(hostDisplayName))
			{
				room.HostName = Sanitize(hostDisplayName);
			}
		}

		public void DeleteRoom(string lobbyId)
		{
			if (string.IsNullOrWhiteSpace(lobbyId))
			{
				return;
			}
			lock (_lock)
			{
				int num = _rooms.RemoveAll((RoomRecord r) => string.Equals(r.LobbyId, lobbyId, StringComparison.Ordinal));
				if (num > 0)
				{
					SaveUnlocked();
					Plugin.Log.LogInfo((object)("Deleted room history: " + lobbyId));
				}
			}
		}

		public void TickDeferredSave(float unscaledTime)
		{
			lock (_lock)
			{
				if (_dirty && !(unscaledTime - _lastDirtyUnscaledTime < 0.75f))
				{
					SaveUnlocked();
				}
			}
		}

		private void MarkDirtyUnlocked()
		{
			_dirty = true;
			try
			{
				_lastDirtyUnscaledTime = Time.unscaledTime;
			}
			catch
			{
				_lastDirtyUnscaledTime = 0f;
			}
		}

		public void Load()
		{
			lock (_lock)
			{
				_rooms = new List<RoomRecord>();
				try
				{
					if (File.Exists(_filePath))
					{
						LoadFileUnlocked(_filePath);
					}
					else if (File.Exists(_legacyReTracePath))
					{
						LoadFileUnlocked(_legacyReTracePath);
						if (_rooms.Count > 0)
						{
							SaveUnlocked();
						}
						Plugin.Log.LogInfo((object)$"Migrated {_rooms.Count} room(s) from ReTrace history.");
					}
					else if (File.Exists(_legacyQuickRejoinPath))
					{
						LoadFileUnlocked(_legacyQuickRejoinPath);
						if (_rooms.Count > 0)
						{
							SaveUnlocked();
						}
						Plugin.Log.LogInfo((object)$"Migrated {_rooms.Count} room(s) from Quick Rejoin TSV.");
					}
					PruneUnlocked(saveIfChanged: true);
					Plugin.Log.LogInfo((object)$"Loaded {_rooms.Count} room(s) from {_filePath}");
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"History load failed: {arg}");
					_rooms = new List<RoomRecord>();
				}
			}
		}

		public void Flush()
		{
			lock (_lock)
			{
				SaveUnlocked();
			}
		}

		private void LoadFileUnlocked(string path)
		{
			string[] array = File.ReadAllLines(path, Encoding.UTF8);
			if (array.Length == 0)
			{
				return;
			}
			int num = 0;
			if (array[0].StartsWith("QR_RECENT_V", StringComparison.Ordinal) || array[0].StartsWith("RETRACE_V", StringComparison.Ordinal) || array[0].StartsWith("ROOMHISTORY_V", StringComparison.Ordinal))
			{
				num = 1;
			}
			Dictionary<string, RoomRecord> dictionary = new Dictionary<string, RoomRecord>(StringComparer.Ordinal);
			for (int i = num; i < array.Length; i++)
			{
				string text = array[i];
				if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#"))
				{
					continue;
				}
				string[] array2 = text.Split('\t');
				if (array2.Length < 2)
				{
					continue;
				}
				long result4;
				if (array2[0] == "R" && array2.Length >= 6)
				{
					if (long.TryParse(array2[5], out var result) && result > 0)
					{
						string text2 = Unescape(array2[1]);
						if (!string.IsNullOrWhiteSpace(text2))
						{
							RoomRecord value = new RoomRecord
							{
								LobbyId = text2,
								HostSteamId = Unescape(array2[2]),
								HostName = Sanitize(Unescape(array2[3])),
								LobbyName = Sanitize(Unescape(array2[4])),
								JoinedAtUtcTicks = result
							};
							dictionary[text2] = value;
						}
					}
				}
				else if (array2[0] == "P" && array2.Length >= 7)
				{
					string key = Unescape(array2[1]);
					if (dictionary.TryGetValue(key, out var value2) && long.TryParse(array2[5], out var result2))
					{
						if (!long.TryParse(array2[6], out var result3))
						{
							result3 = result2;
						}
						value2.Players.Add(new PlayerRecord
						{
							SteamId = Unescape(array2[2]),
							DisplayName = Sanitize(Unescape(array2[3])),
							WasHost = (array2[4] == "1" || array2[4].Equals("true", StringComparison.OrdinalIgnoreCase)),
							FirstSeenUtcTicks = result2,
							LastSeenUtcTicks = result3
						});
					}
				}
				else if (array2.Length == 4 && !array2[0].Equals("R") && !array2[0].Equals("P") && long.TryParse(array2[3], out result4) && result4 > 0)
				{
					string text3 = Unescape(array2[0]);
					if (!string.IsNullOrWhiteSpace(text3))
					{
						dictionary[text3] = new RoomRecord
						{
							LobbyId = text3,
							HostName = Sanitize(Unescape(array2[1])),
							LobbyName = Sanitize(Unescape(array2[2])),
							JoinedAtUtcTicks = result4
						};
					}
				}
			}
			foreach (RoomRecord value3 in dictionary.Values)
			{
				value3.HostName = Sanitize(value3.HostName);
				value3.LobbyName = Sanitize(value3.LobbyName);
				value3.HostSteamId = (value3.HostSteamId ?? "").Trim();
				if (!string.IsNullOrWhiteSpace(value3.HostSteamId))
				{
					ApplySoleHostUnlocked(value3, value3.HostSteamId, value3.HostName);
					continue;
				}
				List<PlayerRecord> list = value3.Players.Where((PlayerRecord p) => p.WasHost).ToList();
				if (list.Count > 1)
				{
					PlayerRecord playerRecord = list.OrderByDescending((PlayerRecord p) => p.LastSeenUtcTicks).First();
					ApplySoleHostUnlocked(value3, playerRecord.SteamId, playerRecord.DisplayName);
				}
			}
			_rooms = dictionary.Values.OrderByDescending((RoomRecord r) => r.JoinedAtUtcTicks).ToList();
		}

		private void PruneUnlocked(bool saveIfChanged)
		{
			int count = Math.Max(1, _config.MaxRecentRooms.Value);
			int num = Math.Max(1, _config.HistoryExpireHours.Value);
			long cutoff = DateTime.UtcNow.AddHours(-num).Ticks;
			int count2 = _rooms.Count;
			_rooms = (from r in _rooms
				where r != null && !string.IsNullOrWhiteSpace(r.LobbyId) && r.JoinedAtUtcTicks >= cutoff
				group r by r.LobbyId into g
				select g.OrderByDescending((RoomRecord x) => x.JoinedAtUtcTicks).First() into r
				orderby r.JoinedAtUtcTicks descending
				select r).Take(count).ToList();
			if (saveIfChanged && _rooms.Count != count2)
			{
				SaveUnlocked();
			}
		}

		private void SaveUnlocked()
		{
			try
			{
				string directoryName = Path.GetDirectoryName(_filePath);
				if (!string.IsNullOrEmpty(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("ROOMHISTORY_V").Append(3).Append('\n');
				stringBuilder.Append("# R lobbyId hostSteamId hostName lobbyName joinedTicks\n");
				stringBuilder.Append("# P lobbyId steamId displayName wasHost firstTicks lastTicks\n");
				foreach (RoomRecord item in _rooms.OrderByDescending((RoomRecord x) => x.JoinedAtUtcTicks))
				{
					stringBuilder.Append("R").Append('\t').Append(Escape(item.LobbyId))
						.Append('\t')
						.Append(Escape(item.HostSteamId))
						.Append('\t')
						.Append(Escape(item.HostName))
						.Append('\t')
						.Append(Escape(item.LobbyName))
						.Append('\t')
						.Append(item.JoinedAtUtcTicks)
						.Append('\n');
					foreach (PlayerRecord item2 in item.Players.OrderByDescending((PlayerRecord x) => x.LastSeenUtcTicks))
					{
						stringBuilder.Append("P").Append('\t').Append(Escape(item.LobbyId))
							.Append('\t')
							.Append(Escape(item2.SteamId))
							.Append('\t')
							.Append(Escape(item2.DisplayName))
							.Append('\t')
							.Append(item2.WasHost ? "1" : "0")
							.Append('\t')
							.Append(item2.FirstSeenUtcTicks)
							.Append('\t')
							.Append(item2.LastSeenUtcTicks)
							.Append('\n');
					}
				}
				string text = _filePath + ".tmp";
				File.WriteAllText(text, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				if (File.Exists(_filePath))
				{
					File.Replace(text, _filePath, null);
				}
				else
				{
					File.Move(text, _filePath);
				}
				_dirty = false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"History save failed: {arg}");
			}
		}

		private static RoomRecord CloneRoom(RoomRecord r)
		{
			RoomRecord roomRecord = new RoomRecord
			{
				LobbyId = r.LobbyId,
				HostSteamId = r.HostSteamId,
				HostName = r.HostName,
				LobbyName = r.LobbyName,
				JoinedAtUtcTicks = r.JoinedAtUtcTicks
			};
			foreach (PlayerRecord player in r.Players)
			{
				roomRecord.Players.Add(new PlayerRecord
				{
					SteamId = player.SteamId,
					DisplayName = player.DisplayName,
					WasHost = player.WasHost,
					FirstSeenUtcTicks = player.FirstSeenUtcTicks,
					LastSeenUtcTicks = player.LastSeenUtcTicks
				});
			}
			return roomRecord;
		}

		private static string Sanitize(string? s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			string text = RichTextTag.Replace(s, "");
			return text.Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ')
				.Trim();
		}

		private static string Escape(string? s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			return s.Replace("\\", "\\\\").Replace("\t", "\\t").Replace("\n", "\\n")
				.Replace("\r", "\\r");
		}

		private static string Unescape(string? s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			for (int i = 0; i < s.Length; i++)
			{
				if (s[i] == '\\' && i + 1 < s.Length)
				{
					char c = s[++i];
					StringBuilder stringBuilder2 = stringBuilder;
					stringBuilder2.Append(c switch
					{
						't' => '\t', 
						'n' => '\n', 
						'r' => '\r', 
						'\\' => '\\', 
						_ => c, 
					});
				}
				else
				{
					stringBuilder.Append(s[i]);
				}
			}
			return stringBuilder.ToString();
		}
	}
	internal static class HistoryUi
	{
		private static HistoryPageController? _page;

		public static bool IsOpen
		{
			get
			{
				if (_page != null)
				{
					return _page.IsPageOpen;
				}
				return false;
			}
		}

		public static bool IsTitleScene
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				Scene activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).IsValid())
				{
					return string.Equals(((Scene)(ref activeScene)).name, "Title", StringComparison.Ordinal);
				}
				return false;
			}
		}

		public static bool IsInLiveLobby
		{
			get
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					SteamLobbyHandler service = GameHandler.GetService<SteamLobbyHandler>();
					CSteamID val = default(CSteamID);
					return service != null && service.InSteamLobby(ref val) && val != CSteamID.Nil;
				}
				catch
				{
					return false;
				}
			}
		}

		public static bool CanShowInvite
		{
			get
			{
				if (!IsTitleScene)
				{
					return IsInLiveLobby;
				}
				return false;
			}
		}

		public static void Open()
		{
			if (!Plugin.ModConfig.Enabled.Value)
			{
				RoomJoiner.ShowFeedback(Loc.T("ROOMHISTORY_DISABLED"), Loc.T("ROOMHISTORY_DISABLED"));
				return;
			}
			try
			{
				PauseMenuHook.ClosePauseMenuIfOpen();
				UiButtonFactory.EnsureTemplate();
				EnsureController();
				_page.Open();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"HistoryUi.Open: {arg}");
				DropPage();
			}
		}

		public static void Hide()
		{
			try
			{
				_page?.Hide();
			}
			catch
			{
			}
		}

		public static void Toggle()
		{
			if (IsOpen)
			{
				Hide();
			}
			else
			{
				Open();
			}
		}

		public static void DropPage()
		{
			try
			{
				_page?.Discard();
			}
			catch
			{
			}
			_page = null;
		}

		public static void OnSceneLoaded()
		{
			DropPage();
		}

		public static void Tick()
		{
			if (_page == null)
			{
				return;
			}
			if (!_page.HasLivePage)
			{
				DropPage();
			}
			else if (_page.IsPageOpen)
			{
				_page.TickInputBlock();
				if (Input.GetKeyDown((KeyCode)27) && !IsTextInputFocused())
				{
					_page.HandleBackOrClose();
				}
				else
				{
					_page.TickAvatarRefresh();
				}
			}
		}

		public static bool IsTextInputFocused()
		{
			try
			{
				EventSystem current = EventSystem.current;
				if ((Object)(object)current == (Object)null)
				{
					return false;
				}
				GameObject currentSelectedGameObject = current.currentSelectedGameObject;
				if ((Object)(object)currentSelectedGameObject == (Object)null)
				{
					return false;
				}
				if ((Object)(object)currentSelectedGameObject.GetComponent<TMP_InputField>() != (Object)null)
				{
					return true;
				}
				if ((Object)(object)currentSelectedGameObject.GetComponent<InputField>() != (Object)null)
				{
					return true;
				}
				Transform parent = currentSelectedGameObject.transform.parent;
				int num = 0;
				while (num < 4 && (Object)(object)parent != (Object)null)
				{
					if ((Object)(object)((Component)parent).GetComponent<TMP_InputField>() != (Object)null || (Object)(object)((Component)parent).GetComponent<InputField>() != (Object)null)
					{
						return true;
					}
					num++;
					parent = parent.parent;
				}
			}
			catch
			{
			}
			return false;
		}

		private static void EnsureController()
		{
			if (_page != null && !_page.HasLivePage)
			{
				DropPage();
			}
			if (_page == null)
			{
				_page = new HistoryPageController();
			}
		}
	}
	internal static class HistoryUiFont
	{
		private static TMP_FontAsset? _uiFont;

		private static Material? _uiFontMat;

		public static void Reset()
		{
			_uiFont = null;
			_uiFontMat = null;
		}

		public static void CaptureFromMenuButton()
		{
			if ((Object)(object)_uiFont != (Object)null)
			{
				return;
			}
			try
			{
				if (TryCaptureFromScene())
				{
					return;
				}
				PeakMenuButton val = UiButtonFactory.CreateMenuButtonSafe("字");
				try
				{
					TextMeshProUGUI componentInChildren = ((Component)val).GetComponentInChildren<TextMeshProUGUI>(true);
					if ((Object)(object)componentInChildren != (Object)null && (Object)(object)((TMP_Text)componentInChildren).font != (Object)null)
					{
						_uiFont = ((TMP_Text)componentInChildren).font;
						_uiFontMat = ((TMP_Text)componentInChildren).fontSharedMaterial;
						Plugin.Log.LogInfo((object)("UI font from PeakMenuButton: " + ((Object)_uiFont).name + " mat=" + (((Object)(object)_uiFontMat != (Object)null) ? ((Object)_uiFontMat).name : "null")));
					}
				}
				finally
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("CaptureUiFont: " + ex.Message));
			}
		}

		private static bool TryCaptureFromScene()
		{
			try
			{
				TextMeshProUGUI[] array = Object.FindObjectsByType<TextMeshProUGUI>((FindObjectsSortMode)0);
				foreach (TextMeshProUGUI val in array)
				{
					if (!((Object)(object)((val != null) ? ((TMP_Text)val).font : null) == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)val).text) && ((Component)val).gameObject.activeInHierarchy && (ContainsCjk(((TMP_Text)val).text) || ((Object)val).name.IndexOf("Button", StringComparison.OrdinalIgnoreCase) >= 0 || (Object)(object)((Component)val).GetComponentInParent<PeakMenuButton>() != (Object)null))
					{
						_uiFont = ((TMP_Text)val).font;
						_uiFontMat = ((TMP_Text)val).fontSharedMaterial;
						Plugin.Log.LogInfo((object)("UI font from scene " + ((Object)val).name + ": " + ((Object)_uiFont).name));
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		public static void Apply(PeakText? peakText)
		{
			if (!((Object)(object)((peakText != null) ? peakText.TextMesh : null) == (Object)null) && !((Object)(object)_uiFont == (Object)null))
			{
				((TMP_Text)peakText.TextMesh).font = _uiFont;
				if ((Object)(object)_uiFontMat != (Object)null)
				{
					((TMP_Text)peakText.TextMesh).fontSharedMaterial = _uiFontMat;
				}
			}
		}

		public static void ForceCenteredLabel(PeakText? peakText, float width, float height, Vector2 anchoredPos)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)peakText == (Object)null)
			{
				return;
			}
			RectTransform component = ((Component)peakText).GetComponent<RectTransform>();
			if (!((Object)(object)component == (Object)null))
			{
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(0.5f, 1f);
				component.anchorMax = val;
				component.anchorMin = val;
				component.pivot = new Vector2(0.5f, 1f);
				component.anchoredPosition = anchoredPos;
				component.sizeDelta = new Vector2(width, height);
				if ((Object)(object)peakText.TextMesh != (Object)null)
				{
					((TMP_Text)peakText.TextMesh).alignment = (TextAlignmentOptions)514;
					((TMP_Text)peakText.TextMesh).textWrappingMode = (TextWrappingModes)0;
					((TMP_Text)peakText.TextMesh).overflowMode = (TextOverflowModes)1;
					((TMP_Text)peakText.TextMesh).enableAutoSizing = true;
					((TMP_Text)peakText.TextMesh).fontSizeMin = 16f;
					((TMP_Text)peakText.TextMesh).fontSizeMax = ((((TMP_Text)peakText.TextMesh).fontSize > 0f) ? ((TMP_Text)peakText.TextMesh).fontSize : 44f);
				}
			}
		}

		private static bool ContainsCjk(string text)
		{
			foreach (char c in text)
			{
				if (c >= '㐀' && c <= '鿿')
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class JoinGate
	{
		private sealed class Entry
		{
			public float CooldownUntil;
		}

		public const double MaxLobbyAgeHours = 2.5;

		private const float JoinFailCooldownSec = 600f;

		private static readonly Dictionary<ulong, Entry> _cooldowns = new Dictionary<ulong, Entry>();

		public static event Action? StateChanged;

		public static bool IsJoinAllowed(string lobbyId, long joinedAtUtcTicks)
		{
			if (joinedAtUtcTicks > 0)
			{
				DateTime dateTime = new DateTime(joinedAtUtcTicks, DateTimeKind.Utc);
				if ((DateTime.UtcNow - dateTime).TotalHours > 2.5)
				{
					return false;
				}
			}
			if (ulong.TryParse(lobbyId, out var result) && result != 0L && _cooldowns.TryGetValue(result, out Entry value) && Time.unscaledTime < value.CooldownUntil)
			{
				return false;
			}
			return true;
		}

		public static void MarkJoinFailed(ulong lobbyId)
		{
			if (lobbyId == 0L)
			{
				return;
			}
			_cooldowns[lobbyId] = new Entry
			{
				CooldownUntil = Time.unscaledTime + 600f
			};
			try
			{
				JoinGate.StateChanged?.Invoke();
			}
			catch
			{
			}
		}
	}
	internal static class LobbyJoinFeedback
	{
		private static Callback<LobbyEnter_t>? _callback;

		private static bool _inited;

		private static float _lastShownUnscaled;

		private const float MinShowIntervalSec = 2f;

		public static void Init()
		{
			if (_inited)
			{
				return;
			}
			try
			{
				_callback = Callback<LobbyEnter_t>.Create((DispatchDelegate<LobbyEnter_t>)OnLobbyEnter);
				_inited = true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("LobbyJoinFeedback init failed: " + ex.Message));
			}
		}

		private static void OnLobbyEnter(LobbyEnter_t param)
		{
			//IL_0000: 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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_0027: 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_004d: 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)
			EChatRoomEnterResponse val = (EChatRoomEnterResponse)param.m_EChatRoomEnterResponse;
			if ((int)val != 1)
			{
				float unscaledTime = Time.unscaledTime;
				if (!(unscaledTime - _lastShownUnscaled < 2f))
				{
					_lastShownUnscaled = unscaledTime;
					string text = Describe(val);
					Plugin.Log.LogInfo((object)($"Join lobby failed lobby={param.m_ulSteamIDLobby} " + $"code={(int)param.m_EChatRoomEnterResponse} ({text})"));
					JoinGate.MarkJoinFailed(param.m_ulSteamIDLobby);
					RoomJoiner.ShowFeedback(Loc.T("ROOMHISTORY_JOIN_FAILED_TITLE"), text);
				}
			}
		}

		private static string Describe(EChatRoomEnterResponse code)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected I4, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected I4, but got Unknown
			return (code - 2) switch
			{
				0 => Loc.T("ROOMHISTORY_JOIN_FAIL_DOESNT_EXIST"), 
				1 => Loc.T("ROOMHISTORY_JOIN_FAIL_NOT_ALLOWED"), 
				2 => Loc.T("ROOMHISTORY_JOIN_FAIL_FULL"), 
				4 => Loc.T("ROOMHISTORY_JOIN_FAIL_BANNED"), 
				_ => Loc.Tf("ROOMHISTORY_JOIN_FAIL_OTHER", (int)code), 
			};
		}
	}
	[HarmonyPatch(typeof(SteamLobbyHandler), "OnLobbyEnter")]
	internal static class SteamLobbyEnterPatch
	{
		private static void Postfix(SteamLobbyHandler __instance, LobbyEnter_t param)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Plugin.ModConfig.Enabled.Value || param.m_EChatRoomEnterResponse != 1)
				{
					return;
				}
				CSteamID val = default(CSteamID);
				((CSteamID)(ref val))..ctor(param.m_ulSteamIDLobby);
				if (val == CSteamID.Nil || param.m_ulSteamIDLobby == 0L)
				{
					return;
				}
				CSteamID steamID = SteamUser.GetSteamID();
				CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(val);
				bool flag = lobbyOwner == steamID;
				string hostSteamId = lobbyOwner.m_SteamID.ToString();
				string lobbyId = val.m_SteamID.ToString();
				if (flag && !Plugin.ModConfig.RecordWhenHosting.Value)
				{
					Plugin.Log.LogDebug((object)"Skip room list entry (hosting).");
					PlayerTracker.SetActiveLobby(lobbyId, hostSteamId);
					return;
				}
				string text = SteamMatchmaking.GetLobbyData(val, "host_Name");
				if (string.IsNullOrWhiteSpace(text))
				{
					try
					{
						text = SteamFriends.GetFriendPersonaName(lobbyOwner);
					}
					catch
					{
						text = "";
					}
				}
				if (string.IsNullOrWhiteSpace(text))
				{
					text = Loc.T("ROOMHISTORY_UNKNOWN_HOST");
				}
				string lobbyName = SteamMatchmaking.GetLobbyData(val, "lobby_Name") ?? "";
				Plugin.Store.UpsertRoom(lobbyId, hostSteamId, text, lobbyName);
				PlayerTracker.SetActiveLobby(lobbyId, hostSteamId);
				SnapshotMembers(val, lobbyOwner);
				Plugin.Store.SetCurrentHost(lobbyId, hostSteamId, text);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Lobby record failed: {arg}");
			}
		}

		private static void SnapshotMembers(CSteamID lobbyId, CSteamID owner)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(lobbyId);
				CSteamID steamID = SteamUser.GetSteamID();
				string lobbyId2 = lobbyId.m_SteamID.ToString();
				for (int i = 0; i < numLobbyMembers; i++)
				{
					CSteamID lobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex(lobbyId, i);
					if (!(lobbyMemberByIndex == CSteamID.Nil) && !(lobbyMemberByIndex == steamID))
					{
						string text;
						try
						{
							text = SteamFriends.GetFriendPersonaName(lobbyMemberByIndex);
						}
						catch
						{
							text = "";
						}
						if (string.IsNullOrWhiteSpace(text))
						{
							text = Loc.T("ROOMHISTORY_UNKNOWN_PLAYER");
						}
						bool wasHost = lobbyMemberByIndex == owner;
						Plugin.Store.UpsertPlayer(lobbyId2, lobbyMemberByIndex.m_Stea