Decompiled source of Room Hub v3.1.1
RoomHub.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 Peak.Network; using Photon.Pun; using Photon.Realtime; using RoomHub.Ui; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zorro.Core; 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+a4772869b5f134b254d4385335d401de87f72dfa")] [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, Airport, Unknown } public enum FriendArea { None, MainMenu, Airport, Shore, Tropics, Alpine, Mesa, Caldera, Kiln, Peak, Roots, Gloom, Citadel, Nadir } public readonly struct FriendRoomInfo { public readonly ulong SteamId; public readonly string Name; public readonly ulong LobbyId; public readonly FriendGameMode Mode; public readonly FriendArea Area; public bool IsJoinable { get { if (Mode == FriendGameMode.Joinable) { return LobbyId != 0; } return false; } } public bool IsInvitable => Mode == FriendGameMode.MainMenu; public FriendRoomInfo(ulong steamId, string name, ulong lobbyId, FriendGameMode mode) : this(steamId, name, lobbyId, mode, FriendArea.None) { } public FriendRoomInfo(ulong steamId, string name, ulong lobbyId, FriendGameMode mode, FriendArea area) { SteamId = steamId; Name = name ?? ""; LobbyId = lobbyId; Mode = mode; Area = area; } } internal static class FriendRoomsService { public const uint PeakAppId = 3527290u; private const float RefreshIntervalSec = 10f; private const float NotifyMinIntervalSec = 1.5f; private const float RpRequestMinIntervalSec = 30f; private const string DisplayTokenPrefix = "#Status_"; private static Callback<PersonaStateChange_t>? _personaChanged; private static Callback<FriendRichPresenceUpdate_t>? _richPresenceUpdate; private static FriendNotifyPump? _pump; private static List<FriendRoomInfo>? _cache; private static string _signature = ""; private static float _nextRefresh; private static float _lastNotify; private static bool _dirty; private static bool _notifyPending; private static bool _inited; private static bool _loggedTokenSeen; private static bool _loggedFallbackSeen; private static readonly Dictionary<ulong, float> _lastRpRequest = new Dictionary<ulong, float>(); public static event Action? Changed; public static void Init() { if (!_inited) { 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)); } StartPump(); } } public static void Shutdown() { try { _personaChanged?.Dispose(); } catch { } _personaChanged = null; try { _richPresenceUpdate?.Dispose(); } catch { } _richPresenceUpdate = null; if ((Object)(object)_pump != (Object)null) { try { Object.Destroy((Object)(object)_pump); } catch { } _pump = null; } _inited = false; _cache = null; _signature = ""; _dirty = false; _notifyPending = false; _lastRpRequest.Clear(); } public static IReadOnlyList<FriendRoomInfo> GetFriends() { RefreshIfDue(force: false); IReadOnlyList<FriendRoomInfo> cache = _cache; return cache ?? Array.Empty<FriendRoomInfo>(); } internal static void Tick() { if (_notifyPending && !(Time.unscaledTime - _lastNotify < 1.5f)) { NotifyChanged(); } } private static void StartPump() { try { if (!((Object)(object)_pump != (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { GameObject gameObject = ((Component)Plugin.Instance).gameObject; _pump = gameObject.GetComponent<FriendNotifyPump>() ?? gameObject.AddComponent<FriendNotifyPump>(); } } catch (Exception ex) { Plugin.Log.LogDebug((object)("FriendRoomsService pump failed: " + ex.Message)); } } private static void RefreshIfDue(bool force) { //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0144: 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_0078: 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_00a4: Unknown result type (might be due to invalid IL or missing references) if (!_inited || !SteamGate.Ready()) { return; } float unscaledTime = Time.unscaledTime; if (!force && !_dirty && _cache != null && unscaledTime < _nextRefresh) { return; } List<FriendRoomInfo> list = new List<FriendRoomInfo>(); HashSet<ulong> hashSet = new HashSet<ulong>(); StringBuilder stringBuilder = new StringBuilder(); try { 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 || !SteamFriends.GetFriendGamePlayed(friendByIndex, ref val) || ((CGameID)(ref val.m_gameID)).AppID().m_AppId != 3527290) { continue; } int num = 0; try { num = SteamFriends.GetFriendRichPresenceKeyCount(friendByIndex); } catch { } if (num != 0 || (int)PersonaState(friendByIndex) != 0) { ulong num2 = 0uL; if (((CSteamID)(ref val.m_steamIDLobby)).IsValid() && val.m_steamIDLobby.m_SteamID != 0L) { num2 = val.m_steamIDLobby.m_SteamID; } string display = ""; string group = ""; string players = ""; string maxPlayers = ""; if (num > 0) { ReadPresence(friendByIndex, out display, out group, out players, out maxPlayers); } FriendArea area; FriendGameMode friendGameMode = Classify(num2, num, display, group, players, maxPlayers, out area); RequestRichPresence(friendByIndex, unscaledTime); string text = ""; try { text = SteamFriends.GetFriendPersonaName(friendByIndex) ?? ""; } catch { } hashSet.Add(friendByIndex.m_SteamID); list.Add(new FriendRoomInfo(friendByIndex.m_SteamID, text, num2, friendGameMode, area)); stringBuilder.Append(friendByIndex.m_SteamID).Append(':').Append(num2) .Append(':') .Append(num) .Append(':') .Append((int)friendGameMode) .Append(':') .Append((int)area) .Append(':') .Append(text) .Append('\n'); } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("FriendRoomsService refresh failed: " + ex.Message)); return; } PruneRpRequests(hashSet); string text2 = stringBuilder.ToString(); bool flag = _cache == null || !string.Equals(text2, _signature, StringComparison.Ordinal); _cache = list.OrderByDescending((FriendRoomInfo f) => f.LobbyId != 0).ThenBy<FriendRoomInfo, string>((FriendRoomInfo f) => f.Name, StringComparer.OrdinalIgnoreCase).ToList(); _signature = text2; _nextRefresh = Time.unscaledTime + 10f; _dirty = false; if (flag) { NotifyChanged(); } } private static FriendGameMode Classify(ulong lobbyId, int keyCount, string display, string group, string players, string maxPlayers, out FriendArea area) { area = FriendArea.None; if (lobbyId != 0L) { if (TryParseArea(display, out var area2)) { area = area2; } return FriendGameMode.Joinable; } if (keyCount == 0) { return FriendGameMode.Unknown; } if (TryParseArea(display, out var area3)) { area = area3; if (!_loggedTokenSeen) { _loggedTokenSeen = true; Plugin.Log.LogInfo((object)("Friend rich presence token is raw: display=[" + display + "]")); } switch (area3) { case FriendArea.MainMenu: return FriendGameMode.MainMenu; case FriendArea.Airport: return FriendGameMode.Airport; default: if (!string.IsNullOrEmpty(group)) { return FriendGameMode.InviteOnly; } if (!IsOne(maxPlayers)) { return FriendGameMode.InviteOnly; } return FriendGameMode.Solo; } } if (!_loggedFallbackSeen) { _loggedFallbackSeen = true; Plugin.Log.LogInfo((object)("Friend rich presence token unusable, falling back to keys: display=[" + display + "] " + $"group=[{group}] players=[{players}] max=[{maxPlayers}] keys={keyCount}")); } if (!string.IsNullOrEmpty(group)) { return FriendGameMode.InviteOnly; } if (IsOne(maxPlayers)) { return FriendGameMode.Solo; } if (IsOne(players)) { return FriendGameMode.MainMenu; } if (!string.IsNullOrEmpty(maxPlayers)) { return FriendGameMode.InviteOnly; } return FriendGameMode.MainMenu; } private static bool TryParseArea(string display, out FriendArea area) { area = FriendArea.None; if (string.IsNullOrEmpty(display) || !display.StartsWith("#Status_", StringComparison.Ordinal)) { return false; } switch (display.Substring("#Status_".Length)) { case "MainMenu": area = FriendArea.MainMenu; return true; case "Airport": area = FriendArea.Airport; return true; case "Shore": area = FriendArea.Shore; return true; case "Tropics": area = FriendArea.Tropics; return true; case "Alpine": area = FriendArea.Alpine; return true; case "Mesa": area = FriendArea.Mesa; return true; case "Caldera": area = FriendArea.Caldera; return true; case "Kiln": area = FriendArea.Kiln; return true; case "Peak": area = FriendArea.Peak; return true; case "Roots": area = FriendArea.Roots; return true; case "Gloom": area = FriendArea.Gloom; return true; case "Citadel": area = FriendArea.Citadel; return true; case "Nadir": area = FriendArea.Nadir; return true; default: return false; } } private static void ReadPresence(CSteamID sid, out string display, out string group, out string players, out string maxPlayers) { //IL_001e: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) display = ""; group = ""; players = ""; maxPlayers = ""; try { display = SteamFriends.GetFriendRichPresence(sid, "steam_display") ?? ""; group = SteamFriends.GetFriendRichPresence(sid, "steam_player_group") ?? ""; players = SteamFriends.GetFriendRichPresence(sid, "players") ?? ""; maxPlayers = SteamFriends.GetFriendRichPresence(sid, "max_players") ?? ""; } catch { } } private static void RequestRichPresence(CSteamID sid, float now) { //IL_0005: 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_0031: Unknown result type (might be due to invalid IL or missing references) try { if (!_lastRpRequest.TryGetValue(sid.m_SteamID, out var value) || !(now - value < 30f)) { _lastRpRequest[sid.m_SteamID] = now; SteamFriends.RequestFriendRichPresence(sid); } } catch { } } private static void PruneRpRequests(HashSet<ulong> present) { if (_lastRpRequest.Count == 0) { return; } List<ulong> list = null; foreach (ulong key in _lastRpRequest.Keys) { if (!present.Contains(key)) { (list ?? (list = new List<ulong>())).Add(key); } } if (list == null) { return; } foreach (ulong item in list) { _lastRpRequest.Remove(item); } } private static EPersonaState PersonaState(CSteamID sid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { return SteamFriends.GetFriendPersonaState(sid); } catch { return (EPersonaState)1; } } private static bool IsOne(string value) { return string.Equals(value, "1", StringComparison.Ordinal); } private static void OnPersonaChanged(PersonaStateChange_t cb) { MarkDirty(); } private static void OnFriendRichPresenceUpdate(FriendRichPresenceUpdate_t cb) { MarkDirty(); } private static void MarkDirty() { _dirty = true; RefreshIfDue(force: false); } private static void NotifyChanged() { float unscaledTime = Time.unscaledTime; if (unscaledTime - _lastNotify < 1.5f) { _notifyPending = true; return; } _lastNotify = unscaledTime; _notifyPending = false; try { FriendRoomsService.Changed?.Invoke(); } catch { } } } internal sealed class FriendNotifyPump : MonoBehaviour { private void Update() { FriendRoomsService.Tick(); } } internal sealed class HistoryPageController : IRowHost { private enum RowKind { None, Rooms, Players } private const string PlayersViewId = "__players__"; private const string SearchViewId = "__search__"; private const float DoubleClickSeconds = 0.4f; private const float RailGap = 24f; private RoomHubPage? _page; private RectTransform? _card; private RectTransform? _body; private RectTransform? _mainColumn; private RectTransform? _listRoot; private RectTransform? _emptyRoot; private RectTransform? _rail; private RectTransform? _banner; private UiScrollView? _list; private UiScrollView? _railList; private TextMeshProUGUI? _titleText; private TextMeshProUGUI? _subtitleText; private TextMeshProUGUI? _hintText; private TextMeshProUGUI? _emptyTitle; private TextMeshProUGUI? _emptyHint; private TextMeshProUGUI? _bannerText; private TextMeshProUGUI? _railTitle; private TextMeshProUGUI? _railHint; private TextMeshProUGUI? _railEmpty; private UiChip? _statChip; private UiButton? _tabRooms; private UiButton? _tabPlayers; private UiButton? _searchButton; private UiButton? _clearButton; private UiButton? _deleteButton; private UiButton? _closeButton; private UiTextInput? _searchInput; private RowPool? _mainPool; private RowPool? _railPool; private List<RowView> _mainRows = new List<RowView>(); private List<RowView> _railRows = new List<RowView>(); private RowKind _mainKind; private RowContext _context; private int _playerTotal; private bool _deleteMode; private string? _viewingLobbyId; private string? _searchQuery; private string? _lastClickLobbyId; private float _lastClickTime; private GameObject? _lastSelection; private bool _openedViaMenuWindow; 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); 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 bool IsSearchFocused { get { if (_searchInput != null) { return _searchInput.IsFocused; } return false; } } public void Open() { if (IsPageOpen) { return; } _deleteMode = false; _viewingLobbyId = null; _searchQuery = null; _lastClickLobbyId = null; HistoryUiFont.Resolve(); EnsurePage(); if (!HasLivePage) { return; } if (_searchInput != null) { _searchInput.Text = ""; } try { ((PeakCustomPage)_page).BlocksPlayerInput = true; ((PeakCustomPage)_page).ShowCursorWhileOpen = true; ((PeakCustomPage)_page).CloseOnUICancel = false; ((PeakCustomPage)_page).CloseOnPause = false; ((PeakCustomPage)_page).SelectOnOpen = true; _page.Layout(); RefreshAll(); _page.SetInitialSelection(FirstInteractive()); if (!OpenMenuWindow()) { if (!HistoryUi.IsTitleScene) { Plugin.Log.LogError((object)"MenuWindow.Open unavailable in-game — aborting open."); Discard(); return; } _page.SkipStartClosed(); ((MenuWindow)_page).Show(); ((MenuWindow)_page).SetInputActive(true); _openedViaMenuWindow = true; Plugin.Log.LogWarning((object)"MenuWindow.Open unavailable — using Show on title."); } else { _openedViaMenuWindow = true; } _page.ApplySorting(apply: true); FocusInitial(); } catch (Exception arg) { Plugin.Log.LogError((object)$"History page Open failed: {arg}"); Discard(); } } public void Hide() { SteamInviteService.ResetInvited(); ReleaseFocus(); if (!HasLivePage) { _openedViaMenuWindow = false; return; } try { _page.ApplySorting(apply: false); } catch { } try { CloseMenuWindow(); } catch { } _openedViaMenuWindow = false; try { if (HasLivePage && ((Component)_page).gameObject.activeInHierarchy) { ((MenuWindow)_page).Hide(); } } catch { } } public void Discard() { SteamInviteService.ResetInvited(); ReleaseFocus(); Unsubscribe(); try { if (HasLivePage) { CloseMenuWindow(); } } catch { } try { if (HasLivePage) { Object.Destroy((Object)(object)((Component)_page).gameObject); } } catch { } _mainPool?.Clear(); _railPool?.Clear(); _mainRows = new List<RowView>(); _railRows = new List<RowView>(); _mainKind = RowKind.None; _lastSelection = null; _page = null; _card = null; _body = null; _mainColumn = null; _listRoot = null; _emptyRoot = null; _rail = null; _banner = null; _list = null; _railList = null; _titleText = null; _subtitleText = null; _hintText = null; _emptyTitle = null; _emptyHint = null; _bannerText = null; _railTitle = null; _railEmpty = null; _statChip = null; _tabRooms = null; _tabPlayers = null; _searchButton = null; _clearButton = null; _deleteButton = null; _closeButton = null; _searchInput = null; _mainPool = null; _railPool = null; _deleteMode = false; _viewingLobbyId = null; _searchQuery = null; _openedViaMenuWindow = false; } public void HandleBackOrClose() { if (IsPageOpen) { if (_searchInput != null && _searchInput.IsFocused) { _searchInput.Deactivate(); FocusInitial(); } if (_viewingLobbyId != null) { ShowRoomsView(); } else if (_deleteMode) { _deleteMode = false; RefreshAll(); } else { Hide(); } } } public void TickAvatarRefresh() { if (!IsPageOpen || Time.frameCount % 45 != 0) { return; } foreach (RowView item in _mainPool?.Live ?? Enumerable.Empty<RowView>()) { item.RefreshAvatar(); } foreach (RowView item2 in _railPool?.Live ?? Enumerable.Empty<RowView>()) { item2.RefreshAvatar(); } } public void TickSelection() { if (!IsPageOpen) { return; } EventSystem current = EventSystem.current; GameObject val = (((Object)(object)current == (Object)null) ? null : current.currentSelectedGameObject); if ((Object)(object)val == (Object)(object)_lastSelection) { return; } _lastSelection = val; if ((Object)(object)val == (Object)null) { return; } Transform transform = val.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (!((Object)(object)val2 == (Object)null)) { if (IsInside((Transform?)(object)val2, _list?.Content)) { _list.EnsureVisible(val2); } else if (IsInside((Transform?)(object)val2, _railList?.Content)) { _railList.EnsureVisible(val2); } } } private static bool IsInside(Transform? child, RectTransform? container) { if ((Object)(object)child == (Object)null || (Object)(object)container == (Object)null) { return false; } Transform val = child; while ((Object)(object)val != (Object)null) { if ((Object)(object)val == (Object)(object)container) { return true; } val = val.parent; } return false; } private void EnsurePage() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (!HasLivePage) { if ((Object)(object)_page != (Object)null) { Discard(); } _page = new GameObject("Room Hub").AddComponent<RoomHubPage>(); ((PeakCustomPage)_page).CloseOnUICancel = false; ((PeakCustomPage)_page).CloseOnPause = false; ((PeakCustomPage)_page).BlocksPlayerInput = true; ((PeakCustomPage)_page).ShowCursorWhileOpen = true; ((PeakCustomPage)_page).SelectOnOpen = true; Image val = UiKit.Panel(((Component)_page).transform, "Scrim", UiTheme.Scrim, 0f); UiKit.Stretch(((Graphic)val).rectTransform); ((Graphic)val).raycastTarget = true; BuildCard(); BuildHeader(); BuildTabs(); BuildBody(); BuildFooter(); _page.Layout(); _page.Resized += OnPageResized; JoinGate.StateChanged += OnJoinGateChanged; FriendRoomsService.Changed += OnFriendsChanged; LocalizationSync.Register(OnGameLanguageChanged); } } private void Unsubscribe() { if ((Object)(object)_page != (Object)null) { _page.Resized -= OnPageResized; } JoinGate.StateChanged -= OnJoinGateChanged; FriendRoomsService.Changed -= OnFriendsChanged; LocalizationSync.Unregister(OnGameLanguageChanged); } private void BuildCard() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) _card = UiKit.Card(((Component)_page).transform, "Card"); UiKit.Anchor(_card, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1440f, 840f)); _page.SetCard(_card); } private void BuildHeader() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_010b: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) RectTransform val = UiKit.Rect((Transform)(object)_card, "Header"); AnchorTop(val, 114f, 0f); float num = 12f; RectTransform val2 = UiKit.ClipBox((Transform)(object)val, "TitleBox"); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(1f, 1f); val2.pivot = new Vector2(0.5f, 1f); val2.offsetMin = new Vector2(32f, 0f - num - 64f); val2.offsetMax = new Vector2(-420f, 0f - num); _titleText = UiKit.Label((Transform)(object)val2, "Title", Loc.T("ROOMHISTORY_PAGE_TITLE"), 36f, UiTheme.Paper, (FontStyles)16); UiKit.Stretch(((TMP_Text)_titleText).rectTransform); float num2 = num + 64f; RectTransform val3 = UiKit.ClipBox((Transform)(object)val, "SubtitleBox"); val3.anchorMin = new Vector2(0f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(0.5f, 1f); val3.offsetMin = new Vector2(32f, 0f - num2 - 32f); val3.offsetMax = new Vector2(-420f, 0f - num2); _subtitleText = UiKit.Label((Transform)(object)val3, "Subtitle", Loc.T("ROOMHISTORY_SUBTITLE"), 18f, UiTheme.TextDim, (FontStyles)0); UiKit.Stretch(((TMP_Text)_subtitleText).rectTransform); _statChip = UiChip.Create((Transform)(object)val, "Stat", "", ChipKind.Mute); UiKit.Anchor(_statChip.Rect, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-32f, 0f), new Vector2(0f, 34f)); Image val4 = UiKit.Divider((Transform)(object)_card, "Rule"); RectTransform rectTransform = ((Graphic)val4).rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.sizeDelta = new Vector2(-64f, 3f); rectTransform.anchoredPosition = new Vector2(0f, -114f); } private void BuildTabs() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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) //IL_00aa: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Expected O, but got Unknown //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected O, but got Unknown //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) RectTransform val = UiKit.Rect((Transform)(object)_card, "Tabs"); AnchorTop(val, 92f, 117f); _tabRooms = UiButton.Create((Transform)(object)val, "TabRooms", Loc.T("ROOMHISTORY_TAB_ROOMS"), UiRole.Primary); UiKit.Anchor(_tabRooms.Rect, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(32f, -16f), new Vector2(180f, 67f)); ((UnityEvent)_tabRooms.Control.onClick).AddListener(new UnityAction(ShowRoomsView)); _tabPlayers = UiButton.Create((Transform)(object)val, "TabPlayers", Loc.T("ROOMHISTORY_TAB_PLAYERS"), UiRole.Ghost); UiKit.Anchor(_tabPlayers.Rect, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(224f, -16f), new Vector2(180f, 67f)); ((UnityEvent)_tabPlayers.Control.onClick).AddListener(new UnityAction(ShowAllPlayersView)); _searchButton = UiButton.Create((Transform)(object)val, "SearchButton", Loc.T("ROOMHISTORY_SEARCH"), UiRole.Ghost); UiKit.Anchor(_searchButton.Rect, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-32f, -16f), new Vector2(150f, 67f)); ((UnityEvent)_searchButton.Control.onClick).AddListener(new UnityAction(RunSearch)); _clearButton = UiButton.Create((Transform)(object)val, "ClearButton", Loc.T("ROOMHISTORY_SEARCH_CLEAR"), UiRole.Ghost); _clearButton.SetVisible(visible: false); ((UnityEvent)_clearButton.Control.onClick).AddListener(new UnityAction(ShowRoomsView)); _searchInput = UiTextInput.Create((Transform)(object)val, "SearchField", 320f, 67f, RunSearch); UiKit.Anchor(_searchInput.Rect, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-32f, -16f), new Vector2(320f, 67f)); _searchInput.SetPlaceholder(Loc.T("ROOMHISTORY_SEARCH_PLACEHOLDER")); } private void BuildBody() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0246: 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_028d: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) _body = UiKit.Rect((Transform)(object)_card, "Body"); _body.anchorMin = Vector2.zero; _body.anchorMax = Vector2.one; _body.offsetMin = new Vector2(32f, 96f); _body.offsetMax = new Vector2(-32f, -209f); _mainColumn = UiKit.Rect((Transform)(object)_body, "Main"); _mainColumn.anchorMin = Vector2.zero; _mainColumn.anchorMax = Vector2.one; _mainColumn.offsetMin = Vector2.zero; _mainColumn.offsetMax = new Vector2(-524f, 0f); _banner = UiKit.Rect((Transform)(object)_mainColumn, "Banner"); Image val = UiKit.Panel((Transform)(object)_banner, "Line", UiTheme.BannerBorder, 10f); UiKit.Stretch(((Graphic)val).rectTransform); Image val2 = UiKit.Panel((Transform)(object)_banner, "Fill", UiTheme.BannerBg, 9f); UiKit.Stretch(((Graphic)val2).rectTransform); ((Graphic)val2).rectTransform.offsetMin = new Vector2(2f, 2f); ((Graphic)val2).rectTransform.offsetMax = new Vector2(-2f, -2f); RectTransform val3 = UiKit.ClipBox((Transform)(object)_banner, "TextBox"); UiKit.Stretch(val3); val3.offsetMin = new Vector2(20f, 0f); val3.offsetMax = new Vector2(-20f, 0f); _bannerText = UiKit.Label((Transform)(object)val3, "Text", Loc.T("ROOMHISTORY_DELETE_BANNER"), 18f, UiTheme.BannerText, (FontStyles)0); UiKit.Stretch(((TMP_Text)_bannerText).rectTransform); ((TMP_Text)_bannerText).alignment = (TextAlignmentOptions)4097; _banner.anchorMin = new Vector2(0f, 1f); _banner.anchorMax = new Vector2(1f, 1f); _banner.pivot = new Vector2(0.5f, 1f); _banner.sizeDelta = new Vector2(0f, 60f); _banner.anchoredPosition = Vector2.zero; ((Component)_banner).gameObject.SetActive(false); _listRoot = UiKit.Rect((Transform)(object)_mainColumn, "ListRoot"); _listRoot.anchorMin = Vector2.zero; _listRoot.anchorMax = Vector2.one; _listRoot.offsetMin = Vector2.zero; _listRoot.offsetMax = Vector2.zero; _list = UiScrollView.Create((Transform)(object)_listRoot, "RoomHubList"); UiKit.Stretch(_list.Root); _emptyRoot = UiKit.Rect((Transform)(object)_mainColumn, "Empty"); UiKit.Stretch(_emptyRoot); RectTransform val4 = UiKit.ClipBox((Transform)(object)_emptyRoot, "TitleBox"); val4.anchorMin = new Vector2(0f, 0.5f); val4.anchorMax = new Vector2(1f, 0.5f); val4.pivot = new Vector2(0.5f, 0.5f); val4.sizeDelta = new Vector2(0f, 42f); val4.anchoredPosition = new Vector2(0f, 26f); _emptyTitle = UiKit.Label((Transform)(object)val4, "Title", "", 24f, UiTheme.Paper, (FontStyles)0, wrap: true); UiKit.Stretch(((TMP_Text)_emptyTitle).rectTransform); ((TMP_Text)_emptyTitle).alignment = (TextAlignmentOptions)514; RectTransform val5 = UiKit.ClipBox((Transform)(object)_emptyRoot, "HintBox"); val5.anchorMin = new Vector2(0f, 0.5f); val5.anchorMax = new Vector2(1f, 0.5f); val5.pivot = new Vector2(0.5f, 0.5f); val5.sizeDelta = new Vector2(0f, 32f); val5.anchoredPosition = new Vector2(0f, -28f); _emptyHint = UiKit.Label((Transform)(object)val5, "Hint", "", 18f, UiTheme.TextFaint, (FontStyles)0, wrap: true); UiKit.Stretch(((TMP_Text)_emptyHint).rectTransform); ((TMP_Text)_emptyHint).alignment = (TextAlignmentOptions)514; ((Component)_emptyRoot).gameObject.SetActive(false); BuildRail(); _mainPool = new RowPool(CreateMainRow); _railPool = new RowPool((string key) => FriendRow.Create((Transform)(object)_railList.Content, key, this)); } private void BuildRail() { //IL_0026: 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) //IL_005a: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) _rail = UiKit.Rect((Transform)(object)_body, "Rail"); _rail.anchorMin = new Vector2(1f, 0f); _rail.anchorMax = new Vector2(1f, 1f); _rail.pivot = new Vector2(1f, 0.5f); _rail.sizeDelta = new Vector2(500f, 0f); _rail.anchoredPosition = Vector2.zero; Image val = UiKit.Divider((Transform)(object)_rail, "Line"); RectTransform rectTransform = ((Graphic)val).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0f, 1f); rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.sizeDelta = new Vector2(3f, 0f); rectTransform.anchoredPosition = Vector2.zero; ((Graphic)val).color = new Color(UiTheme.Divider.r, UiTheme.Divider.g, UiTheme.Divider.b, 0.5f); _railTitle = UiKit.Label((Transform)(object)_rail, "Title", "", 22f, UiTheme.Gold, (FontStyles)16); RectTransform rectTransform2 = ((TMP_Text)_railTitle).rectTransform; rectTransform2.anchorMin = new Vector2(0f, 1f); rectTransform2.anchorMax = new Vector2(1f, 1f); rectTransform2.pivot = new Vector2(0.5f, 1f); rectTransform2.offsetMin = new Vector2(22f, -40f); rectTransform2.offsetMax = new Vector2(0f, 0f); _railHint = UiKit.Label((Transform)(object)_rail, "Hint", Loc.T("ROOMHISTORY_RAIL_HINT"), 16f, UiTheme.TextFaint, (FontStyles)0); RectTransform rectTransform3 = ((TMP_Text)_railHint).rectTransform; rectTransform3.anchorMin = new Vector2(0f, 1f); rectTransform3.anchorMax = new Vector2(1f, 1f); rectTransform3.pivot = new Vector2(0.5f, 1f); rectTransform3.offsetMin = new Vector2(22f, -68f); rectTransform3.offsetMax = new Vector2(0f, -40f); RectTransform val2 = UiKit.Rect((Transform)(object)_rail, "ListRoot"); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = new Vector2(22f, 0f); val2.offsetMax = new Vector2(0f, -74f); _railList = UiScrollView.Create((Transform)(object)val2, "RoomHubFriendList"); UiKit.Stretch(_railList.Root); RectTransform val3 = UiKit.ClipBox((Transform)(object)val2, "EmptyBox"); val3.anchorMin = new Vector2(0f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(0.5f, 1f); val3.sizeDelta = new Vector2(0f, 64f); val3.anchoredPosition = new Vector2(0f, -12f); _railEmpty = UiKit.Label((Transform)(object)val3, "Empty", Loc.T("ROOMHISTORY_FRIENDS_EMPTY"), 18f, UiTheme.TextFaint, (FontStyles)0, wrap: true); UiKit.Stretch(((TMP_Text)_railEmpty).rectTransform); } private void BuildFooter() { //IL_001c: 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_0046: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: 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) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown RectTransform val = UiKit.Rect((Transform)(object)_card, "Footer"); val.anchorMin = new Vector2(0f, 0f); val.anchorMax = new Vector2(1f, 0f); val.pivot = new Vector2(0.5f, 0f); val.sizeDelta = new Vector2(0f, 96f); val.anchoredPosition = Vector2.zero; RectTransform val2 = UiKit.ClipBox((Transform)(object)val, "HintBox"); val2.anchorMin = new Vector2(0f, 0.5f); val2.anchorMax = new Vector2(1f, 0.5f); val2.pivot = new Vector2(0.5f, 0.5f); val2.offsetMin = new Vector2(32f, -14f); val2.offsetMax = new Vector2(-372f, 14f); _hintText = UiKit.Label((Transform)(object)val2, "Hint", "", 16f, UiTheme.TextFaint, (FontStyles)0); UiKit.Stretch(((TMP_Text)_hintText).rectTransform); _closeButton = UiButton.Create((Transform)(object)val, "Close", Loc.T("ROOMHISTORY_CLOSE"), UiRole.Primary); UiKit.Anchor(_closeButton.Rect, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-32f, 0f), new Vector2(150f, 67f)); ((UnityEvent)_closeButton.Control.onClick).AddListener((UnityAction)delegate { _deleteMode = false; Hide(); }); _deleteButton = UiButton.Create((Transform)(object)val, "DeleteMode", Loc.T("ROOMHISTORY_DELETE_MODE_ON"), UiRole.Ghost); UiKit.Anchor(_deleteButton.Rect, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), Vector2.zero, new Vector2(150f, 67f)); ((UnityEvent)_deleteButton.Control.onClick).AddListener(new UnityAction(ToggleDeleteMode)); } private RowView CreateMainRow(string key) { RectTransform content = _list.Content; if (_mainKind != RowKind.Players) { return RoomRow.Create((Transform)(object)content, key, this); } return PlayerRow.Create((Transform)(object)content, key, this); } private RowKind CurrentKind() { if (_viewingLobbyId == null) { return RowKind.Rooms; } return RowKind.Players; } private void RefreshAll() { if (!((Object)(object)_page == (Object)null) && _list != null) { _context = new RowContext(HistoryUi.IsTitleScene || !HistoryUi.IsInLiveLobby, HistoryUi.CanShowInvite); IReadOnlyList<RoomRecord> rooms = Plugin.Store.GetRooms(); IReadOnlyList<FriendRoomInfo> friends = FriendRoomsService.GetFriends(); _playerTotal = AllMetPlayers().Count; RefreshChrome(rooms.Count, friends.Count); RefreshMainList(rooms); RefreshRail(friends); WireNavigation(); RefreshInitialSelection(); } } private void RefreshChrome(int roomCount, int friendCount) { //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_059a: Unknown result type (might be due to invalid IL or missing references) //IL_0688: Unknown result type (might be due to invalid IL or missing references) //IL_0616: Unknown result type (might be due to invalid IL or missing references) //IL_0507: Unknown result type (might be due to invalid IL or missing references) bool flag = _viewingLobbyId == "__search__"; bool flag2 = _viewingLobbyId != null && !flag; bool flag3 = _viewingLobbyId == null; TextMeshProUGUI? titleText = _titleText; if (titleText != null) { ((TMP_Text)titleText).SetText(Loc.T("ROOMHISTORY_PAGE_TITLE")); } if ((Object)(object)_subtitleText != (Object)null) { if (flag) { ((TMP_Text)_subtitleText).text = Loc.Tf("ROOMHISTORY_SEARCH_QUERY", _searchQuery ?? ""); } else if (_viewingLobbyId == "__players__") { ((TMP_Text)_subtitleText).text = Loc.T("ROOMHISTORY_PLAYERS_TITLE"); } else if (flag2) { RoomRecord room = Plugin.Store.GetRoom(_viewingLobbyId); string text = ((room == null || string.IsNullOrWhiteSpace(room.HostName)) ? Loc.T("ROOMHISTORY_UNKNOWN_HOST") : room.HostName); ((TMP_Text)_subtitleText).text = Loc.Tf("ROOMHISTORY_ROOM_TITLE", text) + " · " + Loc.Tf("ROOMHISTORY_PLAYER_COUNT", room?.Players.Count ?? 0); } else { ((TMP_Text)_subtitleText).text = Loc.T("ROOMHISTORY_SUBTITLE"); } } _statChip?.SetText(Loc.Tf("ROOMHISTORY_STAT_PLAYERS", _playerTotal), ChipKind.Mute); if (_tabRooms != null) { _tabRooms.SetMinWidth(180f); _tabRooms.SetLabel(Loc.Tf("ROOMHISTORY_TAB_ROOMS_COUNT", roomCount)); _tabRooms.SetTabActive(flag3); } if (_tabPlayers != null) { _tabPlayers.SetMinWidth(180f); _tabPlayers.SetLabel(Loc.T("ROOMHISTORY_TAB_PLAYERS")); _tabPlayers.Rect.anchoredPosition = new Vector2(32f + _tabRooms.Width + 12f, -16f); _tabPlayers.SetTabActive(!flag3); } float num = 556f; float num2; if (!((Object)(object)_card != (Object)null)) { num2 = 1440f; } else { Rect rect = _card.rect; num2 = ((Rect)(ref rect)).width; } float num3 = num2; float num4 = num3 - 64f - 500f - 24f; float num5 = (_tabRooms?.Width ?? 0f) + 12f + (_tabPlayers?.Width ?? 0f); float num6 = _searchButton?.Width ?? 0f; float num7 = ((flag && _clearButton != null) ? (_clearButton.Width + 10f) : 0f); float num8 = num4 - num5 - 24f - num6 - 10f; bool flag4 = num8 - num7 >= 120f; float num9 = Mathf.Clamp(num8 - (flag4 ? num7 : 0f), 120f, 320f); if (_searchButton != null) { _searchButton.SetLabel(Loc.T("ROOMHISTORY_SEARCH")); _searchButton.Rect.anchoredPosition = new Vector2(0f - num, -17f); } if (_searchInput != null) { _searchInput.SetVisible(visible: true); _searchInput.SetPlaceholder(Loc.T("ROOMHISTORY_SEARCH_PLACEHOLDER")); _searchInput.Rect.sizeDelta = new Vector2(num9, 67f); _searchInput.Rect.anchoredPosition = new Vector2(0f - (num + num6 + 10f), -16f); } if (_clearButton != null) { _clearButton.SetLabel(Loc.T("ROOMHISTORY_SEARCH_CLEAR")); _clearButton.SetVisible(flag && flag4); _clearButton.Rect.anchoredPosition = new Vector2(0f - (num + num6 + 10f + num9 + 10f), -16f); } if (_closeButton != null) { _closeButton.SetLabel(Loc.T("ROOMHISTORY_CLOSE")); _closeButton.Rect.anchoredPosition = new Vector2(-32f, 0f); } if (_deleteButton != null) { _deleteButton.SetLabel(Loc.T(_deleteMode ? "ROOMHISTORY_DELETE_MODE_OFF" : "ROOMHISTORY_DELETE_MODE_ON")); _deleteButton.SetRole(_deleteMode ? UiRole.Danger : UiRole.Ghost); _deleteButton.SetVisible(flag3); _deleteButton.Rect.anchoredPosition = new Vector2(0f - (32f + (_closeButton?.Width ?? 0f) + 12f), 0f); } if ((Object)(object)_hintText != (Object)null) { if (_deleteMode) { ((TMP_Text)_hintText).text = Loc.T("ROOMHISTORY_HINT_ESC_DELETE") + " · " + Loc.T("ROOMHISTORY_DELETE_CONFIRM"); } else { string text2 = Loc.T("ROOMHISTORY_HINT_ESC"); if (flag3) { text2 = text2 + " · " + Loc.T("ROOMHISTORY_HINT_DOUBLECLICK"); } text2 = text2 + " · " + Loc.Tf("ROOMHISTORY_HINT_HOTKEY_SHORT", Plugin.ModConfig.ModMenuHotkey.Value); ((TMP_Text)_hintText).text = text2; } float num10 = 32f + (_closeButton?.Width ?? 0f) + 24f; if (flag3 && _deleteButton != null) { num10 += _deleteButton.Width + 12f; } ((TMP_Text)_hintText).rectTransform.offsetMax = new Vector2(0f - num10, 14f); } bool flag5 = _deleteMode && flag3; if ((Object)(object)_banner != (Object)null) { ((Component)_banner).gameObject.SetActive(flag5); if ((Object)(object)_bannerText != (Object)null) { ((TMP_Text)_bannerText).text = Loc.T("ROOMHISTORY_DELETE_BANNER"); } _listRoot.offsetMax = new Vector2(0f, flag5 ? (-70f) : 0f); } if ((Object)(object)_railTitle != (Object)null) { ((TMP_Text)_railTitle).text = Loc.Tf("ROOMHISTORY_RAIL_TITLE", friendCount); } if ((Object)(object)_railHint != (Object)null) { ((TMP_Text)_railHint).text = Loc.T("ROOMHISTORY_RAIL_HINT"); } } private void RefreshMainList(IReadOnlyList<RoomRecord>? rooms = null) { if (_mainPool == null || _list == null) { return; } RowKind rowKind = CurrentKind(); if (rowKind != _mainKind) { _mainPool.Clear(); _mainKind = rowKind; } List<RowView> list = new List<RowView>(); switch (rowKind) { case RowKind.Rooms: if (rooms == null) { rooms = Plugin.Store.GetRooms(); } foreach (RoomRecord room in rooms) { RoomRow roomRow = (RoomRow)_mainPool.Take(room.LobbyId); roomRow.Bind(room, _deleteMode, _context); list.Add(roomRow); } break; case RowKind.Players: foreach (PlayerSearchHit item in CurrentPlayerHits()) { PlayerRow playerRow = (PlayerRow)_mainPool.Take(item.SteamId); playerRow.Bind(item, _context); list.Add(playerRow); } break; } _mainRows = _mainPool.Commit(list); _list.LayoutRows(Rects(_mainRows), 96f, 10f); bool flag = _mainRows.Count == 0; ((Component)_list.Root).gameObject.SetActive(!flag); if ((Object)(object)_emptyRoot != (Object)null) { ((Component)_emptyRoot).gameObject.SetActive(flag); } if (flag) { SetEmptyState(rowKind); } } private void RefreshRail(IReadOnlyList<FriendRoomInfo>? friends = null) { if (_railPool == null || _railList == null) { return; } if (friends == null) { friends = FriendRoomsService.GetFriends(); } List<RowView> list = new List<RowView>(); foreach (FriendRoomInfo friend in friends) { FriendRow friendRow = (FriendRow)_railPool.Take(friend.SteamId.ToString()); friendRow.Bind(friend, _context); list.Add(friendRow); } _railRows = _railPool.Commit(list); _railList.LayoutRows(Rects(_railRows), 96f, 10f); ((Component)_railList.Root).gameObject.SetActive(_railRows.Count > 0); if ((Object)(object)_railEmpty != (Object)null) { ((TMP_Text)_railEmpty).text = Loc.T("ROOMHISTORY_FRIENDS_EMPTY"); ((Component)_railEmpty).gameObject.SetActive(_railRows.Count == 0); } } private List<PlayerSearchHit> CurrentPlayerHits() { if (_viewingLobbyId == "__search__") { return Plugin.Store.SearchPlayers(_searchQuery ?? "").ToList(); } if (_viewingLobbyId == "__players__") { return AllMetPlayers(); } RoomRecord room = ((_viewingLobbyId == null) ? null : Plugin.Store.GetRoom(_viewingLobbyId)); if (room == null) { return new List<PlayerSearchHit>(); } return (from p in room.Players orderby p.WasHost descending, p.LastSeenUtcTicks descending select new PlayerSearchHit { SteamId = p.SteamId, DisplayName = p.DisplayName, WasHost = p.WasHost, LastSeenUtcTicks = p.LastSeenUtcTicks, LobbyId = room.LobbyId, RoomHostName = room.HostName }).ToList(); } private List<PlayerSearchHit> AllMetPlayers() { Dictionary<string, PlayerSearchHit> dictionary = new Dictionary<string, PlayerSearchHit>(StringComparer.Ordinal); foreach (RoomRecord room in Plugin.Store.GetRooms()) { foreach (PlayerRecord player in room.Players) { if (!string.IsNullOrWhiteSpace(player.SteamId) && (!dictionary.TryGetValue(player.SteamId, out var value) || value.LastSeenUtcTicks < player.LastSeenUtcTicks)) { dictionary[player.SteamId] = new PlayerSearchHit { SteamId = player.SteamId, DisplayName = player.DisplayName, WasHost = player.WasHost, LastSeenUtcTicks = player.LastSeenUtcTicks, LobbyId = room.LobbyId, RoomHostName = room.HostName }; } } } return dictionary.Values.OrderByDescending((PlayerSearchHit h) => h.LastSeenUtcTicks).ToList(); } private void SetEmptyState(RowKind kind) { if ((Object)(object)_emptyTitle == (Object)null || (Object)(object)_emptyHint == (Object)null) { return; } if (kind == RowKind.Players) { if (_viewingLobbyId == "__search__") { ((TMP_Text)_emptyTitle).text = Loc.T("ROOMHISTORY_SEARCH_EMPTY"); ((TMP_Text)_emptyHint).text = Loc.T("ROOMHISTORY_SEARCH_EMPTY_HINT"); } else if (_viewingLobbyId == "__players__") { ((TMP_Text)_emptyTitle).text = Loc.T("ROOMHISTORY_EMPTY_ALL_PLAYERS"); ((TMP_Text)_emptyHint).text = Loc.T("ROOMHISTORY_EMPTY_PLAYERS_HINT"); } else { ((TMP_Text)_emptyTitle).text = Loc.T("ROOMHISTORY_EMPTY_PLAYERS"); ((TMP_Text)_emptyHint).text = Loc.T("ROOMHISTORY_EMPTY_PLAYERS_HINT"); } } else { ((TMP_Text)_emptyTitle).text = Loc.T("ROOMHISTORY_EMPTY_ROOMS_TITLE"); ((TMP_Text)_emptyHint).text = Loc.T("ROOMHISTORY_EMPTY_ROOMS_HINT"); } } private void WireNavigation() { //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0294: 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_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0470: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) List<Selectable> list = new List<Selectable>(); if (_tabRooms != null) { list.Add((Selectable)(object)_tabRooms.Control); } if (_tabPlayers != null) { list.Add((Selectable)(object)_tabPlayers.Control); } if (_searchInput != null) { list.Add((Selectable)(object)_searchInput.Field); } if (_searchButton != null) { list.Add((Selectable)(object)_searchButton.Control); } if (_clearButton != null && _clearButton.GameObject.activeSelf) { list.Add((Selectable)(object)_clearButton.Control); } List<List<Selectable>> list2 = new List<List<Selectable>>(); foreach (RowView mainRow in _mainRows) { List<Selectable> list3 = mainRow.ActionSelectables(); if (list3.Count > 0) { list2.Add(list3); } } List<Selectable> list4 = new List<Selectable>(); if (_deleteButton != null && _deleteButton.GameObject.activeSelf) { list4.Add((Selectable)(object)_deleteButton.Control); } if (_closeButton != null) { list4.Add((Selectable)(object)_closeButton.Control); } UiNav.Row(list); foreach (List<Selectable> item in list2) { UiNav.Row(item); } UiNav.Row(list4); for (int i = 0; i < list2.Count; i++) { Selectable up = ((i != 0) ? list2[i - 1][0] : ((list.Count > 0) ? list[0] : null)); Selectable down = ((i != list2.Count - 1) ? list2[i + 1][0] : ((list4.Count > 0) ? list4[0] : null)); foreach (Selectable item2 in list2[i]) { Navigation navigation = item2.navigation; UiNav.Link(item2, up, down, ((Navigation)(ref navigation)).selectOnLeft, ((Navigation)(ref navigation)).selectOnRight); } } Selectable val = ((list2.Count > 0) ? list2[0][0] : null); Selectable val2 = ((list2.Count > 0) ? list2[list2.Count - 1][0] : null); foreach (Selectable item3 in list) { Navigation navigation2 = item3.navigation; Selectable down2 = val ?? ((list4.Count > 0) ? list4[0] : null); UiNav.Link(item3, null, down2, ((Navigation)(ref navigation2)).selectOnLeft, ((Navigation)(ref navigation2)).selectOnRight); } foreach (Selectable item4 in list4) { Navigation navigation3 = item4.navigation; Selectable up2 = val2 ?? ((list.Count > 0) ? list[0] : null); UiNav.Link(item4, up2, null, ((Navigation)(ref navigation3)).selectOnLeft, ((Navigation)(ref navigation3)).selectOnRight); } List<Selectable> list5 = new List<Selectable>(); foreach (RowView railRow in _railRows) { Selectable val3 = railRow.FirstSelectable(); if ((Object)(object)val3 != (Object)null) { list5.Add(val3); } } UiNav.Column(list5); for (int j = 0; j < list5.Count; j++) { List<Selectable> list6 = ((list2.Count == 0) ? null : list2[Math.Min(j, list2.Count - 1)]); Selectable left = list6?[list6.Count - 1]; Navigation navigation4 = list5[j].navigation; UiNav.Link(list5[j], ((Navigation)(ref navigation4)).selectOnUp, ((Navigation)(ref navigation4)).selectOnDown, left, ((Navigation)(ref navigation4)).selectOnRight); } for (int k = 0; k < list2.Count; k++) { if (list5.Count <= 0) { break; } Selectable right = list5[Math.Min(k, list5.Count - 1)]; Selectable val4 = list2[k][list2[k].Count - 1]; Navigation navigation5 = val4.navigation; UiNav.Link(val4, ((Navigation)(ref navigation5)).selectOnUp, ((Navigation)(ref navigation5)).selectOnDown, ((Navigation)(ref navigation5)).selectOnLeft, right); } } private Selectable? FirstInteractive() { foreach (RowView mainRow in _mainRows) { Selectable val = mainRow.FirstSelectable(); if ((Object)(object)val != (Object)null) { return val; } } return (Selectable?)(object)_closeButton?.Control; } private void ReleaseFocus() { try { EventSystem current = EventSystem.current; if (!((Object)(object)current == (Object)null)) { GameObject currentSelectedGameObject = current.currentSelectedGameObject; if (!((Object)(object)currentSelectedGameObject == (Object)null) && IsInside(currentSelectedGameObject.transform, _card)) { current.SetSelectedGameObject((GameObject)null); } } } catch { } } private void FocusInitial() { if ((Object)(object)_page == (Object)null) { return; } Selectable val = FirstInteractive(); _page.SetInitialSelection(val); if ((Object)(object)val == (Object)null) { return; } try { UIInputHandler.SetSelectedObject(((Component)val).gameObject); } catch { } } private void RefreshInitialSelection() { if ((Object)(object)_page == (Object)null || !IsPageOpen) { return; } Selectable val = FirstInteractive(); _page.SetInitialSelection(val); EventSystem current = EventSystem.current; if (((Object)(object)current != (Object)null && (Object)(object)current.currentSelectedGameObject != (Object)null && current.currentSelectedGameObject.activeInHierarchy) || (Object)(object)val == (Object)null) { return; } try { UIInputHandler.SetSelectedObject(((Component)val).gameObject); } catch { } } private void OnPageResized() { if (!IsPageOpen) { return; } try { RefreshChrome(Plugin.Store.GetRooms().Count, FriendRoomsService.GetFriends().Count); } catch (Exception ex) { Plugin.Log.LogDebug((object)("OnPageResized: " + ex.Message)); } } private void OnGameLanguageChanged() { try { if (IsPageOpen) { RefreshAll(); } } catch (Exception ex) { Plugin.Log.LogDebug((object)("OnGameLanguageChanged: " + ex.Message)); } } private void OnJoinGateChanged() { if (!IsPageOpen) { return; } try { RefreshMainList(); WireNavigation(); RefreshInitialSelection(); } catch (Exception ex) { Plugin.Log.LogDebug((object)("OnJoinGateChanged: " + ex.Message)); } } private void OnFriendsChanged() { if (!IsPageOpen) { return; } try { IReadOnlyList<FriendRoomInfo> friends = FriendRoomsService.GetFriends(); if ((Object)(object)_railTitle != (Object)null) { ((TMP_Text)_railTitle).text = Loc.Tf("ROOMHISTORY_RAIL_TITLE", friends.Count); } _statChip?.SetText(Loc.Tf("ROOMHISTORY_STAT_PLAYERS", _playerTotal), ChipKind.Mute); RefreshRail(friends); WireNavigation(); RefreshInitialSelection(); } catch (Exception ex) { Plugin.Log.LogDebug((object)("OnFriendsChanged: " + ex.Message)); } } private void ToggleDeleteMode() { if (_viewingLobbyId == null) { _deleteMode = !_deleteMode; RefreshAll(); } } private void ShowRoomsView() { _viewingLobbyId = null; _searchQuery = null; _deleteMode = false; if (_searchInput != null) { _searchInput.Text = ""; } RefreshAll(); _list?.ResetScroll(); } private void ShowAllPlayersView() { _viewingLobbyId = "__players__"; _searchQuery = null; _deleteMode = false; RefreshAll(); _list?.ResetScroll(); } private void ShowPlayersView(string lobbyId) { _viewingLobbyId = lobbyId; _searchQuery = null; _deleteMode = false; RefreshAll(); _list?.ResetScroll(); } private void RunSearch() { string text = (_searchInput?.Text ?? "").Trim(); if (text.Length == 0) { ShowAllPlayersView(); return; } _searchQuery = text; _viewingLobbyId = "__search__"; _deleteMode = false; RefreshAll(); _list?.ResetScroll(); } private static List<RectTransform> Rects(IReadOnlyList<RowView> rows) { List<RectTransform> list = new List<RectTransform>(rows.Count); foreach (RowView row in rows) { list.Add(row.Root); } return list; } private static void AnchorTop(RectTransform rect, float height, float topOffset) { //IL_000b: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) rect.anchorMin = new Vector2(0f, 1f); rect.anchorMax = new Vector2(1f, 1f); rect.pivot = new Vector2(0.5f, 1f); rect.sizeDelta = new Vector2(0f, height); rect.anchoredPosition = new Vector2(0f, 0f - topOffset); } 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 { } } } public void OnRoomClicked(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 a live lobby."); return; } _lastClickLobbyId = null; RoomJoiner.TryJoin(room); } } public void OnRoomJoin(RoomRecord room) { RoomJoiner.TryJoin(room); } public void OnRoomView(RoomRecord room) { ShowPlayersView(room.LobbyId); } public void OnRoomDelete(RoomRecord room) { Plugin.Store.DeleteRoom(room.LobbyId); RefreshAll(); } public void OnPlayerInvite(string steamId) { if (!_context.CanInvite) { Plugin.Log.LogDebug((object)"Invite ignored: not in a live lobby."); } else if (SteamInviteService.InvitePlayer(steamId)) { RefreshRail(); WireNavigation(); RefreshInitialSelection(); } } public void OnPlayerAddFriend(string steamId) { SteamAvatarService.OpenAddFriend(steamId); } public void OnFriendJoin(FriendRoomInfo friend) { RoomJoiner.TryJoinLobbyId(friend.LobbyId.ToString(), hidePage: true); } public void OnFriendInvite(ulong steamId) { OnPlayerInvite(steamId.ToString()); } } public sealed class HistoryStore { private const int FormatVersion = 3; private const char Sep = '\t'; private const long SaveRetryDelayTicks = 300000000L; 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 bool _loadFailed; private bool _storageDisabled; private string? _loadBackupSource; private bool _loadBackupDone; private int _lastSavedRowCount; private long _saveRetryAtUtcTicks; private float _lastDirtyUnscaledTime; public string StoragePath => _filePath; public HistoryStore(string configPath, PluginConfig config) { _config = config; string text = Path.Combine(configPath, "RoomHub"); try { Directory.CreateDirectory(text); } catch (Exception ex) { _storageDisabled = true; Plugin.Log.LogError((object)("History directory unavailable, history will not persist: " + ex.Message)); } _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) { return VisibleRoomsUnlocked().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) { Dictionary<string, PlayerSearchHit> dictionary = new Dictionary<string, PlayerSearchHit>(StringComparer.Ordinal); foreach (RoomRecord item in VisibleRoomsUnlocked()) { string roomHostName = (string.IsNullOrWhiteSpace(item.HostName) ? item.LobbyId : item.HostName); foreach (PlayerRecord player in item.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 = item.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; bool flag = false; string hostName2; lock (_lock) { RoomRecord roomRecord = _rooms.FirstOrDefault((RoomRecord r) => string.Equals(r.LobbyId, lobbyId, StringComparison.Ordinal)); if (roomRecord != null && roomRecord.JoinedAtUtcTicks < ExpireCutoffTicks()) { roomRecord = null; } if (roomRecord != null) { if (!IsPlaceholderName(hostName) && IsPlaceholderName(roomRecord.HostName)) { roomRecord.HostName = Sanitize(hostName); } if (!string.IsNullOrWhiteSpace(lobbyName)) { roomRecord.LobbyName = Sanitize(lobbyName); } hostName2 = roomRecord.HostName; } else { RoomRecord roomRecord2 = new RoomRecord { LobbyId = lobbyId.Trim(), HostSteamId = (hostSteamId ?? "").Trim(), HostName = Sanitize(hostName), LobbyName = Sanitize(lobbyName), JoinedAtUtcTicks = ticks }; _rooms.Insert(0, roomRecord2); flag = true; hostName2 = roomRecord2.HostName; } PruneUnlocked(saveIfChanged: false); SaveUnlocked(); } Plugin.Log.LogInfo((object)("Room " + (flag ? "recorded (new)" : "updated (rejoin)") + ": lobby=" + lobbyId + ", host='" + hostName2 + "' → " + _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; 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 (string.IsNullOrWhiteSpace(roomRecord.HostSteamId)) { if (wasHost) { ApplySoleHostUnlocked(roomRecord, sid, playerRecord.DisplayName); } } else if (string.Equals(roomRecord.HostSteamId, sid, StringComparison.Ordinal)) { ApplySoleHostUnlocked(roomRecord, roomRecord.HostSteamId, 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) { if (string.IsNullOrWhiteSpace(room.HostSteamId)) { room.HostSteamId = hostSteamId; } string hostSteamId2 = room.HostSteamId; foreach (PlayerRecord player in room.Players) { player.WasHost = string.Equals(player.SteamId, hostSteamId2, StringComparison.Ordinal); } if (!IsPlaceholderName(hostDisplayName) && string.Equals(hostSteamId2, hostSteamId, StringComparison.Ordinal)) { 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 bool PruneSoloHostedRoom(string? lobbyId, string? localSteamId) { if (string.IsNullOrWhiteSpace(lobbyId)) { return false; } string self = (localSteamId ?? "").Trim(); if (self.Length == 0) { return false; } bool flag; lock (_lock) { flag = _rooms.RemoveAll((RoomRecord r) => string.Equals(r.LobbyId, lobbyId, StringComparison.Ordinal) && IsSoloHostedRoom(r, self)) > 0; if (flag) { SaveUnlocked(); } } if (flag) { Plugin.Log.LogInfo((object)("Pruned solo-hosted room: lobby=" + lobbyId + ", host=" + self + ", reason=no other player was ever recorded")); } return flag; } public int PruneSoloHostedRooms(string? localSteamId) { string self = (localSteamId ?? "").Trim(); if (self.Length == 0) { return 0; } int num = 0; string arg = ""; lock (_lock) { List<RoomRecord> list = _rooms.Where((RoomRecord r) => IsSoloHostedRoom(r, self)).ToList(); if (list.Count > 0) { num = _rooms.RemoveAll(list.Contains); arg = string.Join(", ", list.Select((RoomRecord r) => r.LobbyId)); SaveUnlocked(); } } if (num > 0) { Plugin.Log.LogInfo((object)($"Pruned {num} solo-hosted room(s): lobbies={arg}, host={self}, " + "reason=no other player was ever recorded")); } return num; } private static bool IsSoloHostedRoom(RoomRecord room, string localSteamId) { if (!string.IsNullOrEmpty(room.HostSteamId) && string.Equals(room.HostSteamId, localSteamId, StringComparison.Ordinal)) { return room.Players.Count == 0; } return false; } public void TickDeferredSave(float unscaledTime) { lock (_lock) { if (_dirty && !_loadFailed && !_storageDisabled && !(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>(); _loadFailed = false; string loadBackupSource = null; try { if (File.Exists(_filePath)) { loadBackupSource = _filePath; LoadFileUnlocked(_filePath); } else if (File.Exists(_legacyReTracePath)) { loadBackupSource = _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)) { loadBackupSource = _legacyQuickRejoinPath; LoadFileUnlocked(_legacyQuickRejoinPath); if (_rooms.Count > 0) { SaveUnlocked(); } Plugin.Log.LogInfo((object)$"Migrated {_rooms.Count} room(s) from Quick Rejoin TSV."); } _lastSavedRowCount = _rooms.Count; PruneUnlocked(saveIfChanged: true); Plugin.Log.LogInfo((object)$"Loaded {_rooms.Count} room(s) from {_filePath}"); } catch (Exception arg) { _loadFailed = true; _rooms = new List<RoomRecord>(); _loadBackupSource = loadBackupSource; _loadBackupDone = false; RetryLoadBackupUnlocked(); Plugin.Log.LogError((object)$"History load failed, writes disabled for this session: {arg}"); } } } public void Flush() { lock (_lock) { if (_loadFailed) { RetryLoadBackupUnlocked(); } else if (_dirty) { 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) && UtcTicks.IsUsable(result)) { 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) && UtcTicks.IsUsable(result2)) { if (!long.TryParse(array2[6], out var result3) || !UtcTicks.IsUsable(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) && UtcTicks.IsUsable(result4)) { 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 }; } } } long ticks = DateTime.UtcNow.Ticks; foreach (RoomRecord value3 in dictionary.Values) { value3.HostName = Sanitize(value3.HostName); value3.LobbyName = Sanitize(value3.LobbyName); value3.HostSteamId = (value3.HostSteamId ?? "").Trim(); if (value3.JoinedAtUtcTicks > ticks) { value3.JoinedAtUtcTicks = ticks; } foreach (PlayerRecord player in value3.Players) { if (player.FirstSeenUtcTicks > ticks) { player.FirstSeenUtcTicks = ticks; } if (player.LastSeenUtcTicks > ticks) { player.LastSeenUtcTicks = ticks; } } 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 long ExpireCutoffTicks() { int num = Math.Max(1, _config.HistoryExpireHours.Value); return DateTime.UtcNow.AddHours(-num).Ticks; } private List<RoomRecord> VisibleRoomsUnlocked() { int count = Math.Max(1, _config.MaxRecentRooms.Value); long cutoff = ExpireCutoffTicks(); return (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(); } private void PruneUnlocked(bool saveIfChanged) { int count = _rooms.Count; _rooms = VisibleRoomsUnlocked(); if (saveIfChanged && _rooms.Count != count) { SaveUnlocked(); } } private bool SaveUnlocked() { if (_loadFailed) { RetryLoadBackupUnlocked(); return false; } if (_storageDisabled) { return false; } long ticks = DateTime.UtcNow.Ticks; if (_saveRetryAtUtcTicks != 0L && ticks < _saveRetryAtUtcTicks) { return false; } string text = _filePath + ".tmp"; try { string directoryName = Path.GetDirectoryName(_filePath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } if (_rooms.Count < _lastSavedRowCount) { BackupCurrentUnlocked(); } 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'); } } 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; _lastSavedRowCount = _rooms.Count; _saveRetryAtUtcTicks = 0L; return true; } catch (Exception ex) { _saveRetryAtUtcTicks = ticks + 300000000; _dirty = true; TryDelete(text); Plugin.Log.LogError((object)$"History save failed, retrying in {30L}s: {ex.Message}"); return false; } } private void BackupCurrentUnlocked() { try { if (File.Exists(_filePath)) { File.Copy(_filePath, _filePath + ".bak", overwrite: true); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("History backup failed: " + ex.Message)); } } private string? RetryLoadBackupUnlocked() { if (_loadBackupDone || _loadBackupSource == null) { return null; } string text = BackupUnreadableUnlocked(_loadBackupSource, quiet: true); if (text == null) { return null; } _loadBackupDone = true; Plugin.Log.LogWarning((object)("Unreadable history copied to " + text)); return text; } private string? BackupUnreadableUnlocked(string source, bool quiet) { try { if (!File.Exists(source)) { return null; } string text = DateTime.UtcNow.ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture); string text2 = source + ".bak-" + text; int num = 1; while (File.Exists(text2) && num < 100) { text2 = $"{source}.bak-{text}-{num}"; num++; } File.Copy(source, text2, overwrite: false); return text2; } catch (Exception ex) { if (!quiet) { Plugin.Log.LogWarning((object)("History backup of unreadable file failed: " + ex.Message)); } return null; } } private static void TryDelete(string path) { try { if (File.Exists(path)) { File.Delete(path); } } catch { } } internal static bool IsPlaceholderName(string? name) { if (string.IsNullOrWhiteSpace(name)) { return true; } if (!string.Equals(name, Loc.T("ROOMHISTORY_UNKNOWN_HOST"), StringComparison.Ordinal)) { return string.Equals(name, Loc.T("ROOMHISTORY_UNKNOWN_PLAYER"), StringComparison.Ordinal); } return true; } 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; internal static bool PageOpenThisFrame { get; private set; } 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 { try { IMatchmakingAPI matchmaking = NetCode.Matchmaking; return matchmaking != null && matchmaking.InLobby; } catch { return false; } } } public static bool CanShowInvite { get { if (!IsTitleScene) { return IsInLiveLobby; } return false; } } public static void Open() { try { PauseMenuHook.ClosePauseMenuIfOpen(); 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() { bool flag = _page != null && _page.HasLivePage; try { _page?.Discard(); } catch { } _page = null; if (!flag) { UiSprites.Shutdown(); } } public static void OnSceneLoaded() { DropPage(); } public static void Tick() { PageOpenThisFrame = _page != null && _page.IsPageOpen; if (_page == null) { return; } if (!_page.HasLivePage) { DropPage(); } else if (_page.IsPageOpen) { if (Input.GetKeyDown((KeyCode)27) || ConsumeGamepadBack()) { _page.HandleBackOrClose(); return; } _page.TickSelection(); _page.TickAvatarRefresh(); } } private static bool ConsumeGamepadBack() { try { UIInputHandler instance = Singleton<UIInputHandler>.Instance; if ((Object)(object)instance != (Object)null && instance.cancelWasPressed) { instance.cancelWasPressed = false; return true; } } catch { } try { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null && localCharacter.input.pauseWasPressed) { localCharacter.input.pauseWasPressed = false; return true; } } catch { } return false; } public static bool IsTextInputFocused() { try { return _page != null && _page.IsSearchFocused; } catch { return false; } } private static void EnsureController() { if (_page != null && !_page.HasLivePage) { DropPage(); } if (_page == null) { _page = new HistoryPageController(); } } } [HarmonyPatch(typeof(GUIManager), "UpdatePaused")] internal static class PauseInputSuppressor { [HarmonyPrefix] internal static bool Prefix() { return !HistoryUi.PageOpenThisFrame; } } internal static class HistoryUiFont { private static TMP_FontAsset? _font; public static void Reset() { _font = null; } public static TMP_FontAsset? Resolve() { if ((Object)(object)_font != (Object)null) { return _font; } FontFallbackSwapper instance = FontFallbackSwapper.instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.mainBaseFont != (Object)null) { Adopt(instance.mainBaseFont, "FontFallbackSwapper.mainBaseFont"); return _font; } TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset; if ((Object)(object)defaultFontAsset != (Object)null) { Adopt(defaultFontAsset, "TMP_Settings.defaultFontAsset"); return _font; } AdoptFromScene(); return _font; } public static void Apply(TMP_Text? text) { if ((Object)(object)text == (Object)null) { return; } TMP_FontAsset val = Resolve(); if (!((Object)(object)val == (Object)null) && !((Object)(object)text.font == (Object)(object)val)) { text.font = val; if ((Object)(object)((TMP_Asset)val).material != (Object)null) { text.fontSharedMaterial = ((TMP_Asset)val).material; } } } public static void Apply(PeakText? peakText) { Apply((TMP_Text?)(object)((peakText != null) ? peakText.TextMesh : null)); } private static void Adopt(TMP_FontAsset font, string source) { _font = font; Plugin.Log.LogInfo((object)("UI font from " + source + ": " + ((Object)font).name)); } private static void AdoptFromScene() { try { TextMeshProUGUI[] array = Object.FindObjectsByType<TextMeshProUGUI>((FindObjectsSortMode)0); foreach (TextMeshProUGUI val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((TMP_Text)val).font == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)val).text) && ((Component)val).gameObject.activeInHierarchy) { Adopt(((TMP_Text)val).font, "scene label " + ((Object)val).name); break; } } } catch { } } } internal static class JoinGate { private sealed class Entry { public long CooldownUntilUtcTicks; } public const double MaxLobbyAgeHours = 2.5; private const double JoinFailCooldownSec = 600.0; private static readonly long JoinFailCooldownTicks = 6000000000L; private static readonly long MaxLobbyAgeTicks = 90000000000L; 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 && joinedAtUtcTicks <= DateTime.MaxValue.Ticks) { long num = DateTime.UtcNow.Ticks - joinedAtUtcTicks; if (num > MaxLobbyAgeTicks) { return false; } } if (ulong.TryParse(lobbyId, out var result) && result != 0L && _cooldowns.TryGetValue(result, out Entry value)) { if (DateTime.UtcNow.Ticks < value.CooldownUntilUtcTicks) { return false; } _cooldowns.Remove(result); } return true; } public static void MarkJoinFailed(ulong lobbyId) { if (lobbyId == 0L) { return; } _cooldowns[lobbyId] = new Entry { CooldownUntilUtcTicks = DateTime.UtcNow.Ticks + JoinFailCooldownTicks }; try { JoinGate.StateChanged?.Invoke(); } catch { } } } internal static class LobbyJoinFeedback { private static Callback<LobbyEnter_t>? _callback; private static Callback<LobbyDataUpdate_t>? _lobbyDataCallback; private static bool _inited; private static fl