using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ComputerysModdingUtilities; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: StraftatMod(true)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LobbyRanks { [BepInPlugin("landa.straftat.lobbyranks", "Lobby Ranks", "1.0.1")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry ShowScore; internal static ConfigEntry HighlightSelf; internal static ConfigEntry PanelX; internal static ConfigEntry PanelY; internal static ConfigEntry Scale; internal static ConfigEntry ToggleKey; internal static ConfigEntry ApiNameOverride; private void Awake() { //IL_0085: 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_00d1: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //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_0184: Expected O, but got Unknown //IL_0184: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Display", "Enabled", true, "Master toggle for the lobby rank panel."); ShowScore = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Display", "ShowScore", true, "Show each player's raw leaderboard score (gamesWon*4 + roundsWon) beside their global rank."); HighlightSelf = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Display", "HighlightYou", true, "Tint your own row so you can spot it at a glance."); ToggleKey = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Display", "ToggleKey", new KeyboardShortcut((KeyCode)291, Array.Empty()), "Hotkey to show/hide the panel."); PanelX = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Panel", "CenterX", 0.5f, new ConfigDescription("Panel horizontal center as a fraction of screen width.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); PanelY = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Panel", "TopY", 0.1f, new ConfigDescription("Panel top edge as a fraction of screen height.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.9f), Array.Empty())); Scale = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Panel", "Scale", 1f, new ConfigDescription("Panel scale.", (AcceptableValueBase)(object)new AcceptableValueRange(0.6f, 2f), Array.Empty())); ApiNameOverride = ((BaseUnityPlugin)this).Config.Bind("LobbyRanks.Advanced", "LeaderboardApiName", "", "Leave blank to auto-detect the game's leaderboard from its Heathen LeaderboardObject. Only set this if auto-detection fails - the detected name is logged once at startup."); GameObject val = new GameObject("LobbyRanks"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Lobby Ranks loaded."); } } internal struct PlayerRow { public ulong SteamId; public string Name; public bool IsLocal; public int Rank; public int Score; public bool Ranked; } internal static class Ranks { private enum State { Idle, Finding, Ready, Failed } private static State _state = State.Idle; private static SteamLeaderboard_t _board; private static string _apiName; private static float _retryAt = -999f; private static CallResult _findCall; private static CallResult _downloadCall; private static readonly Dictionary _cache = new Dictionary(); private static HashSet _lastRequested = new HashSet(); private static bool _requestInFlight; private static float _lastRequestAt = -999f; private const float MinRequestInterval = 4f; internal static bool BoardReady => _state == State.Ready; internal static bool BoardFailed => _state == State.Failed; internal static string ApiName => _apiName; internal static void EnsureBoard(float now) { //IL_0077: 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) if (_state == State.Finding || _state == State.Ready || (_state == State.Failed && now < _retryAt)) { return; } string text = ResolveApiName(); if (string.IsNullOrEmpty(text)) { return; } try { if (_findCall == null) { _findCall = CallResult.Create((APIDispatchDelegate)OnFind); } _apiName = text; _state = State.Finding; _retryAt = now + 15f; SteamAPICall_t val = SteamUserStats.FindLeaderboard(text); _findCall.Set(val, (APIDispatchDelegate)null); Plugin.Log.LogInfo((object)("[LobbyRanks] Looking up leaderboard '" + text + "'...")); } catch (Exception ex) { _state = State.Idle; Plugin.Log.LogWarning((object)("[LobbyRanks] FindLeaderboard deferred: " + ex.Message)); } } private static void OnFind(LeaderboardFindResult_t result, bool ioFailure) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (ioFailure || result.m_bLeaderboardFound == 0) { _state = State.Failed; Plugin.Log.LogWarning((object)$"[LobbyRanks] Leaderboard '{_apiName}' not found (ioFailure={ioFailure})."); } else { _board = result.m_hSteamLeaderboard; _state = State.Ready; Plugin.Log.LogInfo((object)("[LobbyRanks] Leaderboard '" + _apiName + "' resolved.")); } } internal static void Refresh(IReadOnlyList roster, float now) { //IL_00a5: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if (_state != State.Ready || _requestInFlight || roster.Count == 0) { return; } HashSet hashSet = new HashSet(roster); if ((hashSet.SetEquals(_lastRequested) && now - _lastRequestAt < 30f) || now - _lastRequestAt < 4f) { return; } try { if (_downloadCall == null) { _downloadCall = CallResult.Create((APIDispatchDelegate)OnDownload); } CSteamID[] array = ((IEnumerable)roster).Select((Func)((ulong id) => new CSteamID(id))).ToArray(); SteamAPICall_t val = SteamUserStats.DownloadLeaderboardEntriesForUsers(_board, array, array.Length); _downloadCall.Set(val, (APIDispatchDelegate)null); _requestInFlight = true; _lastRequested = hashSet; _lastRequestAt = now; } catch (Exception ex) { _requestInFlight = false; Plugin.Log.LogWarning((object)("[LobbyRanks] Download request failed: " + ex.Message)); } } private static void OnDownload(LeaderboardScoresDownloaded_t result, bool ioFailure) { //IL_008d: 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_0035: 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_0061: 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) _requestInFlight = false; if (ioFailure) { Plugin.Log.LogWarning((object)"[LobbyRanks] Leaderboard download failed (ioFailure)."); return; } HashSet hashSet = new HashSet(); LeaderboardEntry_t val = default(LeaderboardEntry_t); for (int i = 0; i < result.m_cEntryCount; i++) { if (SteamUserStats.GetDownloadedLeaderboardEntry(result.m_hSteamLeaderboardEntries, i, ref val, (int[])null, 0)) { ulong steamID = val.m_steamIDUser.m_SteamID; hashSet.Add(steamID); _cache[steamID] = new PlayerRow { SteamId = steamID, Rank = val.m_nGlobalRank, Score = val.m_nScore, Ranked = true }; } } foreach (ulong item in _lastRequested) { if (!hashSet.Contains(item)) { _cache[item] = new PlayerRow { SteamId = item, Rank = 0, Score = 0, Ranked = false }; } } } internal static bool TryGet(ulong id, out PlayerRow row) { return _cache.TryGetValue(id, out row); } private static string ResolveApiName() { string value = Plugin.ApiNameOverride.Value; if (!string.IsNullOrEmpty(value)) { return value; } Type type = AccessTools.TypeByName("HeathenEngineering.SteamworksIntegration.LeaderboardObject"); if (type == null) { return null; } FieldInfo fieldInfo = AccessTools.Field(type, "apiName"); if (fieldInfo == null) { return null; } Object[] array = Resources.FindObjectsOfTypeAll(type); foreach (Object obj in array) { if (fieldInfo.GetValue(obj) is string text && !string.IsNullOrEmpty(text)) { return text; } } return null; } } internal class LobbyRanksBehaviour : MonoBehaviour { private static FieldInfo _instancesField; private static FieldInfo _steamIdField; private static FieldInfo _nameField; private bool _reflectionReady; private bool _reflectionFailed; private ulong _localSteamId; private float _nextRosterPoll; private bool _hidden; private readonly List _roster = new List(); private bool _stylesBuilt; private GUIStyle _title; private GUIStyle _rank; private GUIStyle _name; private GUIStyle _score; private GUIStyle _muted; private Texture2D _bgTex; private Texture2D _selfTex; private void Update() { //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) KeyboardShortcut value = Plugin.ToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { _hidden = !_hidden; } if (!ResolveReflection() || Time.unscaledTime < _nextRosterPoll) { return; } _nextRosterPoll = Time.unscaledTime + 1f; if (!Ranks.BoardReady) { Ranks.EnsureBoard(Time.unscaledTime); } PollRoster(); if (_roster.Count > 0) { Ranks.Refresh(_roster.Select((PlayerRow r) => r.SteamId).ToList(), Time.unscaledTime); } } private void PollRoster() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) _roster.Clear(); if (_localSteamId == 0L) { try { _localSteamId = (ulong)SteamUser.GetSteamID(); } catch { } } if (!(_instancesField.GetValue(null) is IDictionary dictionary)) { return; } foreach (object value in dictionary.Values) { if (value == null) { continue; } ulong num = (ulong)_steamIdField.GetValue(value); if (num != 0L) { string name = (_nameField.GetValue(value) as string) ?? "?"; PlayerRow item = new PlayerRow { SteamId = num, Name = name, IsLocal = (num == _localSteamId) }; if (Ranks.TryGet(num, out var row)) { item.Rank = row.Rank; item.Score = row.Score; item.Ranked = row.Ranked; } _roster.Add(item); } } } private bool ResolveReflection() { if (_reflectionReady) { return true; } if (_reflectionFailed) { return false; } Type type = AccessTools.TypeByName("ClientInstance"); if (type == null) { return false; } _instancesField = AccessTools.Field(type, "playerInstances"); _steamIdField = AccessTools.Field(type, "PlayerSteamID"); _nameField = AccessTools.Field(type, "PlayerName"); if (_instancesField == null || _steamIdField == null || _nameField == null) { _reflectionFailed = true; Plugin.Log.LogError((object)"[LobbyRanks] Could not resolve ClientInstance fields; mod disabled."); return false; } _reflectionReady = true; return true; } private void OnGUI() { //IL_0106: 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_0182: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled.Value || _hidden || _roster.Count == 0) { return; } BuildStyles(); float value = Plugin.Scale.Value; float num = 26f * value; float num2 = 10f * value; float num3 = 26f * value; float num4 = 300f * value; List list = (from r in _roster orderby (!r.Ranked) ? 1 : 0, (!r.Ranked) ? int.MaxValue : r.Rank select r).ToList(); float num5 = num3 + num2 + (float)list.Count * num + num2; float num6 = Mathf.Clamp(Plugin.PanelX.Value * (float)Screen.width - num4 / 2f, 0f, (float)Screen.width - num4); float num7 = Plugin.PanelY.Value * (float)Screen.height; GUI.DrawTexture(new Rect(num6, num7, num4, num5), (Texture)(object)_bgTex); GUI.Label(new Rect(num6, num7 + 4f * value, num4, num3), "LOBBY RANKS", _title); float num8 = num7 + num3 + num2 * 0.5f; Rect val = default(Rect); foreach (PlayerRow item in list) { ((Rect)(ref val))..ctor(num6, num8, num4, num); if (item.IsLocal && Plugin.HighlightSelf.Value) { GUI.DrawTexture(val, (Texture)(object)_selfTex); } float num9 = num6 + 12f * value; if (item.Ranked && item.Rank > 0) { Rect val2 = new Rect(num9, num8, 70f * value, num); int rank = item.Rank; GUI.Label(val2, "#" + rank, _rank); } else if (Ranks.BoardReady) { GUI.Label(new Rect(num9, num8, 70f * value, num), "—", _muted); } else { GUI.Label(new Rect(num9, num8, 70f * value, num), "…", _muted); } string text = item.Name + (item.IsLocal ? " (you)" : ""); GUI.Label(new Rect(num9 + 74f * value, num8, num4 - 150f * value, num), text, _name); string text2; if (item.Ranked) { object obj; if (!Plugin.ShowScore.Value) { obj = ""; } else { int rank = item.Score; obj = rank.ToString("N0"); } text2 = (string)obj; } else { text2 = ((!Ranks.BoardReady) ? "" : "unranked"); } GUI.Label(new Rect(num6, num8, num4 - 12f * value, num), text2, _score); num8 += num; } } private void BuildStyles() { //IL_0025: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_00ac: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Expected O, but got Unknown //IL_01dc: Unknown result type (might be due to invalid IL or missing references) if (!_stylesBuilt) { _stylesBuilt = true; _bgTex = SolidTex(new Color(0.05f, 0.06f, 0.08f, 0.86f)); _selfTex = SolidTex(new Color(0.2f, 0.55f, 0.3f, 0.3f)); int fontSize = Mathf.RoundToInt(16f * Plugin.Scale.Value); _title = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontStyle = (FontStyle)1, fontSize = Mathf.RoundToInt(15f * Plugin.Scale.Value) }; _title.normal.textColor = new Color(0.96f, 0.93f, 0.82f); _rank = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)3, fontStyle = (FontStyle)1, fontSize = fontSize }; _rank.normal.textColor = new Color(1f, 0.84f, 0.3f); _name = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)3, fontSize = fontSize }; _name.normal.textColor = Color.white; _score = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)5, fontSize = Mathf.RoundToInt(14f * Plugin.Scale.Value) }; _score.normal.textColor = new Color(0.78f, 0.82f, 0.88f); _muted = new GUIStyle(_rank); _muted.normal.textColor = new Color(0.55f, 0.58f, 0.62f); } } private static Texture2D SolidTex(Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, c); val.Apply(); ((Object)val).hideFlags = (HideFlags)61; return val; } } }