using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using MenuLib; using MenuLib.MonoBehaviors; using MenuLib.Structs; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using Steamworks; using Steamworks.Data; using TMPro; using UnityEngine; using UnityEngine.UI; [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("PlayerTracker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+0d77aba4ef99a87d6be4daeb81b0de1f2e0c0dca")] [assembly: AssemblyProduct("PlayerTracker")] [assembly: AssemblyTitle("PlayerTracker")] [assembly: AssemblyVersion("1.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace InviteRecentPlayers { internal sealed class InviteSentLobbyBanner { private readonly CanvasGroup _canvasGroup; private readonly TextMeshProUGUI _label; private float _visibleUntil; internal bool IsValid { get { if ((Object)(object)_canvasGroup != (Object)null) { return (Object)(object)_label != (Object)null; } return false; } } internal InviteSentLobbyBanner(CanvasGroup canvasGroup) { _canvasGroup = canvasGroup; _label = (((Object)(object)canvasGroup != (Object)null) ? ((Component)canvasGroup).GetComponentInChildren(true) : null); if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 0f; _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; } } internal void Show(int inviteCount, float durationSeconds = 2.5f) { if (IsValid) { ((TMP_Text)_label).text = ((inviteCount == 1) ? "INVITE SENT" : $"{inviteCount} INVITES SENT"); _visibleUntil = Time.unscaledTime + durationSeconds; _canvasGroup.alpha = 1f; } } internal void Tick() { if (IsValid) { if (_visibleUntil > Time.unscaledTime) { _canvasGroup.alpha = Mathf.Lerp(_canvasGroup.alpha, 1f, Time.unscaledDeltaTime * 10f); } else { _canvasGroup.alpha = Mathf.Lerp(_canvasGroup.alpha, 0f, Time.unscaledDeltaTime * 10f); } } } } [BepInPlugin("denis.repo.invite-recent-players", "Invite Recent Players", "9.9.9")] public sealed class LegacyPluginStub : BaseUnityPlugin { } internal sealed class OverlayNativeScrollController { private readonly float _listSideInset; private readonly float _listTopPadding; private readonly float _scrollStep; private float _contentHeight; private float _scrollOffset; private float _lastAppliedHandleY = float.NaN; internal float Offset => _scrollOffset; internal OverlayNativeScrollController(float listSideInset, float listTopPadding, float scrollStep) { _listSideInset = listSideInset; _listTopPadding = listTopPadding; _scrollStep = scrollStep; } internal void Reset() { _contentHeight = 0f; _scrollOffset = 0f; _lastAppliedHandleY = float.NaN; } internal void SetContentMetrics(RectTransform viewport, RectTransform contentRoot, float contentHeight) { //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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)viewport == (Object)null) && !((Object)(object)contentRoot == (Object)null)) { _contentHeight = contentHeight; float maxScroll = GetMaxScroll(viewport); _scrollOffset = Mathf.Clamp(_scrollOffset, 0f, maxScroll); Rect rect = viewport.rect; float num = Mathf.Max(((Rect)(ref rect)).width, viewport.sizeDelta.x); contentRoot.sizeDelta = new Vector2(Mathf.Max(0f, num - _listSideInset * 2f), contentHeight); } } internal void ApplyContentPosition(RectTransform contentRoot) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)contentRoot == (Object)null)) { contentRoot.anchoredPosition = new Vector2(_listSideInset, 0f - _listTopPadding - _scrollOffset); } } internal void HandleWheel(RectTransform viewport) { //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_0024: 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_0044: 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_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)viewport == (Object)null || _contentHeight <= 0f) { return; } Vector2 val = (((Object)(object)MenuCursor.instance != (Object)null) ? Vector2.op_Implicit(((Component)MenuCursor.instance).transform.position) : Vector2.op_Implicit(Input.mousePosition)); if (RectTransformUtility.RectangleContainsScreenPoint(viewport, val, (Camera)null)) { float y = Input.mouseScrollDelta.y; if (!(Mathf.Abs(y) < 0.01f)) { float maxScroll = GetMaxScroll(viewport); _scrollOffset = Mathf.Clamp(_scrollOffset - y * _scrollStep, 0f, maxScroll); _lastAppliedHandleY = float.NaN; } } } internal void SyncScrollFromNativeHandle(REPOPopupPage page, RectTransform viewport) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)page?.menuScrollBox == (Object)null || (Object)(object)viewport == (Object)null) { return; } RectTransform scrollBarRectTransform = page.scrollBarRectTransform; RectTransform scrollBarBackground = page.menuScrollBox.scrollBarBackground; RectTransform scrollHandle = page.menuScrollBox.scrollHandle; if ((Object)(object)scrollBarRectTransform == (Object)null || (Object)(object)scrollBarBackground == (Object)null || (Object)(object)scrollHandle == (Object)null || !((Component)scrollBarRectTransform).gameObject.activeSelf) { return; } float maxScroll = GetMaxScroll(viewport); if (maxScroll <= 0.01f) { return; } Rect rect = scrollBarBackground.rect; float height = ((Rect)(ref rect)).height; float num = scrollHandle.sizeDelta.y / 2f; float num2 = height - num; float num3 = Mathf.Clamp(((Transform)scrollHandle).localPosition.y, num, num2); if (float.IsNaN(_lastAppliedHandleY) || !(Mathf.Abs(num3 - _lastAppliedHandleY) < 0.5f)) { float num4 = Mathf.InverseLerp(num2, num, num3) * maxScroll; if (!(Mathf.Abs(num4 - _scrollOffset) < 0.5f)) { _scrollOffset = num4; } } } internal void SyncNativeScrollBar(REPOPopupPage page, RectTransform viewport) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)page?.menuScrollBox == (Object)null || (Object)(object)page.scrollBarRectTransform == (Object)null || (Object)(object)viewport == (Object)null) { return; } RectTransform scrollBarRectTransform = page.scrollBarRectTransform; RectTransform scrollBarBackground = page.menuScrollBox.scrollBarBackground; RectTransform scrollHandle = page.menuScrollBox.scrollHandle; if (!((Object)(object)scrollBarBackground == (Object)null) && !((Object)(object)scrollHandle == (Object)null)) { Rect rect = viewport.rect; float num = Mathf.Max(((Rect)(ref rect)).height, viewport.sizeDelta.y); float maxScroll = GetMaxScroll(viewport); Vector3 localPosition = ((Transform)scrollBarRectTransform).localPosition; localPosition.y = ((Transform)viewport).localPosition.y; ((Transform)scrollBarRectTransform).localPosition = localPosition; Vector2 sizeDelta = scrollBarRectTransform.sizeDelta; sizeDelta.y = num; scrollBarRectTransform.sizeDelta = sizeDelta; scrollBarBackground.sizeDelta = sizeDelta; bool flag = maxScroll > 0.5f; if (((Component)scrollBarRectTransform).gameObject.activeSelf != flag) { ((Component)scrollBarRectTransform).gameObject.SetActive(flag); } if (flag) { rect = scrollBarBackground.rect; float height = ((Rect)(ref rect)).height; float num2 = Mathf.Clamp(num / Mathf.Max(_contentHeight, 1f) * height, 36f, height); Vector2 sizeDelta2 = scrollHandle.sizeDelta; sizeDelta2.y = num2; scrollHandle.sizeDelta = sizeDelta2; float num3 = ((maxScroll <= 0.01f) ? 0f : (_scrollOffset / maxScroll)); float num4 = num2 / 2f; float num5 = Mathf.Lerp(height - num4, num4, num3); localPosition = ((Transform)scrollHandle).localPosition; localPosition.y = num5; ((Transform)scrollHandle).localPosition = localPosition; _lastAppliedHandleY = num5; } } } private float GetMaxScroll(RectTransform viewport) { //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_000f: Unknown result type (might be due to invalid IL or missing references) Rect rect = viewport.rect; float num = Mathf.Max(((Rect)(ref rect)).height, viewport.sizeDelta.y); return Mathf.Max(0f, _contentHeight - num); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("denis.repo.player-tracker", "Player Tracker", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { internal const string LegacyPluginGuid = "denis.repo.invite-recent-players"; private const string LegacyDataFileName = "denis.repo.invite-recent-players.json"; private const string LegacyLikesFileName = "denis.repo.invite-recent-players.likes.json"; private const string DataFileName = "denis.repo.player-tracker.json"; private const string LikesFileName = "denis.repo.player-tracker.likes.json"; internal const string CreatorSteamId = "76561198257806281"; internal const string CreatorKey = "steam:76561198257806281"; internal const string CreatorDisplayName = "disabro"; private static readonly Vector2 HostLobbyButtonPosition = new Vector2(190.66f, 91.01997f); private static readonly Vector2 GuestLobbyButtonPosition = new Vector2(222.15f, 64.40998f); private static ConfigEntry _recentPlayersLimit; private static ConfigEntry _iHaveNoFriends; private static ConfigEntry _saveNonFriends; private static readonly HashSet CachedFriendSteamIds = new HashSet(StringComparer.Ordinal); private static float _friendCacheNextRefreshAt; private RecentPlayersStore _store; private RecentPlayerLikesStore _likesStore; private RecentPlayersTracker _tracker; private RecentPlayersPanel _panel; internal static Plugin Instance { get; private set; } internal static ManualLogSource SharedLogger { get; private set; } internal static Vector2 GetLobbyButtonPosition() { //IL_000e: 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) if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient) { return HostLobbyButtonPosition; } return GuestLobbyButtonPosition; } internal static int GetRecentPlayersLimit() { int num = 20; if (_recentPlayersLimit != null) { num = _recentPlayersLimit.Value; } return Mathf.Clamp(num, 20, 40); } internal static bool IsCreatorForcedVisible() { if (_iHaveNoFriends != null) { return _iHaveNoFriends.Value; } return false; } internal static bool SaveNonFriends() { if (_saveNonFriends != null) { return _saveNonFriends.Value; } return false; } internal static bool IsSteamFriend(string steamId) { if (string.IsNullOrWhiteSpace(steamId)) { return false; } RefreshFriendCacheIfNeeded(); return CachedFriendSteamIds.Contains(steamId); } private static void RefreshFriendCacheIfNeeded() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_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) if (Time.unscaledTime < _friendCacheNextRefreshAt && CachedFriendSteamIds.Count > 0) { return; } _friendCacheNextRefreshAt = Time.unscaledTime + 5f; try { HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (Friend friend in SteamFriends.GetFriends()) { Friend current = friend; if (((Friend)(ref current)).IsFriend) { SteamId id = current.Id; hashSet.Add(((object)(SteamId)(ref id)).ToString()); } } CachedFriendSteamIds.Clear(); foreach (string item in hashSet) { CachedFriendSteamIds.Add(item); } } catch { } } internal static bool ShouldKeepPlayerVisible(RecentPlayerRecord player) { if (player == null) { return false; } if (string.IsNullOrWhiteSpace(player.Key)) { return false; } string steamId = player.SteamId; if (IsLocalPlayerSteamId(steamId) || IsLocalPlayerKey(player.Key)) { return false; } if (IsCreatorForcedVisible() && string.Equals(steamId, "76561198257806281", StringComparison.Ordinal)) { return true; } if (!IsValidSteamId(steamId)) { return false; } if (SaveNonFriends()) { return true; } return IsSteamFriend(steamId); } internal static string GetLocalSteamId() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid) { return string.Empty; } SteamId steamId = SteamClient.SteamId; return ((object)(SteamId)(ref steamId)).ToString(); } internal static string GetLocalPlayerKey() { string localSteamId = GetLocalSteamId(); if (!string.IsNullOrWhiteSpace(localSteamId)) { return "steam:" + localSteamId; } return string.Empty; } internal static string GetLocalDisplayName() { if (!SteamClient.IsValid) { return string.Empty; } if (!string.IsNullOrWhiteSpace(SteamClient.Name)) { return SteamClient.Name.Trim(); } return GetLocalSteamId(); } internal static bool IsLocalPlayerSteamId(string steamId) { if (IsValidSteamId(steamId)) { return string.Equals(steamId, GetLocalSteamId(), StringComparison.Ordinal); } return false; } internal static bool IsLocalPlayerKey(string playerKey) { if (!string.IsNullOrWhiteSpace(playerKey)) { return string.Equals(playerKey, GetLocalPlayerKey(), StringComparison.OrdinalIgnoreCase); } return false; } internal static bool ShouldSaveObservedPlayer(string steamId) { if (!IsValidSteamId(steamId)) { return false; } if (string.Equals(steamId, "76561198257806281", StringComparison.Ordinal)) { return true; } if (SaveNonFriends()) { return true; } if (IsSteamFriend(steamId)) { return true; } return false; } internal static bool IsValidSteamId(string steamId) { if (string.IsNullOrWhiteSpace(steamId) || steamId == "0") { return false; } if (!Regex.IsMatch(steamId, "^\\d+$")) { return false; } if (!ulong.TryParse(steamId, out var result)) { return false; } if (result == 0L) { return false; } if (steamId.Length != 17) { return false; } if (!steamId.StartsWith("765611")) { return false; } return true; } private void Awake() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown Instance = this; SharedLogger = ((BaseUnityPlugin)this).Logger; string storagePath = ResolveDataPath("denis.repo.player-tracker.json", "denis.repo.invite-recent-players.json"); string storagePath2 = ResolveDataPath("denis.repo.player-tracker.likes.json", "denis.repo.invite-recent-players.likes.json"); _recentPlayersLimit = ((BaseUnityPlugin)this).Config.Bind("General", "Recent Players Count", 20, new ConfigDescription("How many recent players to show.", (AcceptableValueBase)(object)new AcceptableValueRange(20, 40), Array.Empty())); _saveNonFriends = ((BaseUnityPlugin)this).Config.Bind("General", "Save Non-Friends", true, "When off, only Steam friends are kept in recent players. The joke helper entry is still allowed."); _iHaveNoFriends = ((BaseUnityPlugin)this).Config.Bind("General", "I Have No Friends", false, "Force-show disabro in the list as a joke helper entry."); _store = new RecentPlayersStore(storagePath, ((BaseUnityPlugin)this).Logger); _store.Load(); _store.RemoveSeededOrInvalidRecords(); _store.RemoveNonFriendRecordsIfNeeded(); _likesStore = new RecentPlayerLikesStore(storagePath2, ((BaseUnityPlugin)this).Logger); _likesStore.Load(); RecentPlayersInviteService inviteService = new RecentPlayersInviteService(((BaseUnityPlugin)this).Logger); _tracker = new RecentPlayersTracker(_store, ((BaseUnityPlugin)this).Logger); _panel = new RecentPlayersPanel(_store, _likesStore, _tracker, inviteService, ((BaseUnityPlugin)this).Logger); MenuAPI.AddElementToLobbyMenu(new BuilderDelegate(_panel.BuildLobbyButton)); } private void Update() { //IL_0047: 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) _tracker?.Tick(); _likesStore?.SyncUniquePlayersMetCount(_store?.GetUniqueObservedSteamIdCount() ?? 0); if (SteamClient.IsValid) { RecentPlayerLikesStore likesStore = _likesStore; if (likesStore != null) { SteamId steamId = SteamClient.SteamId; likesStore.ReconcileLocalPlayerState(((object)(SteamId)(ref steamId)).ToString()); } } RecentPlayerLikesSyncBridge.TickPublish(_likesStore); _panel?.Tick(); } private void OnDestroy() { if (Instance == this) { Instance = null; } if (SharedLogger == ((BaseUnityPlugin)this).Logger) { SharedLogger = null; } } private static string ResolveDataPath(string currentFileName, string legacyFileName) { string text = Path.Combine(Paths.ConfigPath, currentFileName); if (File.Exists(text)) { return text; } string text2 = Path.Combine(Paths.ConfigPath, legacyFileName); if (!File.Exists(text2)) { return text; } return text2; } } internal sealed class RecentPlayerFriendBadge { internal const float SlotWidth = 24f; private const float IconSize = 14f; private static Sprite _friendSprite; private static bool _friendSpriteLoadAttempted; private readonly Image _image; private readonly RecentPlayerIconHover _hover; private RecentPlayerFriendBadge(Image image, RecentPlayerIconHover hover) { _image = image; _hover = hover; } internal static RecentPlayerFriendBadge Attach(Transform parent, RectTransform hoverRect) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_00a3: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) Sprite friendSprite = GetFriendSprite(); if ((Object)(object)friendSprite == (Object)null) { return null; } GameObject val = new GameObject("FriendBadgeIcon", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(14f, 14f); component.anchoredPosition = Vector2.zero; ((Transform)component).localScale = Vector3.one; Image component2 = val.GetComponent(); component2.sprite = friendSprite; ((Graphic)component2).color = new Color(1f, 1f, 1f, 0.6f); ((Graphic)component2).raycastTarget = false; component2.preserveAspect = true; ((Behaviour)component2).enabled = false; RecentPlayerIconHover recentPlayerIconHover = val.AddComponent(); recentPlayerIconHover.Initialize(hoverRect, (Graphic)(object)component2); recentPlayerIconHover.SetVisible(visible: false); return new RecentPlayerFriendBadge(component2, recentPlayerIconHover); } internal void Refresh(bool visible) { if (!((Object)(object)_image == (Object)null)) { ((Behaviour)_image).enabled = visible; _hover?.SetVisible(visible); } } private static Sprite GetFriendSprite() { if (_friendSpriteLoadAttempted) { return _friendSprite; } _friendSpriteLoadAttempted = true; _friendSprite = RecentPlayerIconLoader.LoadSprite("friend.png", "RecentPlayersFriendPng", "RecentPlayersFriendSprite"); return _friendSprite; } } internal sealed class RecentPlayerLabelHover : MonoBehaviour { private RectTransform _hoverRect; private TextMeshProUGUI _label; private Canvas _canvas; private Color _normalColor = Color.white; private Color _hoverColor = Color.white; private bool _enabled = true; internal void Initialize(RectTransform hoverRect, TextMeshProUGUI label) { _hoverRect = hoverRect; _label = label; _canvas = (((Object)(object)hoverRect != (Object)null) ? ((Component)hoverRect).GetComponentInParent() : null); } internal void SetColors(Color normalColor, Color hoverColor) { //IL_0001: 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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) _normalColor = normalColor; _hoverColor = hoverColor; Apply(); } internal void SetEnabled(bool enabled) { _enabled = enabled; Apply(); } private void Update() { Apply(); } private void Apply() { //IL_004d: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0090: 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_00a5: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_hoverRect == (Object)null) && !((Object)(object)_label == (Object)null) && ((Component)this).gameObject.activeInHierarchy) { Vector2 val = (((Object)(object)MenuCursor.instance != (Object)null) ? Vector2.op_Implicit(((Component)MenuCursor.instance).transform.position) : Vector2.op_Implicit(Input.mousePosition)); Camera val2 = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null); bool flag = _enabled && RectTransformUtility.RectangleContainsScreenPoint(_hoverRect, val, val2); ((Graphic)_label).color = (flag ? _hoverColor : _normalColor); } } } internal sealed class RecentPlayerIconHover : MonoBehaviour { private RectTransform _hoverRect; private Graphic _graphic; private Canvas _canvas; private float _normalAlpha = 0.6f; private float _hoverAlpha = 1f; private bool _isVisible = true; internal void Initialize(RectTransform hoverRect, Graphic graphic, float normalAlpha = 0.6f, float hoverAlpha = 1f) { _hoverRect = hoverRect; _graphic = graphic; _canvas = (((Object)(object)hoverRect != (Object)null) ? ((Component)hoverRect).GetComponentInParent() : null); _normalAlpha = normalAlpha; _hoverAlpha = hoverAlpha; Apply(); } internal void SetVisible(bool visible) { _isVisible = visible; Apply(); } private void Update() { Apply(); } private void Apply() { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: 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_0074: 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) if (!((Object)(object)_hoverRect == (Object)null) && !((Object)(object)_graphic == (Object)null) && ((Component)this).gameObject.activeInHierarchy) { float num; if (_isVisible) { Vector2 val = (((Object)(object)MenuCursor.instance != (Object)null) ? Vector2.op_Implicit(((Component)MenuCursor.instance).transform.position) : Vector2.op_Implicit(Input.mousePosition)); Camera val2 = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null); num = (RectTransformUtility.RectangleContainsScreenPoint(_hoverRect, val, val2) ? _hoverAlpha : _normalAlpha); } else { num = 0f; } Color color = _graphic.color; if (!Mathf.Approximately(color.a, num)) { _graphic.color = new Color(color.r, color.g, color.b, num); } } } } internal sealed class RecentPlayerSpriteSwapHover : MonoBehaviour { private RectTransform _hoverRect; private Image _image; private Canvas _canvas; private Sprite _normalSprite; private Sprite _hoverSprite; internal void Initialize(RectTransform hoverRect, Image image, Sprite normalSprite, Sprite hoverSprite) { _hoverRect = hoverRect; _image = image; _canvas = (((Object)(object)hoverRect != (Object)null) ? ((Component)hoverRect).GetComponentInParent() : null); _normalSprite = normalSprite; _hoverSprite = hoverSprite; Apply(); } private void Update() { Apply(); } private void Apply() { //IL_004d: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0088: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_hoverRect == (Object)null) && !((Object)(object)_image == (Object)null) && ((Component)this).gameObject.activeInHierarchy) { Vector2 val = (((Object)(object)MenuCursor.instance != (Object)null) ? Vector2.op_Implicit(((Component)MenuCursor.instance).transform.position) : Vector2.op_Implicit(Input.mousePosition)); Camera val2 = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null); bool flag = RectTransformUtility.RectangleContainsScreenPoint(_hoverRect, val, val2); _image.sprite = ((flag && (Object)(object)_hoverSprite != (Object)null) ? _hoverSprite : _normalSprite); } } } internal readonly struct RecentPlayerInviteCooldownState { internal static readonly RecentPlayerInviteCooldownState None = new RecentPlayerInviteCooldownState(isActive: false, string.Empty); internal bool IsActive { get; } internal string Suffix { get; } internal RecentPlayerInviteCooldownState(bool isActive, string suffix) { IsActive = isActive; Suffix = suffix ?? string.Empty; } } internal static class RecentPlayerIconLoader { internal static Sprite LoadSprite(string fileName, string textureName, string spriteName) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrWhiteSpace(directoryName)) { return null; } string text = Path.Combine(directoryName, "assets", "ui", fileName); if (!File.Exists(text)) { ManualLogSource sharedLogger = Plugin.SharedLogger; if (sharedLogger != null) { sharedLogger.LogWarning((object)("RecentPlayers icon missing: [" + text + "]")); } return null; } byte[] array = File.ReadAllBytes(text); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array, false)) { ManualLogSource sharedLogger2 = Plugin.SharedLogger; if (sharedLogger2 != null) { sharedLogger2.LogWarning((object)("RecentPlayers icon failed to decode: [" + text + "]")); } return null; } ((Object)val).name = textureName; Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); ((Object)obj).name = spriteName; return obj; } catch (Exception ex) { ManualLogSource sharedLogger3 = Plugin.SharedLogger; if (sharedLogger3 != null) { sharedLogger3.LogWarning((object)("RecentPlayers icon load failed for [" + fileName + "]: " + ex.Message)); } return null; } } } internal enum RecentPlayerActionVisualState { Hidden, Invite, Warning, Like, LikeActive } internal sealed class RecentPlayerInviteButton { private static Sprite _inviteSprite; private static bool _inviteSpriteLoadAttempted; private static Sprite _warningSprite; private static bool _warningSpriteLoadAttempted; private static Sprite _likeSprite; private static bool _likeSpriteLoadAttempted; private static Sprite _likeGreenSprite; private static bool _likeGreenSpriteLoadAttempted; private static readonly Color FallbackTextColor = new Color(0.95f, 0.95f, 0.95f, 1f); private static readonly Color CountTextColor = new Color(0.85f, 0.85f, 0.85f, 1f); private readonly Image _inviteImage; private readonly RecentPlayerIconHover _inviteHover; private readonly Image _warningImage; private readonly Image _likeImage; private readonly RecentPlayerIconHover _likeHover; private readonly Image _likeGreenImage; private readonly RecentPlayerIconHover _likeGreenHover; private readonly TextMeshProUGUI _countText; private readonly TextMeshProUGUI _fallbackText; private RecentPlayerInviteButton(Image inviteImage, RecentPlayerIconHover inviteHover, Image warningImage, Image likeImage, RecentPlayerIconHover likeHover, Image likeGreenImage, RecentPlayerIconHover likeGreenHover, TextMeshProUGUI countText, TextMeshProUGUI fallbackText) { _inviteImage = inviteImage; _inviteHover = inviteHover; _warningImage = warningImage; _likeImage = likeImage; _likeHover = likeHover; _likeGreenImage = likeGreenImage; _likeGreenHover = likeGreenHover; _countText = countText; _fallbackText = fallbackText; } internal static RecentPlayerInviteButton Attach(Transform parent, float buttonWidth, float buttonHeight) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Min(24f, Mathf.Max(18f, buttonHeight - 12f)); float iconHeight = num; Vector3 localOffset = default(Vector3); ((Vector3)(ref localOffset))..ctor(-14f, 0f, 0f); Vector3 localOffset2 = default(Vector3); ((Vector3)(ref localOffset2))..ctor(-14f, 0f, 0f); Vector3 localOffset3 = default(Vector3); ((Vector3)(ref localOffset3))..ctor(-18f, 0f, 0f); Vector3 localOffset4 = default(Vector3); ((Vector3)(ref localOffset4))..ctor(20f, 0f, 0f); Image image = null; RecentPlayerIconHover hover = null; Image image2 = null; Image image3 = null; RecentPlayerIconHover hover2 = null; Image image4 = null; RecentPlayerIconHover hover3 = null; TextMeshProUGUI fallbackText = null; if (!TryBuildSpriteIcon(parent, "InvitePngIcon", GetInviteSprite(), num, iconHeight, localOffset, 0.6f, useHover: true, out image, out hover)) { fallbackText = BuildFallbackTextIcon(parent, buttonWidth, buttonHeight, Vector3.zero); } TryBuildSpriteIcon(parent, "WarningPngIcon", GetWarningSprite(), num, iconHeight, localOffset2, 0.3f, useHover: false, out image2, out var _); TryBuildSpriteIcon(parent, "LikePngIcon", GetLikeSprite(), num, iconHeight, localOffset3, 0.6f, useHover: true, out image3, out hover2); TryBuildSpriteIcon(parent, "LikeGreenPngIcon", GetLikeGreenSprite(), num, iconHeight, localOffset3, 0.6f, useHover: true, out image4, out hover3); TextMeshProUGUI countText = BuildCountText(parent, buttonWidth, buttonHeight, localOffset4); RecentPlayerInviteButton recentPlayerInviteButton = new RecentPlayerInviteButton(image, hover, image2, image3, hover2, image4, hover3, countText, fallbackText); recentPlayerInviteButton.Refresh(RecentPlayerActionVisualState.Hidden, 0); return recentPlayerInviteButton; } internal void Refresh(RecentPlayerActionVisualState state, int count) { bool flag = state == RecentPlayerActionVisualState.Invite; bool enabled = state == RecentPlayerActionVisualState.Warning; bool flag2 = state == RecentPlayerActionVisualState.Like; bool flag3 = state == RecentPlayerActionVisualState.LikeActive; bool flag4 = flag2 || flag3; if ((Object)(object)_inviteImage != (Object)null) { ((Behaviour)_inviteImage).enabled = flag; } _inviteHover?.SetVisible(flag); if ((Object)(object)_warningImage != (Object)null) { ((Behaviour)_warningImage).enabled = enabled; } if ((Object)(object)_likeImage != (Object)null) { ((Behaviour)_likeImage).enabled = flag2; } _likeHover?.SetVisible(flag2); if ((Object)(object)_likeGreenImage != (Object)null) { ((Behaviour)_likeGreenImage).enabled = flag3; } _likeGreenHover?.SetVisible(flag3); if ((Object)(object)_countText != (Object)null) { ((Behaviour)_countText).enabled = flag4; ((TMP_Text)_countText).text = (flag4 ? FormatCompactCount(count) : string.Empty); } if ((Object)(object)_fallbackText != (Object)null) { ((Behaviour)_fallbackText).enabled = (Object)(object)_inviteImage == (Object)null && flag; } } private static bool TryBuildSpriteIcon(Transform parent, string name, Sprite sprite, float iconWidth, float iconHeight, Vector3 localOffset, float alpha, bool useHover, out Image image, out RecentPlayerIconHover hover) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_0093: Unknown result type (might be due to invalid IL or missing references) image = null; hover = null; if ((Object)(object)sprite == (Object)null) { return false; } GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.sizeDelta = new Vector2(iconWidth, iconHeight); ((Transform)component).localScale = Vector3.one; ((Transform)component).localPosition = localOffset; image = val.GetComponent(); image.sprite = sprite; ((Graphic)image).color = new Color(1f, 1f, 1f, alpha); ((Graphic)image).raycastTarget = false; image.preserveAspect = true; ((Behaviour)image).enabled = false; if (useHover) { hover = val.AddComponent(); hover.Initialize((RectTransform)(object)((parent is RectTransform) ? parent : null), (Graphic)(object)image, alpha); hover.SetVisible(visible: false); } return true; } private static TextMeshProUGUI BuildCountText(Transform parent, float buttonWidth, float buttonHeight, Vector3 localOffset) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_007d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ActionCountText"); val.transform.SetParent(parent, false); TextMeshProUGUI obj = val.AddComponent(); RectTransform rectTransform = ((TMP_Text)obj).rectTransform; rectTransform.sizeDelta = new Vector2(buttonWidth * 0.5f, buttonHeight); ((Transform)rectTransform).localScale = Vector3.one; ((Transform)rectTransform).localPosition = localOffset; ((TMP_Text)obj).text = string.Empty; ((TMP_Text)obj).fontSize = 15f; ((TMP_Text)obj).fontStyle = (FontStyles)0; ((TMP_Text)obj).alignment = (TextAlignmentOptions)513; ((TMP_Text)obj).enableWordWrapping = false; ((Graphic)obj).raycastTarget = false; ((Graphic)obj).color = CountTextColor; ((Behaviour)obj).enabled = false; return obj; } private static TextMeshProUGUI BuildFallbackTextIcon(Transform parent, float buttonWidth, float buttonHeight, Vector3 localOffset) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0030: 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_0077: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("InviteFallbackText"); val.transform.SetParent(parent, false); TextMeshProUGUI obj = val.AddComponent(); RectTransform rectTransform = ((TMP_Text)obj).rectTransform; rectTransform.sizeDelta = new Vector2(buttonWidth, buttonHeight); ((Transform)rectTransform).localScale = Vector3.one; ((Transform)rectTransform).localPosition = localOffset; ((TMP_Text)obj).text = "INVITE"; ((TMP_Text)obj).fontSize = 14f; ((TMP_Text)obj).fontStyle = (FontStyles)1; ((TMP_Text)obj).alignment = (TextAlignmentOptions)514; ((TMP_Text)obj).enableWordWrapping = false; ((Graphic)obj).raycastTarget = false; ((Graphic)obj).color = FallbackTextColor; ((Behaviour)obj).enabled = false; return obj; } private static string FormatCompactCount(int count) { count = Mathf.Max(0, count); if (count <= 999) { return count.ToString(); } int num = count / 1000; return $"{Mathf.Max(1, num)}K"; } private static Sprite GetInviteSprite() { if (_inviteSpriteLoadAttempted) { return _inviteSprite; } _inviteSpriteLoadAttempted = true; _inviteSprite = RecentPlayerIconLoader.LoadSprite("invite.png", "RecentPlayersInvitePng", "RecentPlayersInviteSprite"); return _inviteSprite; } private static Sprite GetWarningSprite() { if (_warningSpriteLoadAttempted) { return _warningSprite; } _warningSpriteLoadAttempted = true; _warningSprite = RecentPlayerIconLoader.LoadSprite("warning.png", "RecentPlayersWarningPng", "RecentPlayersWarningSprite"); return _warningSprite; } private static Sprite GetLikeSprite() { if (_likeSpriteLoadAttempted) { return _likeSprite; } _likeSpriteLoadAttempted = true; _likeSprite = RecentPlayerIconLoader.LoadSprite("like.png", "RecentPlayersLikePng", "RecentPlayersLikeSprite"); return _likeSprite; } private static Sprite GetLikeGreenSprite() { if (_likeGreenSpriteLoadAttempted) { return _likeGreenSprite; } _likeGreenSpriteLoadAttempted = true; _likeGreenSprite = RecentPlayerIconLoader.LoadSprite("like_green.png", "RecentPlayersLikeGreenPng", "RecentPlayersLikeGreenSprite"); return _likeGreenSprite; } } [Serializable] internal sealed class RecentPlayerLikeRecord { public string SteamId; public bool LikedByMe; public string LastLikedLobbyId; public int KnownLikeCount; public bool SuspiciousGrowth; public string[] UniqueLikerIds = Array.Empty(); } [Serializable] internal sealed class RecentPlayerLikesSaveData { public RecentPlayerLikeRecord[] Entries = Array.Empty(); } internal sealed class RecentPlayerLikesStore { private const int LocalMismatchGrace = 3; private static readonly byte[] PackedMagic = Encoding.ASCII.GetBytes("IRPL1"); private readonly string _storagePath; private readonly ManualLogSource _logger; private readonly Dictionary _records = new Dictionary(StringComparer.Ordinal); private int _uniquePlayersMetCount; private int _outboundRevision; internal int UniquePlayersMetCount => Mathf.Max(0, _uniquePlayersMetCount); internal int OutboundRevision => _outboundRevision; internal RecentPlayerLikesStore(string storagePath, ManualLogSource logger) { _storagePath = storagePath; _logger = logger; } internal void Load() { try { if (!File.Exists(_storagePath)) { return; } byte[] array = File.ReadAllBytes(_storagePath); if (array == null || array.Length == 0) { return; } if (IsPackedFormat(array)) { string text = TryReadPackedPayload(array); if (!string.IsNullOrWhiteSpace(text)) { LoadLineFormat(text); } return; } string @string = Encoding.UTF8.GetString(array); if (!string.IsNullOrWhiteSpace(@string) && !(@string.Trim() == "{}")) { if (@string.TrimStart().StartsWith("{")) { LoadLegacyJson(@string); Save(); } else { LoadLineFormat(@string); Save(); } } } catch (Exception ex) { _logger.LogWarning((object)("Failed to load player likes: " + ex.Message)); } } internal void Save() { try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("#meta\tuniquePlayersMetCount\t").Append(UniquePlayersMetCount).Append('\n'); foreach (RecentPlayerLikeRecord item in _records.Values.OrderByDescending(GetPermanentLikeCount).ThenBy((RecentPlayerLikeRecord entry) => entry.SteamId, StringComparer.Ordinal)) { string value = string.Join("|", NormalizeContributorIds(item.UniqueLikerIds).Select(Escape)); stringBuilder.Append(Escape(item.SteamId)).Append('\t').Append(item.LikedByMe ? '1' : '0') .Append('\t') .Append(Escape(item.LastLikedLobbyId ?? string.Empty)) .Append('\t') .Append(Mathf.Max(0, item.KnownLikeCount)) .Append('\t') .Append(item.SuspiciousGrowth ? '1' : '0') .Append('\t') .Append(value) .Append('\n'); } byte[] bytes = BuildPackedPayload(stringBuilder.ToString()); File.WriteAllBytes(_storagePath, bytes); } catch (Exception ex) { _logger.LogWarning((object)("Failed to save player likes: " + ex.Message)); } } internal void SyncUniquePlayersMetCount(int count) { count = Mathf.Max(0, count); if (count > _uniquePlayersMetCount) { _uniquePlayersMetCount = count; Save(); } } internal void ReconcileLocalPlayerState(string localSteamId) { if (!string.IsNullOrWhiteSpace(localSteamId) && TryGet(localSteamId, out var record)) { int permanentLikeCount = GetPermanentLikeCount(record); int num = Mathf.Max(0, record.KnownLikeCount); if (num > permanentLikeCount + 3) { record.KnownLikeCount = 0; record.SuspiciousGrowth = true; _records[localSteamId] = record; Save(); _logger.LogWarning((object)$"RecentPlayers local like count reset: steamId=[{localSteamId}] known=[{num}] permanent=[{permanentLikeCount}]"); } } } internal bool IsLikedByMe(string steamId) { if (TryGet(steamId, out var record)) { return record.LikedByMe; } return false; } internal bool CanRemoveLikeThisLobby(string steamId, string lobbyId) { if (!TryGet(steamId, out var record) || !record.LikedByMe) { return false; } return !string.Equals(record.LastLikedLobbyId ?? string.Empty, lobbyId ?? string.Empty, StringComparison.Ordinal); } internal bool ToggleLike(string steamId, string lobbyId) { if (string.IsNullOrWhiteSpace(steamId)) { return false; } RecentPlayerLikeRecord orCreate = GetOrCreate(steamId); bool flag = false; if (orCreate.LikedByMe) { if (!CanRemoveLikeThisLobby(steamId, lobbyId)) { return false; } orCreate.LikedByMe = false; if (GetPermanentLikeCount(orCreate) <= 0 && orCreate.KnownLikeCount <= 0) { _records.Remove(steamId); } else { _records[steamId] = orCreate; } flag = true; } else { orCreate.LikedByMe = true; orCreate.LastLikedLobbyId = lobbyId ?? string.Empty; _records[steamId] = orCreate; flag = true; } if (flag) { TouchOutboundState(); Save(); } return flag; } internal int GetKnownLikeCount(string steamId) { if (!TryGet(steamId, out var record)) { return 0; } return Mathf.Max(0, record.KnownLikeCount); } internal int GetPermanentLikeCount(string steamId) { if (!TryGet(steamId, out var record)) { return 0; } return GetPermanentLikeCount(record); } internal bool IsSuspicious(string steamId) { if (TryGet(steamId, out var record)) { return record.SuspiciousGrowth; } return false; } internal bool ApplyLiveLikeContributions(string steamId, IEnumerable likerIds, bool isLocalPlayer) { if (string.IsNullOrWhiteSpace(steamId) || likerIds == null) { return false; } RecentPlayerLikeRecord orCreate = GetOrCreate(steamId); HashSet hashSet = new HashSet(NormalizeContributorIds(orCreate.UniqueLikerIds), StringComparer.Ordinal); int count = hashSet.Count; foreach (string likerId in likerIds) { if (!string.IsNullOrWhiteSpace(likerId) && !string.Equals(likerId, steamId, StringComparison.Ordinal)) { hashSet.Add(likerId); } } if (hashSet.Count == count) { return false; } orCreate.UniqueLikerIds = hashSet.OrderBy((string value) => value, StringComparer.Ordinal).ToArray(); orCreate.SuspiciousGrowth = false; _records[steamId] = orCreate; TouchOutboundState(); Save(); return true; } internal bool UpdateKnownLikeCountFromRemote(string steamId, int remoteCount, bool isLocalPlayer) { if (string.IsNullOrWhiteSpace(steamId)) { return false; } remoteCount = Mathf.Max(0, remoteCount); RecentPlayerLikeRecord orCreate = GetOrCreate(steamId); int permanentLikeCount = GetPermanentLikeCount(orCreate); int num = Mathf.Max(0, orCreate.KnownLikeCount); if (isLocalPlayer && remoteCount > permanentLikeCount + 3) { bool num2 = num != 0 || !orCreate.SuspiciousGrowth; orCreate.KnownLikeCount = 0; orCreate.SuspiciousGrowth = true; _records[steamId] = orCreate; if (num2) { Save(); _logger.LogWarning((object)$"RecentPlayers suspicious self reference ignored: steamId=[{steamId}] remote=[{remoteCount}] permanent=[{permanentLikeCount}]"); } return num2; } if (remoteCount <= num) { return false; } orCreate.KnownLikeCount = remoteCount; _records[steamId] = orCreate; Save(); return true; } internal List GetLikedSteamIds() { return (from entry in _records.Values where entry != null && entry.LikedByMe && !string.IsNullOrWhiteSpace(entry.SteamId) select entry.SteamId).Distinct(StringComparer.Ordinal).ToList(); } internal List GetReferenceEntries() { return (from entry in _records.Values where entry != null && !string.IsNullOrWhiteSpace(entry.SteamId) && GetPermanentLikeCount(entry) > 0 select new RecentPlayerLikeRecord { SteamId = entry.SteamId, LikedByMe = entry.LikedByMe, LastLikedLobbyId = (entry.LastLikedLobbyId ?? string.Empty), KnownLikeCount = GetPermanentLikeCount(entry), SuspiciousGrowth = entry.SuspiciousGrowth, UniqueLikerIds = NormalizeContributorIds(entry.UniqueLikerIds) } into entry orderby entry.KnownLikeCount descending select entry).ThenBy((RecentPlayerLikeRecord entry) => entry.SteamId, StringComparer.Ordinal).ToList(); } private void LoadLegacyJson(string text) { RecentPlayerLikesSaveData recentPlayerLikesSaveData = JsonUtility.FromJson(text); if (recentPlayerLikesSaveData?.Entries == null) { return; } _records.Clear(); RecentPlayerLikeRecord[] entries = recentPlayerLikesSaveData.Entries; foreach (RecentPlayerLikeRecord recentPlayerLikeRecord in entries) { if (recentPlayerLikeRecord != null && !string.IsNullOrWhiteSpace(recentPlayerLikeRecord.SteamId)) { RecentPlayerLikeRecord recentPlayerLikeRecord2 = recentPlayerLikeRecord; if (recentPlayerLikeRecord2.LastLikedLobbyId == null) { recentPlayerLikeRecord2.LastLikedLobbyId = string.Empty; } recentPlayerLikeRecord.KnownLikeCount = Mathf.Max(0, recentPlayerLikeRecord.KnownLikeCount); recentPlayerLikeRecord.UniqueLikerIds = NormalizeContributorIds(recentPlayerLikeRecord.UniqueLikerIds); _records[recentPlayerLikeRecord.SteamId] = recentPlayerLikeRecord; } } } private void LoadLineFormat(string text) { _records.Clear(); string[] array = text.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { if (text2.StartsWith("#meta\t", StringComparison.Ordinal)) { string[] array2 = text2.Split('\t'); if (array2.Length >= 3 && string.Equals(array2[1], "uniquePlayersMetCount", StringComparison.Ordinal) && int.TryParse(array2[2], out var result)) { _uniquePlayersMetCount = Mathf.Max(0, result); } continue; } string[] array3 = text2.Split('\t'); if (array3.Length < 5) { continue; } string text3 = Unescape(array3[0]); if (!string.IsNullOrWhiteSpace(text3)) { string[] values = Array.Empty(); if (array3.Length >= 6 && !string.IsNullOrWhiteSpace(array3[5])) { values = array3[5].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(Unescape).ToArray(); } _records[text3] = new RecentPlayerLikeRecord { SteamId = text3, LikedByMe = (array3[1] == "1"), LastLikedLobbyId = Unescape(array3[2]), KnownLikeCount = (int.TryParse(array3[3], out var result2) ? Mathf.Max(0, result2) : 0), SuspiciousGrowth = (array3[4] == "1"), UniqueLikerIds = NormalizeContributorIds(values) }; } } } private static bool IsPackedFormat(byte[] bytes) { if (bytes == null || bytes.Length < PackedMagic.Length + 32 + 1) { return false; } for (int i = 0; i < PackedMagic.Length; i++) { if (bytes[i] != PackedMagic[i]) { return false; } } return true; } private string TryReadPackedPayload(byte[] bytes) { try { int num = PackedMagic.Length; byte[] array = new byte[32]; Buffer.BlockCopy(bytes, num, array, 0, array.Length); num += array.Length; byte[] array2 = new byte[bytes.Length - num]; Buffer.BlockCopy(bytes, num, array2, 0, array2.Length); byte[] array3 = Decompress(array2); byte[] array4 = ComputeSha256(array3); if (!((ReadOnlySpan)array).SequenceEqual((ReadOnlySpan)array4)) { _logger.LogWarning((object)"RecentPlayers likes checksum mismatch; ignoring packed likes file."); return null; } return Encoding.UTF8.GetString(array3); } catch (Exception ex) { _logger.LogWarning((object)("RecentPlayers packed likes read failed: " + ex.Message)); return null; } } private static byte[] BuildPackedPayload(string text) { byte[] bytes = Encoding.UTF8.GetBytes(text ?? string.Empty); byte[] array = ComputeSha256(bytes); byte[] array2 = Compress(bytes); byte[] array3 = new byte[PackedMagic.Length + array.Length + array2.Length]; Buffer.BlockCopy(PackedMagic, 0, array3, 0, PackedMagic.Length); Buffer.BlockCopy(array, 0, array3, PackedMagic.Length, array.Length); Buffer.BlockCopy(array2, 0, array3, PackedMagic.Length + array.Length, array2.Length); return array3; } private static byte[] Compress(byte[] input) { using MemoryStream memoryStream = new MemoryStream(); using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionLevel.Optimal, leaveOpen: true)) { gZipStream.Write(input, 0, input.Length); } return memoryStream.ToArray(); } private static byte[] Decompress(byte[] input) { using MemoryStream stream = new MemoryStream(input); using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(); gZipStream.CopyTo(memoryStream); return memoryStream.ToArray(); } private static byte[] ComputeSha256(byte[] input) { using SHA256 sHA = SHA256.Create(); return sHA.ComputeHash(input ?? Array.Empty()); } private static int GetPermanentLikeCount(RecentPlayerLikeRecord record) { if (record != null) { return NormalizeContributorIds(record.UniqueLikerIds).Length; } return 0; } private static string[] NormalizeContributorIds(IEnumerable values) { return values?.Where((string value) => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).OrderBy((string value) => value, StringComparer.Ordinal) .ToArray() ?? Array.Empty(); } private void TouchOutboundState() { _outboundRevision++; if (_outboundRevision <= 0) { _outboundRevision = 1; } } private bool TryGet(string steamId, out RecentPlayerLikeRecord record) { if (!string.IsNullOrWhiteSpace(steamId) && _records.TryGetValue(steamId, out record)) { return true; } record = null; return false; } private RecentPlayerLikeRecord GetOrCreate(string steamId) { if (_records.TryGetValue(steamId, out var value)) { return value; } RecentPlayerLikeRecord recentPlayerLikeRecord = new RecentPlayerLikeRecord { SteamId = steamId, LikedByMe = false, LastLikedLobbyId = string.Empty, KnownLikeCount = 0, SuspiciousGrowth = false, UniqueLikerIds = Array.Empty() }; _records[steamId] = recentPlayerLikeRecord; return recentPlayerLikeRecord; } private static string Escape(string value) { return Uri.EscapeDataString(value ?? string.Empty); } private static string Unescape(string value) { return Uri.UnescapeDataString(value ?? string.Empty); } } [Serializable] internal sealed class RecentPlayerLikesSyncEntry { public string SteamId; public int Count; } [Serializable] internal sealed class RecentPlayerLikesSyncPayload { public string OwnerSteamId; public string[] SteamIds = Array.Empty(); public RecentPlayerLikesSyncEntry[] Entries = Array.Empty(); } internal sealed class RecentPlayerLikesRemoteCount { internal int ActiveLikerCount; internal int MaxReferenceCount; internal HashSet ActiveLikerIds { get; } = new HashSet(StringComparer.Ordinal); internal int CombinedCount => Mathf.Max(ActiveLikerCount, MaxReferenceCount); } internal static class RecentPlayerLikesSyncBridge { private readonly struct ParsedPayload { internal readonly string OwnerSteamId; internal readonly HashSet LikedSteamIds; internal readonly Dictionary ReferenceCounts; internal ParsedPayload(string ownerSteamId, HashSet likedSteamIds, Dictionary referenceCounts) { OwnerSteamId = ownerSteamId ?? string.Empty; LikedSteamIds = likedSteamIds; ReferenceCounts = referenceCounts; } } private const string PlayerPropertyKey = "denis.irp.likes.v1"; private const float KeepAliveSeconds = 20f; private static readonly Dictionary LastPayloadByActor = new Dictionary(); private static readonly Dictionary ParsedPayloadByActor = new Dictionary(); private static readonly Dictionary LastLoggedCombinedCountBySteamId = new Dictionary(StringComparer.Ordinal); private static float _lastPublishAt = -100000f; private static string _lastPublishedPayload = string.Empty; private static string _lastPublishedRoomName = string.Empty; private static int _lastPublishedRevision = -1; internal static void TickPublish(RecentPlayerLikesStore store) { //IL_00a5: 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_00bd: Expected O, but got Unknown if (store == null) { return; } if (!PhotonNetwork.InRoom || PhotonNetwork.LocalPlayer == null || PhotonNetwork.CurrentRoom == null) { ResetPublishState(); return; } string text = PhotonNetwork.CurrentRoom.Name ?? string.Empty; int outboundRevision = store.OutboundRevision; bool flag = !string.Equals(text, _lastPublishedRoomName, StringComparison.Ordinal); bool flag2 = outboundRevision != _lastPublishedRevision; bool flag3 = Time.unscaledTime - _lastPublishAt >= 20f; if (!flag && !flag2 && !flag3) { return; } string text2 = BuildPayloadJson(store); bool flag4 = !string.Equals(text2, _lastPublishedPayload, StringComparison.Ordinal); if (!flag && !flag3 && !flag4) { _lastPublishedRevision = outboundRevision; return; } try { Hashtable val = new Hashtable { [(object)"denis.irp.likes.v1"] = text2 }; PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); _lastPublishedPayload = text2; _lastPublishedRoomName = text; _lastPublishedRevision = outboundRevision; _lastPublishAt = Time.unscaledTime; } catch (Exception ex) { ManualLogSource sharedLogger = Plugin.SharedLogger; if (sharedLogger != null) { sharedLogger.LogWarning((object)("RecentPlayers like sync publish failed: " + ex.Message)); } } } internal static RecentPlayerLikesRemoteCount GetRemoteLikeCountInfo(string steamId) { RecentPlayerLikesRemoteCount recentPlayerLikesRemoteCount = new RecentPlayerLikesRemoteCount(); if (string.IsNullOrWhiteSpace(steamId) || !PhotonNetwork.InRoom) { return recentPlayerLikesRemoteCount; } try { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { if (((val != null) ? val.CustomProperties : null) == null || !((Dictionary)(object)val.CustomProperties).TryGetValue((object)"denis.irp.likes.v1", out object value) || !(value is string text) || string.IsNullOrWhiteSpace(text)) { continue; } ParsedPayload parsedPayload = GetParsedPayload(val.ActorNumber, text); if (parsedPayload.LikedSteamIds.Contains(steamId)) { recentPlayerLikesRemoteCount.ActiveLikerCount++; string stablePlayerId = GetStablePlayerId(val, parsedPayload.OwnerSteamId); if (!string.IsNullOrWhiteSpace(stablePlayerId)) { recentPlayerLikesRemoteCount.ActiveLikerIds.Add(stablePlayerId); } } if (parsedPayload.ReferenceCounts.TryGetValue(steamId, out var value2) && value2 > recentPlayerLikesRemoteCount.MaxReferenceCount) { recentPlayerLikesRemoteCount.MaxReferenceCount = value2; } } } catch (Exception ex) { ManualLogSource sharedLogger = Plugin.SharedLogger; if (sharedLogger != null) { sharedLogger.LogWarning((object)("RecentPlayers like sync read failed: " + ex.Message)); } } return recentPlayerLikesRemoteCount; } private static void ResetPublishState() { _lastPublishedPayload = string.Empty; _lastPublishedRoomName = string.Empty; _lastPublishedRevision = -1; _lastPublishAt = -100000f; LastPayloadByActor.Clear(); ParsedPayloadByActor.Clear(); LastLoggedCombinedCountBySteamId.Clear(); } private static string BuildPayloadJson(RecentPlayerLikesStore store) { RecentPlayerLikesSyncEntry[] entries = (from record in store.GetReferenceEntries() select new RecentPlayerLikesSyncEntry { SteamId = record.SteamId, Count = Mathf.Max(0, record.KnownLikeCount) }).ToArray(); return JsonUtility.ToJson((object)new RecentPlayerLikesSyncPayload { OwnerSteamId = Plugin.GetLocalSteamId(), SteamIds = store.GetLikedSteamIds().ToArray(), Entries = entries }); } private static ParsedPayload GetParsedPayload(int actorNumber, string payload) { if (LastPayloadByActor.TryGetValue(actorNumber, out var value) && string.Equals(value, payload, StringComparison.Ordinal) && ParsedPayloadByActor.TryGetValue(actorNumber, out var value2)) { return value2; } ParsedPayload parsedPayload = ParsePayload(payload); LastPayloadByActor[actorNumber] = payload; ParsedPayloadByActor[actorNumber] = parsedPayload; return parsedPayload; } private static ParsedPayload ParsePayload(string payload) { string ownerSteamId = string.Empty; HashSet hashSet = new HashSet(StringComparer.Ordinal); Dictionary dictionary = new Dictionary(StringComparer.Ordinal); try { RecentPlayerLikesSyncPayload recentPlayerLikesSyncPayload = JsonUtility.FromJson(payload); if (recentPlayerLikesSyncPayload != null) { ownerSteamId = recentPlayerLikesSyncPayload.OwnerSteamId?.Trim() ?? string.Empty; if (recentPlayerLikesSyncPayload.SteamIds != null) { for (int i = 0; i < recentPlayerLikesSyncPayload.SteamIds.Length; i++) { string text = recentPlayerLikesSyncPayload.SteamIds[i]; if (!string.IsNullOrWhiteSpace(text)) { hashSet.Add(text); } } } if (recentPlayerLikesSyncPayload.Entries != null) { for (int j = 0; j < recentPlayerLikesSyncPayload.Entries.Length; j++) { RecentPlayerLikesSyncEntry recentPlayerLikesSyncEntry = recentPlayerLikesSyncPayload.Entries[j]; if (recentPlayerLikesSyncEntry != null && !string.IsNullOrWhiteSpace(recentPlayerLikesSyncEntry.SteamId)) { int num = Mathf.Max(0, recentPlayerLikesSyncEntry.Count); if (dictionary.TryGetValue(recentPlayerLikesSyncEntry.SteamId, out var value)) { dictionary[recentPlayerLikesSyncEntry.SteamId] = Mathf.Max(value, num); } else { dictionary[recentPlayerLikesSyncEntry.SteamId] = num; } } } } } } catch { } return new ParsedPayload(ownerSteamId, hashSet, dictionary); } private static string GetStablePlayerId(Player player, string ownerSteamId) { if (!string.IsNullOrWhiteSpace(ownerSteamId)) { return ownerSteamId.Trim(); } return string.Empty; } } internal static class RecentPlayerProfileButton { private static Sprite _steamSprite; private static bool _steamSpriteLoadAttempted; private static Sprite _steamHoverSprite; private static bool _steamHoverSpriteLoadAttempted; private static readonly Color BadgeTextColor = new Color(0.95f, 0.95f, 0.95f, 1f); internal static void Attach(Transform parent, float buttonWidth, float buttonHeight) { //IL_001e: 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) float iconSize = 18f; Vector3 localOffset = default(Vector3); ((Vector3)(ref localOffset))..ctor(0f, 0f, 0f); if (!TryBuildSteamPngIcon(parent, iconSize, localOffset)) { BuildFallbackTextIcon(parent, iconSize, localOffset); } } private static bool TryBuildSteamPngIcon(Transform parent, float iconSize, Vector3 localOffset) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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) Sprite steamSprite = GetSteamSprite(); if ((Object)(object)steamSprite == (Object)null) { return false; } GameObject val = new GameObject("SteamPngIcon", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.sizeDelta = new Vector2(iconSize, iconSize); ((Transform)component).localScale = Vector3.one; ((Transform)component).localPosition = localOffset; Image component2 = val.GetComponent(); component2.sprite = steamSprite; ((Graphic)component2).color = new Color(1f, 1f, 1f, 1f); ((Graphic)component2).raycastTarget = false; component2.preserveAspect = true; Sprite steamHoverSprite = GetSteamHoverSprite(); if ((Object)(object)steamHoverSprite != (Object)null) { val.AddComponent().Initialize((RectTransform)(object)((parent is RectTransform) ? parent : null), component2, steamSprite, steamHoverSprite); } return true; } private static void BuildFallbackTextIcon(Transform parent, float iconSize, Vector3 localOffset) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0030: 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_0076: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SteamFallbackText"); val.transform.SetParent(parent, false); TextMeshProUGUI obj = val.AddComponent(); RectTransform rectTransform = ((TMP_Text)obj).rectTransform; rectTransform.sizeDelta = new Vector2(iconSize, iconSize); ((Transform)rectTransform).localScale = Vector3.one; ((Transform)rectTransform).localPosition = localOffset; ((TMP_Text)obj).text = "S"; ((TMP_Text)obj).fontSize = 18f; ((TMP_Text)obj).fontStyle = (FontStyles)1; ((TMP_Text)obj).alignment = (TextAlignmentOptions)514; ((TMP_Text)obj).enableWordWrapping = false; ((Graphic)obj).raycastTarget = false; ((Graphic)obj).color = BadgeTextColor; } private static Sprite GetSteamSprite() { if (_steamSpriteLoadAttempted) { return _steamSprite; } _steamSpriteLoadAttempted = true; _steamSprite = RecentPlayerIconLoader.LoadSprite("steam.png", "RecentPlayersSteamPng", "RecentPlayersSteamSprite"); return _steamSprite; } private static Sprite GetSteamHoverSprite() { if (_steamHoverSpriteLoadAttempted) { return _steamHoverSprite; } _steamHoverSpriteLoadAttempted = true; _steamHoverSprite = RecentPlayerIconLoader.LoadSprite("steam_hover.png", "RecentPlayersSteamHoverPng", "RecentPlayersSteamHoverSprite"); return _steamHoverSprite; } } [Serializable] public sealed class RecentPlayerRecord { public string Key; public string DisplayName; public string SteamId; public long LastSeenUtcTicks; } [Serializable] public sealed class RecentPlayersSaveData { public RecentPlayerRecord[] Players; } internal sealed class RecentPlayerRow { private const float RowHeight = 40f; private const float ProfileButtonWidth = 32f; private const float InviteButtonWidth = 76f; private const float TextLeftInset = 0f; private const float FriendSlotWidth = 24f; private const float BadgeGap = 2f; private static readonly Color FrameBackgroundColor = new Color(0f, 0f, 0f, 0.96f); private static readonly Color LobbyFrameBackgroundColor = new Color(0.1961f, 0.0784f, 0f, 1f); private static readonly Color SelectedBorderColor = new Color(0.98f, 0.39f, 0f, 1f); private static readonly Color TransparentBorderColor = Color.clear; private static readonly Color DefaultTextColor = new Color(0.56f, 0.56f, 0.56f, 1f); private static readonly Color HighlightTextColor = Color.white; private static readonly Color CooldownTextColor = new Color(0.247f, 0.247f, 0.247f, 1f); private static readonly Color LobbyTextColor = Color.white; private readonly RecentPlayerRecord _player; private readonly REPOButton _hostButton; private readonly RectTransform _frame; private readonly TextMeshProUGUI _nameLabel; private readonly RecentPlayerUiButton _mainButton; private readonly RecentPlayerUiButton _profileButton; private readonly RecentPlayerUiButton _inviteButton; private readonly RecentPlayerInviteButton _inviteVisual; private readonly Image[] _frameBorders; private readonly RectTransform _friendSlot; private readonly RecentPlayerFriendBadge _friendBadge; private readonly RecentPlayerLabelHover _labelHover; private readonly float _labelX; private readonly float _maxNickWidth; internal string Key => _player.Key; internal RectTransform Root => ((REPOElement)_hostButton).rectTransform; internal bool HasProfileButton => (Object)(object)_profileButton != (Object)null; internal Vector2 MainButtonPosition { get { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_mainButton != (Object)null)) { return Vector2.zero; } return Vector2.op_Implicit(((Transform)_mainButton.Rect).localPosition); } } internal Vector2 MainButtonSize { get { //IL_0065: 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_004c: 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) if (!((Object)(object)_mainButton != (Object)null)) { Vector2? val = _hostButton?.overrideButtonSize; if (!val.HasValue) { if (!((Object)(object)_frame != (Object)null)) { return Vector2.zero; } return _frame.sizeDelta; } return val.GetValueOrDefault(); } return _mainButton.Rect.sizeDelta; } } internal Vector2 ProfileButtonPosition { get { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_profileButton != (Object)null)) { return Vector2.zero; } return Vector2.op_Implicit(((Transform)_profileButton.Rect).localPosition); } } internal Vector2 ProfileButtonSize { get { //IL_001f: 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) if (!((Object)(object)_profileButton != (Object)null)) { return Vector2.zero; } return _profileButton.Rect.sizeDelta; } } internal Vector2 InviteButtonPosition { get { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_inviteButton != (Object)null)) { return Vector2.zero; } return Vector2.op_Implicit(((Transform)_inviteButton.Rect).localPosition); } } internal Vector2 InviteButtonSize { get { //IL_001f: 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) if (!((Object)(object)_inviteButton != (Object)null)) { return Vector2.zero; } return _inviteButton.Rect.sizeDelta; } } internal RecentPlayerRow(RecentPlayerRecord player, REPOButton hostButton, RectTransform frame, TextMeshProUGUI nameLabel, RecentPlayerUiButton mainButton, RecentPlayerUiButton profileButton, RecentPlayerUiButton inviteButton, RecentPlayerInviteButton inviteVisual, Image[] frameBorders, RectTransform friendSlot, RecentPlayerFriendBadge friendBadge, RecentPlayerLabelHover labelHover, float labelX, float maxNickWidth) { _player = player; _hostButton = hostButton; _frame = frame; _nameLabel = nameLabel; _mainButton = mainButton; _profileButton = profileButton; _inviteButton = inviteButton; _inviteVisual = inviteVisual; _frameBorders = frameBorders; _friendSlot = friendSlot; _friendBadge = friendBadge; _labelHover = labelHover; _labelX = labelX; _maxNickWidth = maxNickWidth; } internal static RecentPlayerRow Create(RecentPlayerRecord player, Transform parent, float rowWidth, Action onMainClick, Action onProfileClick, Action onInviteClick) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010f: 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_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0254: 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_026c: 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_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: 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_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: 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_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_040a: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_0453: 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_0482: Unknown result type (might be due to invalid IL or missing references) //IL_0487: Unknown result type (might be due to invalid IL or missing references) //IL_0489: Unknown result type (might be due to invalid IL or missing references) //IL_048b: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_0495: Unknown result type (might be due to invalid IL or missing references) //IL_04ae: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_04bc: Unknown result type (might be due to invalid IL or missing references) //IL_0503: Unknown result type (might be due to invalid IL or missing references) //IL_0526: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Expected O, but got Unknown REPOButton val = MenuAPI.CreateREPOButton(string.Empty, (Action)delegate { }, parent, default(Vector2)); StyleHostButton(val, rowWidth); RectTransform rectTransform = ((REPOElement)val).rectTransform; GameObject val2 = new GameObject("RecentPlayerRowFrame", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent((Transform)(object)rectTransform, false); RectTransform component = val2.GetComponent(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(rowWidth, 40f); ((Transform)component).localScale = Vector3.one; ((Transform)component).localPosition = Vector3.zero; component.anchoredPosition = Vector2.zero; ((Transform)component).SetAsFirstSibling(); Image component2 = val2.GetComponent(); ((Graphic)component2).color = FrameBackgroundColor; ((Graphic)component2).raycastTarget = false; Image[] frameBorders = CreateFrameBorders((Transform)(object)component, TransparentBorderColor); TMP_FontAsset font = ResolveFont(val.labelTMP); bool num = Plugin.IsValidSteamId(player.SteamId); RecentPlayerUiButton recentPlayerUiButton = null; float num2 = rowWidth - 76f - 24f; Color val3 = default(Color); ((Color)(ref val3))..ctor(0f, 0f, 0f, 0f); new Color(1f, 1f, 1f, 0.035f); Color textColor = default(Color); ((Color)(ref textColor))..ctor(0.95f, 0.95f, 0.95f, 1f); if (num) { recentPlayerUiButton = RecentPlayerUiButton.Create((Transform)(object)component, "RecentPlayerProfileButton", new Vector2(32f, 40f), new Vector2(0f - rowWidth * 0.5f + 16f, 0f), onProfileClick, font, 16f, (TextAlignmentOptions)514, Vector4.zero, val3, val3, Color.clear, Color.clear, Color.clear, textColor); recentPlayerUiButton.SetText(string.Empty); RecentPlayerProfileButton.Attach((Transform)(object)recentPlayerUiButton.Rect, 32f, 40f); num2 -= 32f; } RecentPlayerUiButton recentPlayerUiButton2 = RecentPlayerUiButton.Create((Transform)(object)component, "RecentPlayerInviteButton", new Vector2(76f, 40f), new Vector2(rowWidth * 0.5f - 38f, 0f), onInviteClick, font, 14f, (TextAlignmentOptions)514, Vector4.zero, val3, val3, Color.clear, Color.clear, Color.clear, textColor); recentPlayerUiButton2.SetText(string.Empty); RecentPlayerInviteButton inviteVisual = RecentPlayerInviteButton.Attach((Transform)(object)recentPlayerUiButton2.Rect, 76f, 40f); float num3 = (num ? 32f : 0f); float num4 = num2 - 24f - 2f; TextMeshProUGUI labelTMP = val.labelTMP; ((TMP_Text)labelTMP).overflowMode = (TextOverflowModes)1; ((TMP_Text)labelTMP).text = string.Empty; ((Transform)((TMP_Text)labelTMP).rectTransform).SetParent((Transform)(object)component, false); RectTransform rectTransform2 = ((TMP_Text)labelTMP).rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0f); rectTransform2.anchorMax = new Vector2(0f, 1f); rectTransform2.pivot = new Vector2(0f, 0.5f); rectTransform2.anchoredPosition = new Vector2(num3, 0f); rectTransform2.sizeDelta = new Vector2(num4, 0f); ((Transform)rectTransform2).localScale = Vector3.one; ((TMP_Text)labelTMP).enableAutoSizing = false; ((TMP_Text)labelTMP).isTextObjectScaleStatic = false; ((Behaviour)labelTMP).enabled = true; GameObject val4 = new GameObject("RecentPlayerFriendSlot", new Type[1] { typeof(RectTransform) }); val4.transform.SetParent((Transform)(object)component, false); RectTransform component3 = val4.GetComponent(); component3.anchorMin = new Vector2(0f, 0f); component3.anchorMax = new Vector2(0f, 1f); component3.pivot = new Vector2(0f, 0.5f); component3.anchoredPosition = new Vector2(num3, 0f); component3.sizeDelta = new Vector2(24f, 0f); ((Transform)component3).localScale = Vector3.one; RecentPlayerFriendBadge friendBadge = RecentPlayerFriendBadge.Attach((Transform)(object)component3, component); val4.SetActive(false); RecentPlayerUiButton recentPlayerUiButton3 = RecentPlayerUiButton.Create((Transform)(object)component, "RecentPlayerMainHitButton", new Vector2(num2, 40f), new Vector2(num3 + num2 * 0.5f - rowWidth * 0.5f, 0f), onMainClick, font, 1f, (TextAlignmentOptions)513, Vector4.zero, val3, val3, Color.clear, Color.clear, Color.clear, new Color(1f, 1f, 1f, 0f)); recentPlayerUiButton3.SetText(string.Empty); ((Transform)recentPlayerUiButton2.Rect).SetAsLastSibling(); if (recentPlayerUiButton != null) { ((Transform)recentPlayerUiButton.Rect).SetAsLastSibling(); } ((Graphic)labelTMP).color = new Color(0.95f, 0.95f, 0.95f, 1f); if ((Object)(object)((Graphic)labelTMP).material == (Object)null) { Material material = new Material(Shader.Find("TextMeshPro/Distance Field")); ((Graphic)labelTMP).material = material; } RecentPlayerLabelHover recentPlayerLabelHover = ((Component)val).gameObject.AddComponent(); recentPlayerLabelHover.Initialize(component, labelTMP); RecentPlayerRow recentPlayerRow = new RecentPlayerRow(player, val, component, labelTMP, recentPlayerUiButton3, recentPlayerUiButton, recentPlayerUiButton2, inviteVisual, frameBorders, component3, friendBadge, recentPlayerLabelHover, num3, num4); recentPlayerRow.Refresh(isSelected: false, RecentPlayerInviteCooldownState.None, isAlreadyInLobby: false, 0, isLikedByMe: false, Plugin.IsLocalPlayerSteamId(player.SteamId)); return recentPlayerRow; } internal void Refresh(bool isSelected, RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby, int likeCount, bool isLikedByMe, bool isSelfProfile) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_00ad: 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_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) bool isFriend = !isSelfProfile && Plugin.IsSteamFriend(_player.SteamId); bool frameSelected = ShouldShowSelectedBorder(isSelected, cooldownState, isAlreadyInLobby, isSelfProfile); bool flag = IsRowInteractable(cooldownState, isAlreadyInLobby, isSelfProfile); bool interactable = IsProfileInteractable(isSelfProfile); Color mainTextColor = GetMainTextColor(isSelected, cooldownState, isAlreadyInLobby); RecentPlayerActionVisualState actionState = GetActionState(cooldownState, isAlreadyInLobby, isFriend, isLikedByMe, isSelfProfile); ((TMP_Text)_nameLabel).text = BuildLabel(_player, cooldownState, isAlreadyInLobby); ((Graphic)_nameLabel).color = mainTextColor; RepositionFriendBadgeSlot(); _labelHover?.SetColors(mainTextColor, HighlightTextColor); _labelHover?.SetEnabled(flag); SetFrameBackground(isAlreadyInLobby ? LobbyFrameBackgroundColor : FrameBackgroundColor); _profileButton?.SetSelected(selected: false); _profileButton?.SetInteractable(interactable); _mainButton?.SetSelected(selected: false); _mainButton?.SetInteractable(flag); _inviteButton?.SetSelected(selected: false); _inviteButton?.SetBackgroundColors(isAlreadyInLobby ? LobbyFrameBackgroundColor : Color.clear, isAlreadyInLobby ? LobbyFrameBackgroundColor : Color.clear); _inviteButton?.SetInteractable(IsActionInteractable(cooldownState, isAlreadyInLobby, isFriend, isSelfProfile)); _inviteVisual?.Refresh(actionState, likeCount); RefreshFriendBadge(cooldownState, isAlreadyInLobby, isFriend); SetFrameSelected(frameSelected); } private void SetFrameSelected(bool selected) { //IL_0013: 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_0018: 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) if (_frameBorders == null) { return; } Color color = (selected ? SelectedBorderColor : TransparentBorderColor); Image[] frameBorders = _frameBorders; foreach (Image val in frameBorders) { if ((Object)(object)val != (Object)null) { ((Graphic)val).color = color; } } } private void SetFrameBackground(Color color) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_frame == (Object)null)) { Image component = ((Component)_frame).GetComponent(); if ((Object)(object)component != (Object)null) { ((Graphic)component).color = color; } } } private void RepositionFriendBadgeSlot() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_friendSlot == (Object)null) && !((Object)(object)_nameLabel == (Object)null)) { float num = Mathf.Min(((TMP_Text)_nameLabel).preferredWidth, _maxNickWidth); _friendSlot.anchoredPosition = new Vector2(_labelX + num + 2f, 0f); } } private void RefreshFriendBadge(RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby, bool isFriend) { bool flag = isFriend; if ((Object)(object)_friendSlot != (Object)null) { ((Component)_friendSlot).gameObject.SetActive(flag); } _friendBadge?.Refresh(flag); } private static void StyleHostButton(REPOButton button, float rowWidth) { //IL_0049: 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_0095: 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_00af: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI labelTMP = button.labelTMP; ((TMP_Text)labelTMP).fontStyle = (FontStyles)0; ((TMP_Text)labelTMP).fontSize = 20f; ((TMP_Text)labelTMP).alignment = (TextAlignmentOptions)513; ((TMP_Text)labelTMP).enableWordWrapping = false; ((Graphic)labelTMP).raycastTarget = false; ((TMP_Text)labelTMP).text = string.Empty; ((Behaviour)labelTMP).enabled = true; button.overrideButtonSize = new Vector2(rowWidth, 40f); MenuButton component = ((Component)button).GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } Image[] componentsInChildren = ((Component)button).GetComponentsInChildren(true); foreach (Image val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { ((Graphic)val).color = new Color(((Graphic)val).color.r, ((Graphic)val).color.g, ((Graphic)val).color.b, 0f); ((Graphic)val).raycastTarget = false; } } } private static Image[] CreateFrameBorders(Transform parent, Color color) { //IL_0018: 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_0036: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0126: 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) return (Image[])(object)new Image[4] { CreateBorderLine(parent, "Top", new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 1f), color), CreateBorderLine(parent, "Bottom", new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 1f), color), CreateBorderLine(parent, "Left", new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(1f, 0f), color), CreateBorderLine(parent, "Right", new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2(1f, 0f), color) }; } private static Image CreateBorderLine(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 sizeDelta, Color color) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_0052: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("RowBorder_" + name, new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.anchorMin = anchorMin; component.anchorMax = anchorMax; component.pivot = pivot; component.sizeDelta = sizeDelta; component.anchoredPosition = Vector2.zero; Image component2 = val.GetComponent(); ((Graphic)component2).color = color; ((Graphic)component2).raycastTarget = false; return component2; } private static string BuildLabel(RecentPlayerRecord player, RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby) { if (cooldownState.IsActive) { return player.DisplayName + " " + cooldownState.Suffix; } return player.DisplayName; } private static bool ShouldShowSelectedBorder(bool isSelected, RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby, bool isSelfProfile) { if (!isSelfProfile && isSelected && !isAlreadyInLobby) { return !cooldownState.IsActive; } return false; } private static bool IsRowInteractable(RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby, bool isSelfProfile) { if (!isSelfProfile && !cooldownState.IsActive) { return !isAlreadyInLobby; } return false; } private static bool IsProfileInteractable(bool isSelfProfile) { return true; } private static bool IsActionInteractable(RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby, bool isFriend, bool isSelfProfile) { if (!isSelfProfile) { if (!isAlreadyInLobby) { return !cooldownState.IsActive && isFriend; } return true; } return false; } private static RecentPlayerActionVisualState GetActionState(RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby, bool isFriend, bool isLikedByMe, bool isSelfProfile) { if (isSelfProfile) { return RecentPlayerActionVisualState.LikeActive; } if (isAlreadyInLobby) { if (!isLikedByMe) { return RecentPlayerActionVisualState.Like; } return RecentPlayerActionVisualState.LikeActive; } if (cooldownState.IsActive && !isFriend) { return RecentPlayerActionVisualState.Warning; } if (!cooldownState.IsActive && isFriend) { return RecentPlayerActionVisualState.Invite; } return RecentPlayerActionVisualState.Hidden; } private static Color GetMainTextColor(bool isSelected, RecentPlayerInviteCooldownState cooldownState, bool isAlreadyInLobby) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (cooldownState.IsActive) { return CooldownTextColor; } if (isAlreadyInLobby) { return LobbyTextColor; } if (!isSelected) { return DefaultTextColor; } return HighlightTextColor; } private static TMP_FontAsset ResolveFont(TextMeshProUGUI label) { if ((Object)(object)((label != null) ? ((TMP_Text)label).font : null) != (Object)null) { return ((TMP_Text)label).font; } TextMeshProUGUI val = Object.FindObjectOfType(); if (!((Object)(object)val != (Object)null)) { return TMP_Settings.defaultFontAsset; } return ((TMP_Text)val).font; } } internal sealed class RecentPlayersInviteService { private readonly ManualLogSource _logger; internal RecentPlayersInviteService(ManualLogSource logger) { _logger = logger; } internal List InvitePlayers(IEnumerable players) { List list = new List(); HashSet hashSet = (from friend in SteamFriends.GetFriends() select SteamId.op_Implicit(friend.Id)).ToHashSet(); foreach (RecentPlayerRecord player in players) { if (!Plugin.IsValidSteamId(player.SteamId)) { _logger.LogWarning((object)("Invite skipped for " + player.DisplayName + ": invalid Steam ID [" + player.SteamId + "] (fallback key?)")); } else { ulong result; bool isFriend = ulong.TryParse(player.SteamId, out result) && hashSet.Contains(result); if (TryInvitePlayer(player, isFriend, out var message)) { list.Add(player); } else { _logger.LogWarning((object)("Invite action failed for " + player.DisplayName + ": " + message)); } } } return list; } private bool TryInvitePlayer(RecentPlayerRecord player, bool isFriend, out string message) { //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) message = string.Empty; if (!Plugin.IsValidSteamId(player.SteamId)) { message = player.DisplayName + " has invalid Steam ID. steamId=[" + player.SteamId + "] (not 17 digits or doesn't start with 765611)"; return false; } if (!ulong.TryParse(player.SteamId, out var result)) { message = $"{player.DisplayName} Steam ID failed to parse. steamId=[{player.SteamId}] isFriend=[{isFriend}]"; return false; } SteamManager instance = SteamManager.instance; if ((Object)(object)instance == (Object)null) { message = $"SteamManager is not ready yet. isFriend=[{isFriend}]"; return false; } FieldInfo field = typeof(SteamManager).GetField("currentLobby", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { message = $"Couldn't find current lobby field. isFriend=[{isFriend}]"; return false; } object value = field.GetValue(instance); if (value == null) { message = $"No active Steam lobby found. isFriend=[{isFriend}]"; return false; } Lobby val = (Lobby)value; SteamId id = ((Lobby)(ref val)).Id; if (!((SteamId)(ref id)).IsValid) { message = $"No active Steam lobby found. isFriend=[{isFriend}]"; return false; } SteamId val2 = SteamId.op_Implicit(result); bool flag = false; try { flag = ((Lobby)(ref val)).InviteFriend(val2); } catch (Exception ex) { _logger.LogWarning((object)$"InviteFriend threw for {player.DisplayName}: steamId=[{player.SteamId}] isFriend=[{isFriend}] error=[{ex.Message}]"); } if (!flag) { try { SteamFriends.OpenUserOverlay(val2, "steamid"); message = $"InviteFriend returned false; opened Steam overlay for {player.DisplayName}. steamId=[{player.SteamId}] isFriend=[{isFriend}]"; return true; } catch (Exception ex2) { _logger.LogWarning((object)$"OpenUserOverlay fallback failed for {player.DisplayName}: steamId=[{player.SteamId}] isFriend=[{isFriend}] error=[{ex2.Message}]"); return false; } } message = $"Steam lobby invite sent to {player.DisplayName}. steamId=[{player.SteamId}] isFriend=[{isFriend}] inviteFriend=[true]"; return true; } } internal sealed class RecentPlayersPanel { private readonly struct RecentPlayerRowState { internal readonly bool IsSelected; internal readonly bool IsAlreadyInLobby; internal readonly RecentPlayerInviteCooldownState CooldownState; internal readonly bool IsLikedByMe; internal readonly int LikeCount; internal readonly bool IsSelfProfile; internal RecentPlayerRowState(bool isSelected, bool isAlreadyInLobby, RecentPlayerInviteCooldownState cooldownState, bool isLikedByMe, int likeCount, bool isSelfProfile) { IsSelected = isSelected; IsAlreadyInLobby = isAlreadyInLobby; CooldownState = cooldownState; IsLikedByMe = isLikedByMe; LikeCount = likeCount; IsSelfProfile = isSelfProfile; } } private readonly RecentPlayersStore _store; private readonly RecentPlayerLikesStore _likesStore; private readonly RecentPlayersTracker _tracker; private readonly RecentPlayersInviteService _inviteService; private readonly ManualLogSource _logger; private static readonly FieldInfo JoiningPlayersCanvasGroupField = typeof(MenuPageLobby).GetField("joiningPlayersCanvasGroup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private REPOPopupPage _page; private REPOButton _lobbyButton; private REPOButton _selectAllButton; private REPOButton _inviteButton; private InviteSentLobbyBanner _inviteSentBanner; private RectTransform _listViewport; private RectTransform _listContentRoot; private readonly List _listVisuals = new List(); private readonly Dictionary _playerRows = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _visiblePlayersByKey = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly HashSet _selectedPlayerKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _inviteCooldownUntilByPlayerKey = new Dictionary(StringComparer.OrdinalIgnoreCase); private float _inviteStatusUntil; private float _suppressProfileOpenUntil; private readonly OverlayNativeScrollController _scrollController = new OverlayNativeScrollController(8f, 6f, 36f); private bool _pageOpen; private int _lastLobbyStateVersion = -1; private int _lastLobbyRecordsVersion = -1; private int _lastCooldownVisualFrame = -1; private int _lastFriendVisualFrame = -1; private float _pendingRebuildAt = -1f; private readonly Dictionary _remoteCountCache = new Dictionary(StringComparer.Ordinal); private float _remoteCountCacheRefreshedAt = -100f; private const float InviteCooldownSeconds = 10f; private const float ListSideInset = 8f; private const float ListTopPadding = 6f; private const float RowSpacing = 2f; private const float ListBottomPadding = 12f; private const float EmptyStateHeight = 40f; private const float ScrollStep = 36f; private const float RemoteCountCacheTtl = 0.5f; internal RecentPlayersPanel(RecentPlayersStore store, RecentPlayerLikesStore likesStore, RecentPlayersTracker tracker, RecentPlayersInviteService inviteService, ManualLogSource logger) { _store = store; _likesStore = likesStore; _tracker = tracker; _inviteService = inviteService; _logger = logger; } internal void Tick() { UpdateLobbyButtonPosition(); _inviteSentBanner?.Tick(); bool flag = CleanupExpiredInviteCooldowns(); if (_pageOpen) { bool num = _lastLobbyStateVersion != _tracker.CurrentLobbyStateVersion; bool flag2 = _lastLobbyRecordsVersion != _tracker.CurrentLobbyRecordsVersion; int cooldownVisualFrame = GetCooldownVisualFrame(); int friendVisualFrame = GetFriendVisualFrame(); bool flag3 = _inviteCooldownUntilByPlayerKey.Count > 0 && cooldownVisualFrame != _lastCooldownVisualFrame; bool flag4 = friendVisualFrame != _lastFriendVisualFrame; if (num || flag || flag3 || flag4) { RefreshVisibleSelectionState(); } if (flag2 && _pendingRebuildAt < 0f) { _pendingRebuildAt = Time.unscaledTime + 1f; } _lastLobbyStateVersion = _tracker.CurrentLobbyStateVersion; _lastLobbyRecordsVersion = _tracker.CurrentLobbyRecordsVersion; _lastCooldownVisualFrame = cooldownVisualFrame; _lastFriendVisualFrame = friendVisualFrame; } if (_pendingRebuildAt >= 0f && Time.unscaledTime >= _pendingRebuildAt) { _pendingRebuildAt = -1f; if (_pageOpen) { Rebuild(); } } if (_inviteStatusUntil > 0f && Time.unscaledTime >= _inviteStatusUntil) { _inviteStatusUntil = 0f; UpdateActionButtons(); } TickRemoteCountCache(); } private void TickRemoteCountCache() { if (!_pageOpen || _likesStore == null || _visiblePlayersByKey.Count == 0 || Time.unscaledTime - _remoteCountCacheRefreshedAt < 0.5f) { return; } _remoteCountCacheRefreshedAt = Time.unscaledTime; foreach (RecentPlayerRecord value in _visiblePlayersByKey.Values) { if (value != null && !string.IsNullOrWhiteSpace(value.SteamId)) { bool isLocalPlayer = IsLocalPlayerSteamId(value.SteamId); RecentPlayerLikesRemoteCount remoteLikeCountInfo = RecentPlayerLikesSyncBridge.GetRemoteLikeCountInfo(value.SteamId); _remoteCountCache[value.SteamId] = remoteLikeCountInfo; _likesStore.ApplyLiveLikeContributions(value.SteamId, remoteLikeCountInfo.ActiveLikerIds, isLocalPlayer); _likesStore.UpdateKnownLikeCountFromRemote(value.SteamId, remoteLikeCountInfo.MaxReferenceCount, isLocalPlayer); _likesStore.UpdateKnownLikeCountFromRemote(value.SteamId, remoteLikeCountInfo.ActiveLikerCount, isLocalPlayer); } } RefreshVisibleSelectionState(); } internal void BuildLobbyButton(Transform parent) { //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) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Vector2 lobbyButtonPosition = Plugin.GetLobbyButtonPosition(); _lobbyButton = MenuAPI.CreateREPOButton("Player Tracker", (Action)TogglePage, parent, lobbyButtonPosition); } private void TogglePage() { EnsurePage(); if (_pageOpen) { _page.ClosePage(false); _pageOpen = false; if ((Object)(object)_listViewport != (Object)null) { ((Component)_listViewport).gameObject.SetActive(false); } ResetTransientSelectionState(); return; } Rebuild(); if ((Object)(object)_listViewport != (Object)null) { ((Component)_listViewport).gameObject.SetActive(false); } _page.OpenPage(true); ((Component)_page).GetComponent().PageStateSet((PageState)1); _pageOpen = true; } private void UpdateLobbyButtonPosition() { //IL_000f: 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) //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_0026: 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_003f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_lobbyButton == (Object)null)) { Vector2 lobbyButtonPosition = Plugin.GetLobbyButtonPosition(); if (!(((Transform)((REPOElement)_lobbyButton).rectTransform).localPosition == Vector2.op_Implicit(lobbyButtonPosition))) { ((Transform)((REPOElement)_lobbyButton).rectTransform).localPosition = Vector2.op_Implicit(lobbyButtonPosition); } } } private void EnsurePage() { //IL_0026: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown if (!((Object)(object)_page != (Object)null)) { _page = MenuAPI.CreateREPOPopupPage("Player Tracker", true, false, 0f, (Vector2?)new Vector2(40f, 0f)); _page.maskPadding = new Padding(0f, 0f, 0f, 12f); _page.onEscapePressed = (ShouldCloseMenuDelegate)delegate { _pageOpen = false; ResetTransientSelectionState(); return true; }; EnsureActionButtons(); } } private void EnsureCustomListHost() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_012d: 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_0174: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_page?.maskRectTransform == (Object)null) { return; } RectTransform maskRectTransform = _page.maskRectTransform; Transform parent = ((Transform)_page.rectTransform).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val == (Object)null) { return; } if ((Object)(object)_listViewport == (Object)null) { GameObject val2 = new GameObject("RecentPlayersViewport", new Type[3] { typeof(RectTransform), typeof(Image), typeof(RectMask2D) }); val2.transform.SetParent((Transform)(object)val, false); val2.transform.SetAsLastSibling(); _listViewport = val2.GetComponent(); _listViewport.anchorMin = new Vector2(0f, 0f); _listViewport.anchorMax = new Vector2(0f, 0f); _listViewport.pivot = new Vector2(0f, 0f); ((Transform)_listViewport).localScale = Vector3.one; Image component = val2.GetComponent(); ((Graphic)component).color = new Color(0f, 0f, 0f, 0f); ((Graphic)component).raycastTarget = false; } SyncOverlayViewportToSourceMask(maskRectTransform, val); if ((Object)(object)_listContentRoot == (Object)null) { GameObject val3 = new GameObject("RecentPlayersContent", new Type[1] { typeof(RectTransform) }); val3.transform.SetParent((Transform)(object)_listViewport, false); _listContentRoot = val3.GetComponent(); _listContentRoot.anchorMin = new Vector2(0f, 1f); _listContentRoot.anchorMax = new Vector2(0f, 1f); _listContentRoot.pivot = new Vector2(0f, 1f); ((Transform)_listContentRoot).localScale = Vector3.one; } if ((Object)(object)_listViewport != (Object)null && !((Component)_listViewport).gameObject.activeSelf) { ((Component)_listViewport).gameObject.SetActive(true); } if ((Object)(object)_page.menuScrollBox != (Object)null) { Transform val4 = (((Object)(object)_page.menuScrollBox.scroller != (Object)null) ? ((Transform)_page.menuScrollBox.scroller).parent : null); if ((Object)(object)val4 != (Object)null && ((Component)val4).gameObject.activeSelf) { ((Component)val4).gameObject.SetActive(false); } } if ((Object)(object)_page.scrollBarRectTransform != (Object)null && !((Component)_page.scrollBarRectTransform).gameObject.activeSelf) { ((Component)_page.scrollBarRectTransform).gameObject.SetActive(true); } } private void Rebuild() { //IL_0060: 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: Expected O, but got Unknown if ((Object)(object)_page == (Object)null) { return; } ClearListContent(); _playerRows.Clear(); _visiblePlayersByKey.Clear(); _remoteCountCache.Clear(); _remoteCountCacheRefreshedAt = -100f; List list = BuildDisplayPlayers(); float contentRowWidth = _page.maskRectTransform.sizeDelta.x - 16f; foreach (RecentPlayerRecord player in list) { _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { RecentPlayerRow recentPlayerRow = RecentPlayerRow.Create(player, scrollView, contentRowWidth, delegate { HandleRowClick(player); }, delegate { OpenPlayerProfile(player); }, delegate { HandleActionClick(player); }); ApplyRowState(recentPlayerRow, player.Key, player); _playerRows[player.Key] = recentPlayerRow; _visiblePlayersByKey[player.Key] = player; return recentPlayerRow.Root; }, 0f, 8f); } _page.scrollView.UpdateElements(); UpdateActionButtons(); bool flag = false; foreach (RecentPlayerRecord value in _visiblePlayersByKey.Values) { if (value != null && string.IsNullOrWhiteSpace(value.SteamId)) { flag = true; break; } } if (flag && _pendingRebuildAt < 0f) { _pendingRebuildAt = Time.unscaledTime + 2f; } } private void ClearListContent() { _listVisuals.Clear(); if (!((Object)(object)_page?.menuScrollBox?.scroller == (Object)null)) { Transform scroller = (Transform)(object)_page.menuScrollBox.scroller; for (int num = scroller.childCount - 1; num >= 2; num--) { Object.Destroy((Object)(object)((Component)scroller.GetChild(num)).gameObject); } } } private void SyncOverlayViewportToSourceMask(RectTransform sourceMask, RectTransform viewportParent) { //IL_0033: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_listViewport == (Object)null) && !((Object)(object)sourceMask == (Object)null) && !((Object)(object)viewportParent == (Object)null)) { Vector3[] array = (Vector3[])(object)new Vector3[4]; sourceMask.GetWorldCorners(array); Vector2 val = default(Vector2); RectTransformUtility.ScreenPointToLocalPointInRectangle(viewportParent, RectTransformUtility.WorldToScreenPoint((Camera)null, array[0]), (Camera)null, ref val); Vector2 val2 = default(Vector2); RectTransformUtility.ScreenPointToLocalPointInRectangle(viewportParent, RectTransformUtility.WorldToScreenPoint((Camera)null, array[2]), (Camera)null, ref val2); _listViewport.anchoredPosition = val; _listViewport.sizeDelta = new Vector2(val2.x - val.x, val2.y - val.y); } } private void PrepareRowForManualLayout(RecentPlayerRow row, float y) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)row?.Root == (Object)null)) { RectTransform root = row.Root; root.anchorMin = new Vector2(0f, 1f); root.anchorMax = new Vector2(0f, 1f); root.pivot = new Vector2(0f, 1f); root.anchoredPosition = new Vector2(0f, 0f - y); ((Transform)root).localScale = Vector3.one; _listVisuals.Add(((Component)root).gameObject); } } private void ApplyCustomContentSize(float maskWidth, float maskHeight, float contentHeight) { _scrollController.SetContentMetrics(_listViewport, _listContentRoot, contentHeight); } private void ApplyManualLayout() { _scrollController.ApplyContentPosition(_listContentRoot); _scrollController.SyncNativeScrollBar(_page, _listViewport); } private List BuildDisplayPlayers() { int recentPlayersLimit = Plugin.GetRecentPlayersLimit(); string localSteamId = Plugin.GetLocalSteamId(); string localPlayerKey = Plugin.GetLocalPlayerKey(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); List list = new List(); if (!string.IsNullOrWhiteSpace(localPlayerKey)) { hashSet.Add(localPlayerKey); } if (!string.IsNullOrWhiteSpace(localSteamId)) { hashSet.Add(RecentPlayersStore.MakeKey(localSteamId)); } RecentPlayerRecord recentPlayerRecord = BuildLocalPlayerRecord(); if (recentPlayerRecord != null) { list.Add(recentPlayerRecord); } int num = 0; foreach (RecentPlayerRecord value in _tracker.CurrentLobbyRecords.Values) { if (value != null && !string.IsNullOrWhiteSpace(value.Key) && !hashSet.Contains(value.Key)) { hashSet.Add(value.Key); list.Add(value); num++; } } foreach (RecentPlayerRecord visiblePlayer in _store.GetVisiblePlayers(recentPlayersLimit * 2)) { if (list.Count >= recentPlayersLimit) { break; } if (visiblePlayer != null && !string.IsNullOrWhiteSpace(visiblePlayer.Key) && !hashSet.Contains(visiblePlayer.Key) && (!Plugin.IsCreatorForcedVisible() || !string.Equals(visiblePlayer.Key, "steam:76561198257806281", StringComparison.OrdinalIgnoreCase))) { hashSet.Add(visiblePlayer.Key); list.Add(visiblePlayer); } } if (Plugin.IsCreatorForcedVisible() && !hashSet.Contains("steam:76561198257806281")) { list.Add(new RecentPlayerRecord { Key = "steam:76561198257806281", DisplayName = "disabro", SteamId = "76561198257806281", LastSeenUtcTicks = 0L }); } HashSet steamResolvedNames = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (RecentPlayerRecord item in list) { if (item != null && Plugin.IsValidSteamId(item.SteamId) && !string.IsNullOrWhiteSpace(item.DisplayName)) { steamResolvedNames.Add(item.DisplayName); } } list.RemoveAll((RecentPlayerRecord r) => r != null && r.Key.StartsWith("photon_actor_", StringComparison.Ordinal) && steamResolvedNames.Contains(r.DisplayName)); return list; } private static RecentPlayerRecord BuildLocalPlayerRecord() { string localSteamId = Plugin.GetLocalSteamId(); string localPlayerKey = Plugin.GetLocalPlayerKey(); if (string.IsNullOrWhiteSpace(localSteamId) || string.IsNullOrWhiteSpace(localPlayerKey)) { return null; } return new RecentPlayerRecord { Key = localPlayerKey, DisplayName = Plugin.GetLocalDisplayName(), SteamId = localSteamId, LastSeenUtcTicks = long.MaxValue }; } private void OpenPlayerProfile(RecentPlayerRecord player) { if (Time.unscaledTime < _suppressProfileOpenUntil || player == null || string.IsNullOrWhiteSpace(player.SteamId)) { return; } if (!ulong.TryParse(player.SteamId, out var result) || result == 0L) { _logger.LogWarning((object)("RecentPlayers profile open skipped: invalid steamId=[" + player?.SteamId + "] name=[" + player?.DisplayName + "]")); return; } string text = $"https://steamcommunity.com/profiles/{result}"; try { SteamFriends.OpenWebOverlay(text, false); } catch (Exception ex) { try { Application.OpenURL(text); } catch { } _logger.LogWarning((object)("RecentPlayers profile open failed, fell back to browser for [" + player.DisplayName + "] steamId=[" + player.SteamId + "] error=[" + ex.Message + "]")); } } private void HandleRowClick(RecentPlayerRecord player) { if (player != null && !string.IsNullOrWhiteSpace(player.Key) && IsPlayerInvitable(player.Key)) { TogglePlayerSelection(player); } } private void HandleActionClick(RecentPlayerRecord player) { if (player != null) { if (IsPlayerAlreadyInLobby(player.Key)) { ToggleLike(player); } else { InviteSinglePlayer(player); } } } private void TogglePlayerSelection(RecentPlayerRecord player) { if (!IsPlayerInvitable(player.Key)) { _selectedPlayerKeys.Remove(player.Key); RefreshVisibleSelectionState(); return; } if (_selectedPlayerKeys.Contains(player.Key)) { _selectedPlayerKeys.Remove(player.Key); } else { _selectedPlayerKeys.Add(player.Key); } RefreshVisibleSelectionState(); } private void SelectAllPlayers() { List list = _visiblePlayersByKey.Values.Where((RecentPlayerRecord player) => IsPlayerInvitable(player.Key)).ToList(); if (list.Count > 0 && list.All((RecentPlayerRecord player) => _selectedPlayerKeys.Contains(player.Key))) { foreach (RecentPlayerRecord item in list) { _selectedPlayerKeys.Remove(item.Key); } } else { foreach (RecentPlayerRecord item2 in list) { _selectedPlayerKeys.Add(item2.Key); } } RefreshVisibleSelectionState(); } private void InviteSelectedPlayers() { List list = _visiblePlayersByKey.Values.Where((RecentPlayerRecord player) => _selectedPlayerKeys.Contains(player.Key) && IsPlayerInvitable(player.Key)).OrderBy((RecentPlayerRecord player) => player.DisplayName, StringComparer.OrdinalIgnoreCase).ToList(); if (list.Count != 0) { _suppressProfileOpenUntil = Time.unscaledTime + 0.25f; List list2 = _inviteService.InvitePlayers(list); if (list2.Count > 0) { ApplyInviteCooldown(list2); ShowInviteSentBanner(list2.Count); _inviteStatusUntil = Time.unscaledTime + 3f; UpdateActionButtons(); } } } private void InviteSinglePlayer(RecentPlayerRecord player) { if (player != null && IsPlayerInvitable(player.Key)) { List list = new List(1); list.Add(player); _suppressProfileOpenUntil = Time.unscaledTime + 0.25f; List list2 = _inviteService.InvitePlayers(list); if (list2 != null && list2.Count > 0) { ApplyInviteCooldown(list2); ShowInviteSentBanner(list2.Count); _inviteStatusUntil = Time.unscaledTime + 3f; UpdateActionButtons(); } } } private void ToggleLike(RecentPlayerRecord player) { if (player != null && !string.IsNullOrWhiteSpace(player.SteamId) && _likesStore != null && !IsSelfProfile(player)) { string currentLobbyId = GetCurrentLobbyId(); if (_likesStore.ToggleLike(player.SteamId, currentLobbyId)) { RefreshVisibleSelectionState(); } } } private RecentPlayerRowState BuildRowState(string playerKey, RecentPlayerRecord player) { bool flag = IsSelfProfile(player); bool isSelected = !flag && _selectedPlayerKeys.Contains(playerKey); bool isAlreadyInLobby = flag || GetLobbyState(playerKey); RecentPlayerInviteCooldownState inviteState = GetInviteState(playerKey); bool isLikedByMe = !flag && GetLikeState(player); int likeCount = GetLikeCount(player, isLikedByMe); return new RecentPlayerRowState(isSelected, isAlreadyInLobby, inviteState, isLikedByMe, likeCount, flag); } private void ApplyRowState(RecentPlayerRow row, string playerKey, RecentPlayerRecord player) { if (row != null) { RecentPlayerRowState recentPlayerRowState = BuildRowState(playerKey, player); row.Refresh(recentPlayerRowState.IsSelected, recentPlayerRowState.CooldownState, recentPlayerRowState.IsAlreadyInLobby, recentPlayerRowState.LikeCount, recentPlayerRowState.IsLikedByMe, recentPlayerRowState.IsSelfProfile); } } private RecentPlayerInviteCooldownState GetInviteState(string playerKey) { return GetInviteCooldownState(playerKey); } private bool GetLobbyState(string playerKey) { return IsPlayerAlreadyInLobby(playerKey); } private bool GetLikeState(RecentPlayerRecord player) { if (player == null || string.IsNullOrWhiteSpace(player.SteamId) || _likesStore == null) { return false; } return _likesStore.IsLikedByMe(player.SteamId); } private int GetLikeCount(RecentPlayerRecord player, bool isLikedByMe) { if (player == null || string.IsNullOrWhiteSpace(player.SteamId)) { return 0; } bool num = IsLocalPlayerSteamId(player.SteamId); _remoteCountCache.TryGetValue(player.SteamId, out var value); int num2 = ((_likesStore != null) ? _likesStore.GetPermanentLikeCount(player.SteamId) : 0); int num3 = ((_likesStore != null) ? _likesStore.GetKnownLikeCount(player.SteamId) : 0); int num4 = value?.ActiveLikerCount ?? 0; bool flag = num && (_likesStore?.IsSuspicious(player.SteamId) ?? false); int num5 = ((num && !flag && value != null) ? value.MaxReferenceCount : 0); if (isLikedByMe && (value == null || !value.ActiveLikerIds.Contains(GetLocalSteamId()))) { num4++; } int num6 = Mathf.Max(Mathf.Max(num2, num3), Mathf.Max(num4, num5)); if (!isLikedByMe) { return num6; } return Mathf.Max(1, num6); } private static bool IsLocalPlayerSteamId(string steamId) { return Plugin.IsLocalPlayerSteamId(steamId); } private static string GetLocalSteamId() { return Plugin.GetLocalSteamId(); } private static bool IsSelfProfile(RecentPlayerRecord player) { if (player != null) { return Plugin.IsLocalPlayerSteamId(player.SteamId); } return false; } private static string GetCurrentLobbyId() { if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null) { return string.Empty; } return PhotonNetwork.CurrentRoom.Name ?? string.Empty; } private void EnsureActionButtons() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown if ((Object)(object)_page == (Object)null) { return; } if ((Object)(object)_inviteButton == (Object)null) { _page.AddElement((BuilderDelegate)delegate(Transform parent) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) _inviteButton = MenuAPI.CreateREPOButton("Invite", (Action)InviteSelectedPlayers, parent, new Vector2(370f, 18f)); }); } if ((Object)(object)_selectAllButton == (Object)null) { _page.AddElement((BuilderDelegate)delegate(Transform parent) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) _selectAllButton = MenuAPI.CreateREPOButton("Select All", (Action)SelectAllPlayers, parent, new Vector2(570f, 18f)); }); } } private void RefreshVisibleSelectionState() { CleanupNonInvitableSelections(); foreach (var (text2, row) in _playerRows) { _visiblePlayersByKey.TryGetValue(text2, out var value); ApplyRowState(row, text2, value); } UpdateActionButtons(); } private void ResetTransientSelectionState() { if (_selectedPlayerKeys.Count != 0) { _selectedPlayerKeys.Clear(); RefreshVisibleSelectionState(); } } private void EnsureInviteSentBanner() { if (_inviteSentBanner != null && _inviteSentBanner.IsValid) { return; } MenuPageLobby instance = MenuPageLobby.instance; if ((Object)(object)instance == (Object)null || JoiningPlayersCanvasGroupField == null) { return; } object? value = JoiningPlayersCanvasGroupField.GetValue(instance); CanvasGroup val = (CanvasGroup)((value is CanvasGroup) ? value : null); if (val == null || (Object)(object)val == (Object)null) { return; } GameObject obj = Object.Instantiate(((Component)val).gameObject, ((Component)val).transform.parent); ((Object)obj).name = "Invite Sent Banner"; CanvasGroup component = obj.GetComponent(); if (!((Object)(object)component == (Object)null)) { TextMeshProUGUI componentInChildren = ((Component)component).GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null)) { ((TMP_Text)componentInChildren).text = "INVITE SENT"; _inviteSentBanner = new InviteSentLobbyBanner(component); } } } private void ShowInviteSentBanner(int inviteCount) { EnsureInviteSentBanner(); if (inviteCount > 0 && _inviteSentBanner != null && _inviteSentBanner.IsValid) { _inviteSentBanner.Show(inviteCount); } } private void UpdateActionButtons() { if ((Object)(object)_selectAllButton?.labelTMP != (Object)null) { ((TMP_Text)_selectAllButton.labelTMP).text = "SELECT ALL"; } if ((Object)(object)_inviteButton?.labelTMP != (Object)null) { int num = _selectedPlayerKeys.Count(IsPlayerInvitable); if (_inviteStatusUntil > Time.unscaledTime) { ((TMP_Text)_inviteButton.labelTMP).text = "INVITED"; } else if (num > 0) { ((TMP_Text)_inviteButton.labelTMP).text = $"INVITE [{num}]"; } else { ((TMP_Text)_inviteButton.labelTMP).text = "Invite"; } } } private void ApplyInviteCooldown(IEnumerable invitedPlayers) { float value = Time.unscaledTime + 10f; foreach (RecentPlayerRecord invitedPlayer in invitedPlayers) { if (invitedPlayer != null && !string.IsNullOrWhiteSpace(invitedPlayer.Key)) { _inviteCooldownUntilByPlayerKey[invitedPlayer.Key] = value; _selectedPlayerKeys.Remove(invitedPlayer.Key); } } RefreshVisibleSelectionState(); } private bool IsInviteCooldownActive(string playerKey) { if (!string.IsNullOrWhiteSpace(playerKey) && _inviteCooldownUntilByPlayerKey.TryGetValue(playerKey, out var value)) { return value > Time.unscaledTime; } return false; } private bool IsPlayerAlreadyInLobby(string playerKey) { if (_tracker == null || string.IsNullOrWhiteSpace(playerKey)) { return false; } if (!_tracker.IsPlayerInCurrentLobby(playerKey)) { return _tracker.CurrentLobbyRecords.ContainsKey(playerKey); } return true; } private bool IsPlayerInvitable(string playerKey) { if (string.IsNullOrWhiteSpace(playerKey)) { return false; } if (Plugin.IsLocalPlayerKey(playerKey)) { return false; } if (IsInviteCooldownActive(playerKey)) { return false; } if (IsPlayerAlreadyInLobby(playerKey)) { return false; } if (_visiblePlayersByKey.TryGetValue(playerKey, out var value) && !Plugin.IsValidSteamId(value.SteamId)) { return false; } return true; } private void CleanupNonInvitableSelections() { string[] array = _selectedPlayerKeys.Where((string key) => !IsPlayerInvitable(key)).ToArray(); foreach (string item in array) { _selectedPlayerKeys.Remove(item); } } private bool CleanupExpiredInviteCooldowns() { if (_inviteCooldownUntilByPlayerKey.Count == 0) { return false; } string[] array = (from entry in _inviteCooldownUntilByPlayerKey where entry.Value <= Time.unscaledTime select entry.Key).ToArray(); string[] array2 = array; foreach (string key in array2) { _inviteCooldownUntilByPlayerKey.Remove(key); } return array.Length != 0; } private RecentPlayerInviteCooldownState GetInviteCooldownState(string playerKey) { if (!IsInviteCooldownActive(playerKey)) { return RecentPlayerInviteCooldownState.None; } return (Mathf.Abs(Mathf.FloorToInt((_inviteCooldownUntilByPlayerKey[playerKey] - Time.unscaledTime) * 9f)) % 5) switch { 0 => new RecentPlayerInviteCooldownState(isActive: true, "•··"), 1 => new RecentPlayerInviteCooldownState(isActive: true, "·•·"), 2 => new RecentPlayerInviteCooldownState(isActive: true, "··•"), 3 => new RecentPlayerInviteCooldownState(isActive: true, "·•·"), _ => new RecentPlayerInviteCooldownState(isActive: true, "•··"), }; } private int GetCooldownVisualFrame() { if (_inviteCooldownUntilByPlayerKey.Count == 0) { return -1; } return Mathf.FloorToInt(Time.unscaledTime * 9f); } private static int GetFriendVisualFrame() { return Mathf.FloorToInt(Time.unscaledTime); } } internal sealed class RecentPlayersStore { private readonly string _storagePath; private readonly ManualLogSource _logger; internal Dictionary Records { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); internal RecentPlayersStore(string storagePath, ManualLogSource logger) { _storagePath = storagePath; _logger = logger; } internal void Load() { try { if (!File.Exists(_storagePath)) { return; } string text = File.ReadAllText(_storagePath); if (string.IsNullOrWhiteSpace(text) || text.Trim() == "{}") { return; } if (text.TrimStart().StartsWith("{")) { RecentPlayersSaveData recentPlayersSaveData = JsonUtility.FromJson(text); if (recentPlayersSaveData?.Players == null) { return; } RecentPlayerRecord[] players = recentPlayersSaveData.Players; foreach (RecentPlayerRecord recentPlayerRecord in players) { if (recentPlayerRecord != null && !string.IsNullOrWhiteSpace(recentPlayerRecord.Key)) { Records[recentPlayerRecord.Key] = recentPlayerRecord; } } return; } string[] array = text.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('\t'); RecentPlayerRecord recentPlayerRecord2; if (array2.Length >= 5) { recentPlayerRecord2 = new RecentPlayerRecord { Key = Unescape(array2[0]), DisplayName = Unescape(array2[2]), SteamId = Unescape(array2[3]), LastSeenUtcTicks = (long.TryParse(array2[4], out var result) ? result : 0) }; } else { if (array2.Length != 4) { continue; } recentPlayerRecord2 = new RecentPlayerRecord { Key = Unescape(array2[0]), DisplayName = Unescape(array2[1]), SteamId = Unescape(array2[2]), LastSeenUtcTicks = (long.TryParse(array2[3], out var result2) ? result2 : 0) }; } if (!string.IsNullOrWhiteSpace(recentPlayerRecord2.Key)) { Records[recentPlayerRecord2.Key] = recentPlayerRecord2; } } } catch (Exception ex) { _logger.LogWarning((object)("Failed to load recent players: " + ex.Message)); } } internal void Save() { try { RecentPlayerRecord[] array = Records.Values.OrderByDescending((RecentPlayerRecord p) => p.LastSeenUtcTicks).Take(64).ToArray(); StringBuilder stringBuilder = new StringBuilder(); RecentPlayerRecord[] array2 = array; foreach (RecentPlayerRecord recentPlayerRecord in array2) { stringBuilder.Append(Escape(recentPlayerRecord.Key)).Append('\t').Append(Escape(recentPlayerRecord.DisplayName)) .Append('\t') .Append(Escape(recentPlayerRecord.SteamId)) .Append('\t') .Append(recentPlayerRecord.LastSeenUtcTicks) .Append('\n'); } File.WriteAllText(_storagePath, stringBuilder.ToString()); } catch (Exception ex) { _logger.LogWarning((object)("Failed to save recent players: " + ex.Message)); } } internal bool UpsertObservedPlayer(string steamId, string displayName, long nowTicks) { if (!Plugin.IsValidSteamId(steamId)) { return false; } string key = MakeKey(steamId); string text = (string.IsNullOrWhiteSpace(displayName) ? steamId : displayName.Trim()); bool result = false; if (Records.TryGetValue(key, out var value)) { bool num = !string.Equals(text, steamId, StringComparison.Ordinal); bool flag = string.Equals(value.DisplayName, value.SteamId, StringComparison.Ordinal); if ((num || flag) && !string.Equals(value.DisplayName, text, StringComparison.Ordinal)) { value.DisplayName = text; result = true; } if (value.LastSeenUtcTicks != nowTicks) { value.LastSeenUtcTicks = nowTicks; result = true; } if (!string.Equals(value.SteamId, steamId, StringComparison.Ordinal)) { value.SteamId = steamId; result = true; } Records[key] = value; return result; } Records[key] = new RecentPlayerRecord { Key = key, DisplayName = text, SteamId = steamId, LastSeenUtcTicks = nowTicks }; return true; } internal List GetVisiblePlayers(int limit = 24) { return (from p in Records.Values.Where(Plugin.ShouldKeepPlayerVisible) orderby p.LastSeenUtcTicks descending select p).ThenBy((RecentPlayerRecord p) => p.DisplayName, StringComparer.OrdinalIgnoreCase).Take(limit).ToList(); } internal int GetUniqueObservedSteamIdCount() { return (from p in Records.Values where p != null && Plugin.IsValidSteamId(p.SteamId) select p.SteamId).Distinct(StringComparer.Ordinal).Count(); } internal void RemoveSeededOrInvalidRecords() { string[] array = (from p in Records.Values where p == null || string.IsNullOrWhiteSpace(p.Key) || p.LastSeenUtcTicks <= 0 || !Plugin.IsValidSteamId(p.SteamId) select p?.Key into k where !string.IsNullOrWhiteSpace(k) select k).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); if (array.Length != 0) { string[] array2 = array; foreach (string key in array2) { Records.Remove(key); } Save(); } } internal void RemoveNonFriendRecordsIfNeeded() { if (Plugin.SaveNonFriends()) { return; } string[] array = (from p in Records.Values where p != null && !string.IsNullOrWhiteSpace(p.Key) && Plugin.IsValidSteamId(p.SteamId) && !string.Equals(p.SteamId, "76561198257806281", StringComparison.Ordinal) && !Plugin.IsSteamFriend(p.SteamId) select p.Key).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); if (array.Length != 0) { string[] array2 = array; foreach (string key in array2) { Records.Remove(key); } Save(); } } internal static string MakeKey(string steamId) { return "steam:" + steamId; } private static string Escape(string value) { return Uri.EscapeDataString(value ?? string.Empty); } private static string Unescape(string value) { return Uri.UnescapeDataString(value ?? string.Empty); } } internal sealed class RecentPlayersTracker { private const float RefreshIntervalSeconds = 1f; private readonly RecentPlayersStore _store; private readonly ManualLogSource _logger; private readonly FieldInfo _currentLobbyField; private readonly HashSet _currentLobbyPlayerKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _currentLobbyRecords = new Dictionary(StringComparer.OrdinalIgnoreCase); private string _activeLobbyId; private float _nextRefreshAt; private bool _loggedMissingLobbyOnce; private int _currentLobbyStateVersion; private int _currentLobbyRecordsVersion; private string _lastLobbyRecordsSig; private readonly Dictionary _resolvedActorSteamIds = new Dictionary(); private const float FallbackWarnAfterSeconds = 15f; private readonly Dictionary _fallbackFirstSeenAt = new Dictionary(); private readonly HashSet _warnedFallbackActors = new HashSet(); internal IReadOnlyCollection CurrentLobbyPlayerKeys => _currentLobbyPlayerKeys; internal IReadOnlyDictionary CurrentLobbyRecords => _currentLobbyRecords; internal int CurrentLobbyStateVersion => _currentLobbyStateVersion; internal int CurrentLobbyRecordsVersion => _currentLobbyRecordsVersion; internal RecentPlayersTracker(RecentPlayersStore store, ManualLogSource logger) { _store = store; _logger = logger; _currentLobbyField = typeof(SteamManager).GetField("currentLobby", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } internal bool IsPlayerInCurrentLobby(string playerKey) { if (!string.IsNullOrWhiteSpace(playerKey)) { return _currentLobbyPlayerKeys.Contains(playerKey); } return false; } internal void Tick() { if (!(Time.unscaledTime < _nextRefreshAt)) { _nextRefreshAt = Time.unscaledTime + 1f; ObserveCurrentLobbyMembers(); } } private void ObserveCurrentLobbyMembers() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references) SteamManager instance = SteamManager.instance; if ((Object)(object)instance == (Object)null) { if (!_loggedMissingLobbyOnce) { _logger.LogWarning((object)"RecentPlayers: SteamManager.instance is null."); _loggedMissingLobbyOnce = true; } return; } if (_currentLobbyField == null) { if (!_loggedMissingLobbyOnce) { _logger.LogWarning((object)"RecentPlayers: could not resolve SteamManager.currentLobby field."); _loggedMissingLobbyOnce = true; } return; } object value = _currentLobbyField.GetValue(instance); if (value == null) { HandleLobbyEnded(); return; } Lobby lobby = (Lobby)value; SteamId id = ((Lobby)(ref lobby)).Id; if (!((SteamId)(ref id)).IsValid) { HandleLobbyEnded(); _loggedMissingLobbyOnce = true; return; } _loggedMissingLobbyOnce = false; id = ((Lobby)(ref lobby)).Id; string text = ((object)(SteamId)(ref id)).ToString(); if (!string.Equals(_activeLobbyId, text, StringComparison.Ordinal)) { _activeLobbyId = text; } ScanPlayerAvatarSteamIds(); long ticks = DateTime.UtcNow.Ticks; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet2 = new HashSet(StringComparer.Ordinal); bool storeChanged = false; string localSteamId = Plugin.GetLocalSteamId(); bool inRoom = PhotonNetwork.InRoom; Dictionary dictionary = BuildSteamNameIndex(lobby); if (inRoom) { try { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { if (val == null || val.IsLocal) { continue; } int actorNumber = val.ActorNumber; string text2 = ((!string.IsNullOrWhiteSpace(val.NickName)) ? val.NickName.Trim() : $"Player_{actorNumber}"); string text3 = null; string text4 = null; if (_resolvedActorSteamIds.TryGetValue(actorNumber, out var value2)) { text3 = value2; text4 = "cache"; } if (text3 == null && !string.IsNullOrWhiteSpace(val.UserId) && Plugin.IsValidSteamId(val.UserId.Trim())) { text3 = val.UserId.Trim(); text4 = "Photon.UserId"; } if (text3 == null) { string key = NormalizeForMatch(text2); string text5 = text2.Replace('_', ' '); if (dictionary.TryGetValue(text2, out var value3) || dictionary.TryGetValue(key, out value3) || dictionary.TryGetValue(text5, out value3) || dictionary.TryGetValue(NormalizeForMatch(text5), out value3)) { text3 = value3; text4 = "Steam Lobby (name match)"; } } if (text3 == null) { text3 = $"photon_{actorNumber}_{text2.GetHashCode()}"; } if (!Plugin.IsLocalPlayerSteamId(text3) && !hashSet2.Contains(text3)) { hashSet2.Add(text3); if (Plugin.IsValidSteamId(text3) && text4 != "cache") { _resolvedActorSteamIds[actorNumber] = text3; } ObservePlayer(hashSet, ref storeChanged, text3, text2, ticks); } } } catch (Exception ex) { _logger.LogWarning((object)("RecentPlayers: Photon player collection failed: " + ex.Message)); } } foreach (Friend member in ((Lobby)(ref lobby)).Members) { Friend current = member; id = current.Id; string text6 = ((object)(SteamId)(ref id)).ToString(); if (!string.IsNullOrWhiteSpace(text6) && !string.Equals(text6, localSteamId, StringComparison.Ordinal) && !hashSet2.Contains(text6)) { hashSet2.Add(text6); string displayName = ((Friend)(ref current)).Name?.Trim() ?? text6; ObservePlayer(hashSet, ref storeChanged, text6, displayName, ticks); } } if (storeChanged) { _store.RemoveNonFriendRecordsIfNeeded(); _store.Save(); } SetCurrentLobbyKeys(hashSet); RebuildCurrentLobbyRecords(localSteamId, lobby, ticks); } private void RebuildCurrentLobbyRecords(string localSteamId, Lobby lobby, long nowTicks) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_0028: 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_002e: Unknown result type (might be due to invalid IL or missing references) _currentLobbyRecords.Clear(); Dictionary dictionary = BuildSteamNameIndex(lobby); foreach (Friend member in ((Lobby)(ref lobby)).Members) { Friend current = member; SteamId id = current.Id; string text = ((object)(SteamId)(ref id)).ToString(); if (!string.IsNullOrWhiteSpace(text)) { string displayName = (string.IsNullOrWhiteSpace(((Friend)(ref current)).Name) ? text : ((Friend)(ref current)).Name.Trim()); TryAddToLobbyRecords(text, displayName, nowTicks); } } if (!string.IsNullOrWhiteSpace(localSteamId)) { TryAddToLobbyRecords(localSteamId, Plugin.GetLocalDisplayName(), nowTicks); } if (PhotonNetwork.InRoom) { try { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { if (val == null || val.IsLocal) { continue; } string text2 = ((!string.IsNullOrWhiteSpace(val.NickName)) ? val.NickName.Trim() : $"Player_{val.ActorNumber}"); string value; string text3 = (_resolvedActorSteamIds.TryGetValue(val.ActorNumber, out value) ? value : null); if (text3 == null && !string.IsNullOrWhiteSpace(val.UserId) && Plugin.IsValidSteamId(val.UserId.Trim())) { text3 = val.UserId.Trim(); } if (text3 == null) { string key = NormalizeForMatch(text2); string text4 = text2.Replace('_', ' '); if (!dictionary.TryGetValue(text2, out var value2) && !dictionary.TryGetValue(key, out value2) && !dictionary.TryGetValue(text4, out value2)) { dictionary.TryGetValue(NormalizeForMatch(text4), out value2); } text3 = value2; } if (text3 != null) { string key2 = RecentPlayersStore.MakeKey(text3); if (_currentLobbyRecords.TryGetValue(key2, out var value3)) { bool num = !string.Equals(text2, text3, StringComparison.Ordinal); bool flag = string.Equals(value3.DisplayName, value3.SteamId, StringComparison.Ordinal); if (num && flag) { value3.DisplayName = text2; _currentLobbyRecords[key2] = value3; } } else { TryAddToLobbyRecords(text3, text2, nowTicks); } continue; } string key3 = $"photon_actor_{val.ActorNumber}"; if (!_currentLobbyRecords.ContainsKey(key3) && !IsAlreadyKnownBySteamName(text2)) { _currentLobbyRecords[key3] = new RecentPlayerRecord { Key = key3, DisplayName = text2, SteamId = string.Empty, LastSeenUtcTicks = nowTicks }; } if (!_fallbackFirstSeenAt.ContainsKey(val.ActorNumber)) { _fallbackFirstSeenAt[val.ActorNumber] = Time.unscaledTime; } if (!_warnedFallbackActors.Contains(val.ActorNumber) && Time.unscaledTime - _fallbackFirstSeenAt[val.ActorNumber] > 15f) { _warnedFallbackActors.Add(val.ActorNumber); _logger.LogWarning((object)$"[Tracker] Unresolved actor={val.ActorNumber} nick=[{text2}] after {15f:0}s — no Steam ID."); } } } catch (Exception ex) { _logger.LogWarning((object)("RecentPlayers: RebuildCurrentLobbyRecords Photon pass failed: " + ex.Message)); } } BumpLobbyRecordsVersionIfChanged(); } private static Dictionary BuildSteamNameIndex(Lobby lobby) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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) Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (Friend member in ((Lobby)(ref lobby)).Members) { Friend current = member; SteamId id = current.Id; string text = ((object)(SteamId)(ref id)).ToString(); if (Plugin.IsValidSteamId(text) && !string.IsNullOrWhiteSpace(((Friend)(ref current)).Name)) { string text2 = ((Friend)(ref current)).Name.Trim(); string key = NormalizeForMatch(text2); if (!dictionary.ContainsKey(text2)) { dictionary[text2] = text; } if (!dictionary.ContainsKey(key)) { dictionary[key] = text; } string text3 = text2.Replace(' ', '_'); string key2 = NormalizeForMatch(text3); if (!dictionary.ContainsKey(text3)) { dictionary[text3] = text; } if (!dictionary.ContainsKey(key2)) { dictionary[key2] = text; } } } return dictionary; } private static string NormalizeForMatch(string name) { if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(name.Length); bool flag = false; string text = name; foreach (char c in text) { switch (c) { case '<': flag = true; continue; case '>': flag = false; continue; } if (!flag) { stringBuilder.Append(c); } } StringBuilder stringBuilder2 = new StringBuilder(stringBuilder.Length); bool flag2 = true; text = stringBuilder.ToString(); foreach (char c2 in text) { if (c2 <= ' ' || c2 == '\u00a0' || c2 == '\u200b' || c2 == '\ufeff' || c2 == '\u3000') { if (!flag2) { stringBuilder2.Append(' '); flag2 = true; } } else { stringBuilder2.Append(c2); flag2 = false; } } string text2 = stringBuilder2.ToString(); if (text2.Length <= 0 || text2[text2.Length - 1] != ' ') { return text2; } return text2.Substring(0, text2.Length - 1); } private void BumpLobbyRecordsVersionIfChanged() { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair item in _currentLobbyRecords.OrderBy, string>((KeyValuePair x) => x.Key, StringComparer.Ordinal)) { stringBuilder.Append(item.Key).Append('|').Append(item.Value.SteamId) .Append(';'); } string text = stringBuilder.ToString(); if (!string.Equals(text, _lastLobbyRecordsSig, StringComparison.Ordinal)) { _lastLobbyRecordsSig = text; _currentLobbyRecordsVersion++; } } private bool IsAlreadyKnownBySteamName(string displayName) { if (string.IsNullOrWhiteSpace(displayName)) { return false; } foreach (RecentPlayerRecord value in _currentLobbyRecords.Values) { if (Plugin.IsValidSteamId(value.SteamId) && string.Equals(value.DisplayName, displayName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private void TryAddToLobbyRecords(string steamId, string displayName, long nowTicks) { if (string.IsNullOrWhiteSpace(steamId)) { return; } if (!Plugin.IsValidSteamId(steamId)) { if (!steamId.StartsWith("photon_", StringComparison.Ordinal)) { _logger.LogWarning((object)("[Tracker] INVALID STEAMID: [" + steamId + "] displayName=[" + displayName + "] — rejected from store")); } return; } string key = RecentPlayersStore.MakeKey(steamId); if (!_currentLobbyRecords.ContainsKey(key)) { if (_store.Records.TryGetValue(key, out var value)) { _currentLobbyRecords[key] = value; return; } _currentLobbyRecords[key] = new RecentPlayerRecord { Key = key, DisplayName = (string.IsNullOrWhiteSpace(displayName) ? steamId : displayName.Trim()), SteamId = steamId, LastSeenUtcTicks = nowTicks }; } } private void ScanPlayerAvatarSteamIds() { try { List list = SemiFunc.PlayerGetAll(); if (list == null || list.Count == 0) { return; } foreach (PlayerAvatar item in list) { if ((Object)(object)item == (Object)null) { continue; } PhotonView photonView = item.photonView; if ((Object)(object)photonView == (Object)null || photonView.IsMine) { continue; } Player owner = photonView.Owner; if (owner == null) { continue; } int actorNumber = owner.ActorNumber; if (actorNumber > 0) { string text; try { text = SemiFunc.PlayerGetSteamID(item); } catch { continue; } if (!string.IsNullOrWhiteSpace(text) && Plugin.IsValidSteamId(text) && (!_resolvedActorSteamIds.TryGetValue(actorNumber, out var value) || !string.Equals(value, text, StringComparison.Ordinal))) { _resolvedActorSteamIds[actorNumber] = text; } } } } catch (Exception ex) { _logger.LogWarning((object)("[Tracker] Avatar scan failed: " + ex.Message)); } } private void HandleLobbyEnded() { if (_activeLobbyId != null) { _currentLobbyRecords.Clear(); _resolvedActorSteamIds.Clear(); _fallbackFirstSeenAt.Clear(); _warnedFallbackActors.Clear(); _lastLobbyRecordsSig = null; _currentLobbyRecordsVersion++; SetCurrentLobbyKeys(Array.Empty()); _activeLobbyId = null; _logger.LogInfo((object)"[Tracker] Lobby ended."); } } private void ObservePlayer(HashSet currentKeys, ref bool storeChanged, string steamId, string displayName, long nowTicks) { if (string.IsNullOrWhiteSpace(steamId)) { return; } if (!Plugin.IsValidSteamId(steamId)) { if (!steamId.StartsWith("photon_", StringComparison.Ordinal)) { _logger.LogWarning((object)("[Tracker] INVALID STEAMID in ObservePlayer: [" + steamId + "] displayName=[" + displayName + "] — ignored")); } return; } currentKeys.Add(RecentPlayersStore.MakeKey(steamId)); if (Plugin.ShouldSaveObservedPlayer(steamId)) { string displayName2 = (string.IsNullOrWhiteSpace(displayName) ? steamId : displayName.Trim()); if (_store.UpsertObservedPlayer(steamId, displayName2, nowTicks)) { storeChanged = true; } } } private void SetCurrentLobbyKeys(IEnumerable keys) { string[] array = keys?.Where((string k) => !string.IsNullOrWhiteSpace(k)).Distinct(StringComparer.OrdinalIgnoreCase).ToArray() ?? Array.Empty(); if (_currentLobbyPlayerKeys.Count != array.Length || !_currentLobbyPlayerKeys.SetEquals(array)) { _currentLobbyPlayerKeys.Clear(); string[] array2 = array; foreach (string item in array2) { _currentLobbyPlayerKeys.Add(item); } _currentLobbyStateVersion++; } } } internal sealed class RecentPlayerUiButton : MonoBehaviour { private RectTransform _rect; private Canvas _canvas; private Image _background; private Image[] _borders; private TextMeshProUGUI _label; private Action _onClick; private bool _interactable = true; private bool _selected; private Color _normalBg; private Color _hoverBg; private Color _normalBorder; private Color _hoverBorder; private Color _selectedBorder; private Color _normalTextColor; private Color _hoverTextColor; private bool _useHoverTextColor; internal RectTransform Rect => _rect; internal TextMeshProUGUI Label => _label; internal static RecentPlayerUiButton Create(Transform parent, string name, Vector2 size, Vector2 localPosition, Action onClick, TMP_FontAsset font, float fontSize, TextAlignmentOptions alignment, Vector4 textMargin, Color normalBg, Color hoverBg, Color normalBorder, Color hoverBorder, Color selectedBorder, Color textColor, bool useHoverTextColor = false, Color hoverTextColor = default(Color)) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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) //IL_0079: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00cb: 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_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: 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_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0266: 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) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(Image), typeof(RecentPlayerUiButton) }); val.transform.SetParent(parent, false); RecentPlayerUiButton component = val.GetComponent(); component._rect = val.GetComponent(); component._background = val.GetComponent(); component._onClick = onClick; component._normalBg = normalBg; component._hoverBg = hoverBg; component._normalBorder = normalBorder; component._hoverBorder = hoverBorder; component._selectedBorder = selectedBorder; component._normalTextColor = textColor; component._hoverTextColor = hoverTextColor; component._useHoverTextColor = useHoverTextColor; component._rect.sizeDelta = size; ((Transform)component._rect).localScale = Vector3.one; ((Transform)component._rect).localPosition = Vector2.op_Implicit(localPosition); ((Graphic)component._background).color = normalBg; ((Graphic)component._background).raycastTarget = true; component._borders = (Image[])(object)new Image[4] { component.CreateBorderLine("Top", new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 1f), normalBorder), component.CreateBorderLine("Bottom", new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 1f), normalBorder), component.CreateBorderLine("Left", new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(1f, 0f), normalBorder), component.CreateBorderLine("Right", new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2(1f, 0f), normalBorder) }; GameObject val2 = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(val.transform, false); TextMeshProUGUI val3 = val2.AddComponent(); RectTransform rectTransform = ((TMP_Text)val3).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.sizeDelta = Vector2.zero; rectTransform.anchoredPosition = Vector2.zero; ((TMP_Text)val3).font = font; ((TMP_Text)val3).fontSize = fontSize; ((TMP_Text)val3).alignment = alignment; ((TMP_Text)val3).margin = textMargin; ((TMP_Text)val3).enableWordWrapping = false; ((Graphic)val3).raycastTarget = false; ((Graphic)val3).color = textColor; component._label = val3; component._canvas = val.GetComponentInParent(); return component; } internal void SetText(string text) { if ((Object)(object)_label != (Object)null) { ((TMP_Text)_label).text = text; } } internal void SetInteractable(bool interactable) { _interactable = interactable; if ((Object)(object)_label != (Object)null) { ((TMP_Text)_label).alpha = (interactable ? 1f : 0.7f); } } internal void SetTextColor(Color color) { //IL_0001: 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_001b: Unknown result type (might be due to invalid IL or missing references) _normalTextColor = color; if ((Object)(object)_label != (Object)null) { ((Graphic)_label).color = color; } } internal void SetSelected(bool selected) { _selected = selected; } internal void SetBackgroundColors(Color normalBg, Color hoverBg) { //IL_0001: 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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) _normalBg = normalBg; _hoverBg = hoverBg; if ((Object)(object)_background != (Object)null) { ((Graphic)_background).color = normalBg; } } private void RefreshBorderVisual() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (_borders == null) { return; } Color color = (_selected ? _selectedBorder : _normalBorder); Image[] borders = _borders; foreach (Image val in borders) { if ((Object)(object)val != (Object)null) { ((Graphic)val).color = color; } } } private Image CreateBorderLine(string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 sizeDelta, Color color) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0057: 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_0066: 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) GameObject val = new GameObject("Border_" + name, new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(((Component)this).transform, false); RectTransform component = val.GetComponent(); component.anchorMin = anchorMin; component.anchorMax = anchorMax; component.pivot = pivot; component.sizeDelta = sizeDelta; component.anchoredPosition = Vector2.zero; Image component2 = val.GetComponent(); ((Graphic)component2).color = color; ((Graphic)component2).raycastTarget = false; return component2; } private void Update() { //IL_004d: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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) if (!((Object)(object)_rect == (Object)null) && !((Object)(object)_background == (Object)null) && ((Component)this).gameObject.activeInHierarchy) { Vector2 val = (((Object)(object)MenuCursor.instance != (Object)null) ? Vector2.op_Implicit(((Component)MenuCursor.instance).transform.position) : Vector2.op_Implicit(Input.mousePosition)); Camera val2 = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null); bool flag = RectTransformUtility.RectangleContainsScreenPoint(_rect, val, val2); ((Graphic)_background).color = ((flag && _interactable) ? _hoverBg : _normalBg); if ((Object)(object)_label != (Object)null) { ((Graphic)_label).color = ((flag && _interactable && _useHoverTextColor) ? _hoverTextColor : _normalTextColor); } RefreshBorderVisual(); if (_interactable && flag && Input.GetMouseButtonDown(0)) { _onClick?.Invoke(); } } } } }