Decompiled source of LobbyRanks v1.0.1

LobbyRanks/LobbyRanks.dll

Decompiled 3 days ago
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<bool> Enabled;

		internal static ConfigEntry<bool> ShowScore;

		internal static ConfigEntry<bool> HighlightSelf;

		internal static ConfigEntry<float> PanelX;

		internal static ConfigEntry<float> PanelY;

		internal static ConfigEntry<float> Scale;

		internal static ConfigEntry<KeyboardShortcut> ToggleKey;

		internal static ConfigEntry<string> 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<bool>("LobbyRanks.Display", "Enabled", true, "Master toggle for the lobby rank panel.");
			ShowScore = ((BaseUnityPlugin)this).Config.Bind<bool>("LobbyRanks.Display", "ShowScore", true, "Show each player's raw leaderboard score (gamesWon*4 + roundsWon) beside their global rank.");
			HighlightSelf = ((BaseUnityPlugin)this).Config.Bind<bool>("LobbyRanks.Display", "HighlightYou", true, "Tint your own row so you can spot it at a glance.");
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("LobbyRanks.Display", "ToggleKey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Hotkey to show/hide the panel.");
			PanelX = ((BaseUnityPlugin)this).Config.Bind<float>("LobbyRanks.Panel", "CenterX", 0.5f, new ConfigDescription("Panel horizontal center as a fraction of screen width.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			PanelY = ((BaseUnityPlugin)this).Config.Bind<float>("LobbyRanks.Panel", "TopY", 0.1f, new ConfigDescription("Panel top edge as a fraction of screen height.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f), Array.Empty<object>()));
			Scale = ((BaseUnityPlugin)this).Config.Bind<float>("LobbyRanks.Panel", "Scale", 1f, new ConfigDescription("Panel scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 2f), Array.Empty<object>()));
			ApiNameOverride = ((BaseUnityPlugin)this).Config.Bind<string>("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<LobbyRanksBehaviour>();
			((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<LeaderboardFindResult_t> _findCall;

		private static CallResult<LeaderboardScoresDownloaded_t> _downloadCall;

		private static readonly Dictionary<ulong, PlayerRow> _cache = new Dictionary<ulong, PlayerRow>();

		private static HashSet<ulong> _lastRequested = new HashSet<ulong>();

		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<LeaderboardFindResult_t>.Create((APIDispatchDelegate<LeaderboardFindResult_t>)OnFind);
				}
				_apiName = text;
				_state = State.Finding;
				_retryAt = now + 15f;
				SteamAPICall_t val = SteamUserStats.FindLeaderboard(text);
				_findCall.Set(val, (APIDispatchDelegate<LeaderboardFindResult_t>)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<ulong> 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<ulong> hashSet = new HashSet<ulong>(roster);
			if ((hashSet.SetEquals(_lastRequested) && now - _lastRequestAt < 30f) || now - _lastRequestAt < 4f)
			{
				return;
			}
			try
			{
				if (_downloadCall == null)
				{
					_downloadCall = CallResult<LeaderboardScoresDownloaded_t>.Create((APIDispatchDelegate<LeaderboardScoresDownloaded_t>)OnDownload);
				}
				CSteamID[] array = ((IEnumerable<ulong>)roster).Select((Func<ulong, CSteamID>)((ulong id) => new CSteamID(id))).ToArray();
				SteamAPICall_t val = SteamUserStats.DownloadLeaderboardEntriesForUsers(_board, array, array.Length);
				_downloadCall.Set(val, (APIDispatchDelegate<LeaderboardScoresDownloaded_t>)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<ulong> hashSet = new HashSet<ulong>();
			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<PlayerRow> _roster = new List<PlayerRow>();

		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<PlayerRow> 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;
		}
	}
}