Due to update 2.4.3, some mods may no longer function. FixedConfig may be necessary.
Decompiled source of BoplPlayerHistory v1.5.0
BoplPlayerHistory.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using Steamworks; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [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("BoplPlayerHistory")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BoplPlayerHistory")] [assembly: AssemblyTitle("BoplPlayerHistory")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } [Serializable] public sealed class PlayerDatabase { public List<PlayerRecord> players = new List<PlayerRecord>(); } [Serializable] public sealed class PlayerRecord { public string steamId; public string nickname; public long lastMetUnixSeconds; } [BepInPlugin("com.creed.bopl.playerhistory", "Bopl Player History", "1.3.1-DEBUG")] public sealed class BoplPlayerHistoryPlugin : BaseUnityPlugin { private const float ScanInterval = 2f; private const float SaveInterval = 15f; private const int MinimumRememberedPlayers = 1; private const int MaximumRememberedPlayers = 1000; private const int WindowId = 741205; private const float MinWindowWidth = 380f; private const float MinWindowHeight = 280f; private const float ResizeHandleSize = 20f; private readonly PlayerDatabase _database = new PlayerDatabase(); private readonly Dictionary<string, string> _lastMetSnapshot = new Dictionary<string, string>(); private ConfigEntry<int> _rememberLimit; private ConfigEntry<bool> _openOnStartup; private GUIStyle _creditStyle; private float _nextScan; private float _nextSave; private bool _dirty; private bool _isOpen; private bool _confirmClear; private bool _isResizing; private Vector2 _resizeStartMouse; private Vector2 _resizeStartSize; private Rect _windowRect; private string _expandedSteamId = string.Empty; private string _searchQuery = string.Empty; private bool _sortByName; private Vector2 _scrollPosition; private int _creditClicks; private string _easterEggMessage = "Made By UxCreed"; private string DatabasePath => Path.Combine(Application.persistentDataPath, "BoplPlayerHistory", "players.txt"); private void Awake() { //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) try { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[DEBUG] Awake() started."); _rememberLimit = ((BaseUnityPlugin)this).Config.Bind<int>("History", "RememberedPlayers", 100, "Max players."); _rememberLimit.Value = Mathf.Clamp(_rememberLimit.Value, 1, 1000); _openOnStartup = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "OpenOnStartup", true, "Open on start."); LoadDatabase(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[DEBUG] Awake() screen size: {Screen.width}x{Screen.height}"); float num = Mathf.Min(620f, (float)Screen.width - 40f); float num2 = Mathf.Min(520f, (float)Screen.height - 40f); _windowRect = new Rect(Mathf.Max(0f, ((float)Screen.width - num) * 0.5f), Mathf.Max(0f, ((float)Screen.height - num2) * 0.5f), num, num2); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[DEBUG] Awake() initialized window rect: {_windowRect}"); if (_openOnStartup.Value) { OpenHistoryWindow(); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[DEBUG] Awake() finished successfully."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[DEBUG] CRASH IN AWAKE: " + ex.ToString())); } } private void Start() { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[DEBUG] Start() called."); try { CharacterSelectHandler_online.clientSideMods_you_can_increment_this_to_enable_matchmaking_for_your_mods__please_dont_use_it_to_cheat_thats_really_cringe_especially_if_its_desyncing_others___you_didnt_even_win_on_your_opponents_screen___I_cannot_imagine_a_sadder_existence++; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[DEBUG] CRASH IN START: " + ex.ToString())); } } private void Update() { try { float unscaledTime = Time.unscaledTime; if (unscaledTime >= _nextScan) { _nextScan = unscaledTime + 2f; ObserveConnectedPlayers(); } if (_dirty && unscaledTime >= _nextSave) { SaveDatabase(); _nextSave = unscaledTime + 15f; } HandleResize(); if (Keyboard.current == null) { return; } if (((ButtonControl)Keyboard.current.hKey).wasPressedThisFrame) { bool flag = ((ButtonControl)Keyboard.current.leftShiftKey).isPressed || ((ButtonControl)Keyboard.current.rightShiftKey).isPressed; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[DEBUG] 'H' key pressed. Shift held: {flag}"); if (flag) { if (_isOpen) { _isOpen = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[DEBUG] Closed window via Shift+H"); } else { OpenHistoryWindow(); } } } if (_isOpen && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { _isOpen = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[DEBUG] Closed window via Escape"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[DEBUG] CRASH IN UPDATE: " + ex.ToString())); } } private void HandleResize() { //IL_001a: 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_0022: 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) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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_00c5: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) if (_isOpen && Mouse.current != null) { Vector2 val = ((InputControl<Vector2>)(object)((Pointer)Mouse.current).position).ReadValue(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(val.x, (float)Screen.height - val.y); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(((Rect)(ref _windowRect)).x + ((Rect)(ref _windowRect)).width - 20f, ((Rect)(ref _windowRect)).y + ((Rect)(ref _windowRect)).height - 20f, 20f, 20f); if (Mouse.current.leftButton.wasPressedThisFrame && ((Rect)(ref val3)).Contains(val2)) { _isResizing = true; _resizeStartMouse = val2; _resizeStartSize = new Vector2(((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height); } else if (_isResizing && Mouse.current.leftButton.isPressed) { Vector2 val4 = val2 - _resizeStartMouse; ((Rect)(ref _windowRect)).width = Mathf.Max(380f, _resizeStartSize.x + val4.x); ((Rect)(ref _windowRect)).height = Mathf.Max(280f, _resizeStartSize.y + val4.y); } else if (_isResizing && Mouse.current.leftButton.wasReleasedThisFrame) { _isResizing = false; } } } private void OnGUI() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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) try { if ((int)Event.current.type == 8) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[DEBUG] OnGUI running. Screen: {Screen.width}x{Screen.height}. IsOpen: {_isOpen}. WindowRect: {_windowRect}"); } MainMenu val = Object.FindObjectOfType<MainMenu>(); if (!_isOpen && (Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled && val.IsEnabled && GUI.Button(new Rect((float)Screen.width - 132f, 18f, 112f, 42f), "History")) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[DEBUG] Clicked History button in Main Menu."); OpenHistoryWindow(); } if (_isOpen) { _windowRect = GUI.Window(741205, _windowRect, new WindowFunction(DrawWindow), "Players You Met"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[DEBUG] CRASH IN ONGUI: " + ex.ToString())); } } private void DrawWindow(int windowId) { //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: 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_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Expected O, but got Unknown //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Saved locally. Last-met age refreshes on open.", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _isOpen = false; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (_confirmClear) { GUILayout.Label("Clear all players?", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("Confirm", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { ClearRecentPlayers(); } if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _confirmClear = false; } } else if (GUILayout.Button("Clear all players?", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) })) { _confirmClear = true; } GUILayout.Label("Limit: " + _rememberLimit.Value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); if (GUILayout.Button("-", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { SetRememberLimit(_rememberLimit.Value - 10); } if (GUILayout.Button("+", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { SetRememberLimit(_rememberLimit.Value + 10); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Search:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); _searchQuery = GUILayout.TextField(_searchQuery ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button(_sortByName ? "Sort: Name" : "Sort: Recent", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) })) { _sortByName = !_sortByName; } GUILayout.EndHorizontal(); GUILayout.Label("Players (" + _database.players.Count + ")", GUI.skin.box, Array.Empty<GUILayoutOption>()); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawPlayerList(); GUILayout.EndScrollView(); if (_creditStyle == null) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 10 }; val.normal.textColor = new Color(1f, 1f, 1f, 0.55f); _creditStyle = val; } if (GUILayout.Button(_easterEggMessage, _creditStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(20f) })) { _creditClicks++; if (_creditClicks == 3) { _easterEggMessage = "Made By UxCreed & The Copilot \ud83d\udc10"; } if (_creditClicks >= 5) { _easterEggMessage = "You found the secret! \ud83e\udd5a (Click to reset)"; } if (_creditClicks > 6) { _creditClicks = 0; _easterEggMessage = "Made By UxCreed"; } } GUILayout.EndVertical(); GUI.Box(new Rect(((Rect)(ref _windowRect)).width - 20f, ((Rect)(ref _windowRect)).height - 20f, 20f, 20f), "◢"); GUI.DragWindow(new Rect(0f, 0f, 10000f, 24f)); } private void DrawPlayerList() { List<PlayerRecord> source = _database.players.Where((PlayerRecord r) => r != null && (string.IsNullOrWhiteSpace(_searchQuery) || r.nickname.IndexOf(_searchQuery, StringComparison.OrdinalIgnoreCase) >= 0 || r.steamId.Contains(_searchQuery))).ToList(); source = ((!_sortByName) ? source.OrderByDescending((PlayerRecord r) => r.lastMetUnixSeconds).ToList() : source.OrderBy((PlayerRecord r) => r.nickname).ToList()); if (source.Count == 0) { GUILayout.Label(string.IsNullOrEmpty(_searchQuery) ? "No players remembered yet." : "No players match your search.", Array.Empty<GUILayoutOption>()); return; } PlayerRecord playerRecord = null; foreach (PlayerRecord item in source) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty<GUILayoutOption>()); if (GUILayout.Button((string.IsNullOrWhiteSpace(item.nickname) ? "Unknown player" : item.nickname) + ((item.steamId == _expandedSteamId) ? " -" : " +"), Array.Empty<GUILayoutOption>())) { _expandedSteamId = ((item.steamId == _expandedSteamId) ? string.Empty : item.steamId); } if (item.steamId == _expandedSteamId) { if (!_lastMetSnapshot.TryGetValue(item.steamId, out var value)) { value = FormatLastMetAge(item.lastMetUnixSeconds); _lastMetSnapshot[item.steamId] = value; } GUILayout.Label("Last met: " + value, Array.Empty<GUILayoutOption>()); GUILayout.Label("Steam ID: " + item.steamId, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Profile", Array.Empty<GUILayoutOption>())) { OpenSteamProfile(item.steamId); } if (GUILayout.Button("Copy ID", Array.Empty<GUILayoutOption>())) { GUIUtility.systemCopyBuffer = item.steamId; } if (GUILayout.Button("Delete", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { playerRecord = item; } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } if (playerRecord != null) { _database.players.Remove(playerRecord); _lastMetSnapshot.Remove(playerRecord.steamId); MarkDirty(); } } private void OpenHistoryWindow() { _isOpen = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[DEBUG] OpenHistoryWindow() called. _isOpen set to true."); RefreshLastMetSnapshot(); } private void ObserveConnectedPlayers() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) SteamManager instance = SteamManager.instance; if ((Object)(object)instance == (Object)null || instance.connectedPlayers == null) { return; } long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); bool flag = false; for (int i = 0; i < instance.connectedPlayers.Count; i++) { SteamConnection val = instance.connectedPlayers[i]; if (val == null || !((ConnectionManager)val).Connected) { continue; } ulong num2 = SteamId.op_Implicit(val.id); if (num2 == 0L) { continue; } string text = num2.ToString(CultureInfo.InvariantCulture); PlayerRecord playerRecord = FindPlayer(text); if (playerRecord == null) { playerRecord = new PlayerRecord { steamId = text, nickname = NormalizeNickname(val.steamName, text), lastMetUnixSeconds = num }; _database.players.Add(playerRecord); TrimRecentPlayers(); MarkDirty(); flag = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("[DEBUG] Added new player: " + playerRecord.nickname + " (" + text + ")")); } else { bool flag2 = false; string text2 = NormalizeNickname(val.steamName, text); if (!string.Equals(playerRecord.nickname, text2, StringComparison.Ordinal)) { playerRecord.nickname = text2; flag2 = true; } if (num - playerRecord.lastMetUnixSeconds >= 10) { playerRecord.lastMetUnixSeconds = num; flag2 = true; } if (flag2) { MarkDirty(); } } } if (flag) { SaveDatabase(); } } private void LoadDatabase() { try { if (!File.Exists(DatabasePath)) { return; } string[] array = File.ReadAllLines(DatabasePath); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array2 = array; foreach (string text in array2) { if (!string.IsNullOrWhiteSpace(text)) { string[] array3 = text.Split('|'); if (array3.Length >= 3 && ulong.TryParse(array3[0], NumberStyles.None, CultureInfo.InvariantCulture, out var result) && result != 0L && hashSet.Add(array3[0])) { _database.players.Add(new PlayerRecord { steamId = array3[0], nickname = array3[1], lastMetUnixSeconds = long.Parse(array3[2]) }); } } } TrimRecentPlayers(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[PlayerHistory] loaded " + _database.players.Count + " saved player record(s).")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[DEBUG] failed to load player history: " + ex)); } } private bool SaveDatabase() { try { string directoryName = Path.GetDirectoryName(DatabasePath); if (string.IsNullOrEmpty(directoryName)) { return false; } Directory.CreateDirectory(directoryName); List<string> list = new List<string>(); foreach (PlayerRecord player in _database.players) { string arg = (player.nickname ?? "").Replace("|", ""); list.Add($"{player.steamId}|{arg}|{player.lastMetUnixSeconds}"); } string text = DatabasePath + ".tmp"; File.WriteAllLines(text, list); if (File.Exists(DatabasePath)) { File.Delete(DatabasePath); } File.Move(text, DatabasePath); _dirty = false; return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[DEBUG] failed to save player history: " + ex)); return false; } } private void OnApplicationQuit() { if (_dirty) { SaveDatabase(); } } private void OnDestroy() { if (_dirty) { SaveDatabase(); } } private void RefreshLastMetSnapshot() { _lastMetSnapshot.Clear(); foreach (PlayerRecord player in _database.players) { if (player != null) { _lastMetSnapshot[player.steamId] = FormatLastMetAge(player.lastMetUnixSeconds); } } } private static string FormatLastMetAge(long timestamp) { if (timestamp <= 0) { return "Unknown"; } long num = Math.Max(0L, DateTimeOffset.UtcNow.ToUnixTimeSeconds() - timestamp); if (num < 60) { return "Just now"; } if (num < 3600) { return num / 60 + " minute(s) ago"; } if (num < 86400) { return num / 3600 + " hour(s) ago"; } return num / 86400 + " day(s) ago"; } private static string NormalizeNickname(string nickname, string fallback) { if (!string.IsNullOrWhiteSpace(nickname)) { return nickname.Trim(); } return "Player " + fallback; } private PlayerRecord FindPlayer(string steamId) { return _database.players.FirstOrDefault((PlayerRecord r) => r != null && string.Equals(r.steamId, steamId, StringComparison.Ordinal)); } private void SetRememberLimit(int value) { _rememberLimit.Value = Mathf.Clamp(value, 1, 1000); ((BaseUnityPlugin)this).Config.Save(); TrimRecentPlayers(); MarkDirty(); } private void TrimRecentPlayers() { while (_database.players.Count > _rememberLimit.Value) { PlayerRecord playerRecord = _database.players.OrderBy((PlayerRecord r) => r.lastMetUnixSeconds).FirstOrDefault(); if (playerRecord == null) { break; } _database.players.Remove(playerRecord); _lastMetSnapshot.Remove(playerRecord.steamId); _dirty = true; } } private void ClearRecentPlayers() { _database.players.Clear(); _expandedSteamId = string.Empty; _confirmClear = false; MarkDirty(); SaveDatabase(); } private void MarkDirty() { _dirty = true; if (_nextSave <= Time.unscaledTime) { _nextSave = Time.unscaledTime + 15f; } } private void OpenSteamProfile(string steamId) { if (!ulong.TryParse(steamId, out var result) || result == 0L) { return; } try { Application.OpenURL("steam://url/SteamIDPage/" + result.ToString(CultureInfo.InvariantCulture)); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to open Steam profile: " + ex.Message)); } } }