using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using FMODUnity; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SBGL.UnifiedMod.Core; using SBGL.UnifiedMod.Features; using SBGL.UnifiedMod.Features.CompetitivePluginCheck; using SBGL.UnifiedMod.Features.HitTracker; using SBGL.UnifiedMod.Features.Poll; using SBGL.UnifiedMod.Patches; using SBGL.UnifiedMod.Utils; using SBGLLiveLeaderboard; using SBGLeagueAutomation; using Steamworks; using Steamworks.Data; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Localization; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("GameAssembly")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SBGL.UnifiedMod")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("0.1.20.0")] [assembly: AssemblyInformationalVersion("0.1.20+54a0e429eca82ffe2ab88e5208f61fd03cfa0465")] [assembly: AssemblyProduct("Super Battle Golf League Unified Mod")] [assembly: AssemblyTitle("SBGL.UnifiedMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.20.0")] [module: UnverifiableCode] [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 SBGLeagueAutomation { public class SBGLPlugin : MonoBehaviour { [HarmonyPatch] private static class CourseManager_RpcInformEndingCourse_Patch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("CourseManager"); if (type == null) { return null; } return AccessTools.Method(type, "UserCode_RpcInformEndingCourse", (Type[])null, (Type[])null); } private static void Postfix() { try { SBGLPlugin sBGLPlugin = Object.FindAnyObjectByType((FindObjectsInactive)1); if ((Object)(object)sBGLPlugin != (Object)null) { sBGLPlugin.OnCourseRpcInformEndingCourse(); } } catch (Exception arg) { Debug.Log((object)$"[SBGL.Matchmaking] CourseManager.RpcInformEndingCourse postfix error: {arg}"); } } } public class PlayerProfile { public string id; public string display_name; public string region; public string state_province; public float current_mmr; } public class MatchmakingSession { public string id; public string lobby_name; public string lobby_password; public string host_player_id; public string status; public string match_id; public string lobby_id; public string host_steam_id; public string steam_lobby_link; public List player_ids; public List accepted_player_ids; public string match_type; public string selected_course; public int season; } public class MatchStats { public string matchmaking_session_id; public string match_id; public string player_id; public string player_name; public string match_date; public int duration_seconds; public bool is_host; public string status; public string course_name; public string match_type; public int season; } private class CachedLeaderboardPlayer { public string Name; public int BaseScore; public string RawStrokes; public string MMR; public int ProjectedDelta; } public struct PlayerData { public string name; public string mmr; } [CompilerGenerated] private sealed class d__153 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Action onUploaded; public SBGLPlugin <>4__this; private string 5__1; private WWWForm
5__2; private UnityWebRequest 5__3; private JObject 5__4; private string 5__5; private string 5__6; private int 5__7; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__153(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } 5__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; 5__5 = null; 5__6 = null; <>1__state = -2; } private bool MoveNext() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_0148: Unknown result type (might be due to invalid IL or missing references) try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; onUploaded?.Invoke(null); return false; case 1: <>1__state = -3; if ((int)5__3.result == 1) { 5__4 = <>4__this.ParseApiSingleObject(5__3.downloadHandler.text); JObject obj = 5__4; string text = (string)((obj != null) ? obj["file_url"] : null); if (text == null) { JObject obj2 = 5__4; text = (string)((obj2 != null) ? obj2["url"] : null); } 5__5 = text; if (!string.IsNullOrWhiteSpace(5__5)) { <>4__this.Log("[Match Screenshot] ✓ Screenshot uploaded to file storage"); onUploaded?.Invoke(5__5); } else { <>4__this.Log("[Match Screenshot] UploadFile succeeded but response did not contain file_url"); onUploaded?.Invoke(null); } 5__4 = null; 5__5 = null; } else { 5__6 = $"[Match Screenshot] UploadFile failed: {5__3.result}"; if (!string.IsNullOrEmpty(5__3.error)) { 5__6 = 5__6 + " - " + 5__3.error; } if (5__3.responseCode > 0) { 5__6 += $" (HTTP {5__3.responseCode})"; } DownloadHandler downloadHandler = 5__3.downloadHandler; if (!string.IsNullOrEmpty((downloadHandler != null) ? downloadHandler.text : null)) { 5__7 = Math.Min(240, 5__3.downloadHandler.text.Length); 5__6 = 5__6 + " - Response: " + 5__3.downloadHandler.text.Substring(0, 5__7); } <>4__this.Log("" + 5__6 + ""); onUploaded?.Invoke(null); 5__6 = null; } <>m__Finally1(); 5__3 = null; 5__1 = null; 5__2 = null; break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } ((IDisposable)this).Dispose(); return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (5__3 != null) { ((IDisposable)5__3).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private Dictionary _randomizedPlayerMap = null; private static readonly List _sbglTestNames = new List { "TrashxCat", "KingCire03", "FucklTheIRS", "Jackie", "TikTok Z4C_FN", "adthykrshnn", "mrguy7608", "Noah_Boatt", "YoMaMMeJr", "TheMrEStudio", "Odius9064", "Jake paul", "f19chy", "Kodakblackarack", "ArrowTheFighter", "limbo", "Marcus_Pipes", "That Traynor", "Blinkerfluid", "TaxiCAB", "Achunl2", "Cinereous", "Liafeon", "Peter", "Inkie", "LLENN", "Nanainasuit", "Midnight", "PattyTits", "Bekuh", "Lunwik", "yackback", "AlpineMilk", "HandsomeSkippy", "TheOneKP", "RealJosher", "Ooshida", "Moto", "Jozza", "Patrick Swayze", "ryan.scibetta", "Zaikr", "Thalosii", "paradiorevey", "Ricee", "Slacker87", "MRGoldberg", "trusted", "designedsilence", "The Oreo Orgy", "Zoboomafoo", "Yoda Cage", "Black Dolphin", "Pengini", "antyde", "Glider", "lyth", "Jabobus.o7", "dkgaming219", "Cody", "Sidimmu", "снусмумрик", "Skydown26", "Jeb", "Vac", "Slem Dogg", "Batto", "Broj0e" }; private ConfigEntry _showLogsConfig; private ConfigEntry _showFlowDebugConfig; private ConfigEntry _showUploadNoticesConfig; private ConfigEntry _ignoreSbglLobbyRequirementConfig; private ConfigEntry _testPlayerOverridesConfig; private ConfigEntry _testRandomizeAllPlayersConfig; private ManualLogSource _bepinexLogger; private bool _isInitializing = true; private int _onlineCount = 0; private int _queuedCount = 0; private int _matchedCount = 0; public static bool IsRankedTriggered = false; public static string CurrentMatchId = null; private bool _isQueueing = false; private string _webStatus = "IDLE"; private string _currentQueueId = ""; private PlayerProfile _userProfile = null; private MatchmakingSession _currentSession = null; private bool _isHost = false; private bool _hasAccepted = false; private string _hostRulesetSelection = "ranked"; private string _queueTypeSelection = "ranked"; private DateTime? _queueStartTime = null; private bool _hostLobbyStarted = false; private bool _hostServerWasActive = false; private bool _hostCancelSent = false; private bool _matchStatsSubmitted = false; private bool _proSeriesSkipLogged = false; private DateTime? _matchStartTime = null; private Dictionary _cachedLeaderboardScores = new Dictionary(); private Dictionary _cachedLeaderboardScoresVsPar = new Dictionary(); private string __currentMatchId = null; private HashSet _finalizedMatchIds = new HashSet(StringComparer.OrdinalIgnoreCase); private Dictionary _playerMatchEntryIds = new Dictionary(); private HashSet _matchEntryCreationInProgress = new HashSet(StringComparer.OrdinalIgnoreCase); private Dictionary _playerIdsByName = new Dictionary(StringComparer.OrdinalIgnoreCase); private Dictionary _lastSubmittedScores = new Dictionary(); private Dictionary _lastSubmittedScoresVsPar = new Dictionary(); private int _matchExpectedPlayerCount = 2; private int _lastUploadedPlayerCount = -1; private List _finalLeaderboardSnapshot = new List(); private byte[] _pendingMatchScreenshotBytes = null; private string _pendingMatchScreenshotFileName = null; private string _pendingUploadedScreenshotUrl = null; private bool _matchScreenshotUploadInProgress = false; private bool _matchScreenshotUploadCompleted = false; private bool _matchEntriesCreated = false; private bool _matchCreationInProgress = false; private bool _isInGameplay = false; private bool _cachedIsMenuScene = false; private bool _matchEndedReceived = false; private Coroutine _monitorCoroutine = null; private Coroutine _lobbyMonitorCoroutine = null; private Coroutine _endOfMatchSignalCoroutine = null; private Coroutine _matchScreenshotSignalCoroutine = null; private Coroutine _courseEndRpcCoroutine = null; private float _nextEnsureMatchCreateAttemptAt = 0f; private string _localManualSessionId = null; private const int MatchScreenshotMaxWidth = 1920; private const int MatchScreenshotMaxHeight = 1080; private const int MatchScreenshotTargetMaxBytes = 819200; private const int MatchScreenshotHardMaxBytes = 1048576; private string _activeSeasonId = null; private string _activeSeasonName = null; private bool _activeSeasonFetched = false; private List _debugLogs = new List(); private List _queuedPlayers = new List(); private Vector2 _logScroll; private Texture2D _profileTexture = null; private Texture2D _solidBgTex = null; private bool _hasFetchedProfilePic = false; private GUIStyle _centerLabelStyle = null; private GUIStyle _debugLineStyle = null; private string _uploadNotification = ""; private DateTime _uploadNotificationTime = DateTime.MinValue; private const float _uploadNotificationDuration = 4f; private Color _uploadNotificationColor = new Color(0.2f, 0.85f, 1f); private string _debugLobbySessionSource = ""; private string _debugLobbyPrefsSource = ""; private string _debugLobbyCapturedSource = ""; private string _debugLobbyResolved = ""; private string _debugLobbyResolvedBy = "none"; private MatchResultSubmissionService _matchResultSubmission; private static readonly WaitForSeconds _syncLoopDelay = new WaitForSeconds(5f); private static readonly WaitForSeconds _readyTransitionDelay = new WaitForSeconds(0.5f); private int _syncTickCount = 0; private int _lobbyCreatedEventCount = 0; private int _steamLinkUploadAttempts = 0; private int _steamLinkUploadSuccesses = 0; private int _steamLinkUploadFailures = 0; private int _autoJoinAttempts = 0; private int _autoJoinSuccesses = 0; private int _autoJoinFailures = 0; private DateTime? _lastLobbyCreatedAt = null; private DateTime? _lastUploadAttemptAt = null; private DateTime? _lastUploadSuccessAt = null; private DateTime? _lastAutoJoinAttemptAt = null; private DateTime? _lastAutoJoinSuccessAt = null; private string _lastGeneratedSteamLink = "-"; private string _lastUploadedSteamLink = "-"; private string _lastAutoJoinSteamLink = "-"; private string _lastUploadError = "-"; private string _lastAutoJoinError = "-"; private string _lastCallApiError = ""; private bool IgnoreSbglLobbyRequirementEnabled => _ignoreSbglLobbyRequirementConfig?.Value ?? false; private string _currentMatchId { get { return __currentMatchId; } set { __currentMatchId = value; CurrentMatchId = value; } } private bool IsActiveSeason => _activeSeasonFetched && !string.IsNullOrEmpty(_activeSeasonId); public bool IsQueueing => _isQueueing; public bool IsHost => _isHost; public bool HasAccepted => _hasAccepted; public PlayerProfile UserProfile => _userProfile; public MatchmakingSession CurrentSession => _currentSession; private string GetBaseApiUrl() { return UnifiedPlugin.GetCurrentBaseApi(); } private string GetAppId() { return UnifiedPlugin.GetCurrentAppId(); } private string GetAuthToken() { return UnifiedPlugin.GetCurrentAuthToken(); } public void SetConfig(ConfigEntry showLogs, ConfigEntry showFlowDebug, ConfigEntry showUploadNotices, ConfigEntry ignoreSbglLobbyRequirement, ConfigEntry testPlayerOverrides, ConfigEntry testRandomizeAllPlayers, ManualLogSource bepinexLogger) { _showLogsConfig = showLogs; _showFlowDebugConfig = showFlowDebug; _showUploadNoticesConfig = showUploadNotices; _ignoreSbglLobbyRequirementConfig = ignoreSbglLobbyRequirement; _testPlayerOverridesConfig = testPlayerOverrides; _testRandomizeAllPlayersConfig = testRandomizeAllPlayers; _bepinexLogger = bepinexLogger; } private Dictionary GetTestPlayerOverrides(List currentPlayerNames = null) { if (_testRandomizeAllPlayersConfig != null && _testRandomizeAllPlayersConfig.Value && currentPlayerNames != null) { if (_randomizedPlayerMap == null || _randomizedPlayerMap.Count != currentPlayerNames.Count || !_randomizedPlayerMap.Keys.SequenceEqual(currentPlayerNames)) { List source = new List(_sbglTestNames); Random rand = new Random(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); List list = source.OrderBy((string x) => rand.Next()).ToList(); int num = 0; foreach (string currentPlayerName in currentPlayerNames) { if (string.IsNullOrWhiteSpace(currentPlayerName)) { continue; } if (_userProfile != null && string.Equals(currentPlayerName, _userProfile.display_name, StringComparison.OrdinalIgnoreCase)) { dictionary[currentPlayerName] = currentPlayerName; continue; } if (num >= list.Count) { num = 0; } dictionary[currentPlayerName] = list[num++]; } _randomizedPlayerMap = dictionary; } return _randomizedPlayerMap; } Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); string text = _testPlayerOverridesConfig?.Value; if (string.IsNullOrWhiteSpace(text)) { return dictionary2; } string[] array = text.Split(','); foreach (string text2 in array) { int num3 = text2.IndexOf('='); if (num3 > 0 && num3 < text2.Length - 1) { string text3 = text2.Substring(0, num3).Trim(); string value = text2.Substring(num3 + 1).Trim(); if (!string.IsNullOrWhiteSpace(text3) && !string.IsNullOrWhiteSpace(value)) { dictionary2[text3] = value; } } } return dictionary2; } private string ApplyTestPlayerOverride(string displayName, Dictionary overrides) { if (overrides != null && overrides.TryGetValue(displayName, out var value)) { Log("[Test Override] '" + displayName + "' → '" + value + "'"); return value; } return displayName; } public IEnumerator MatchmakingLoopCoroutine() { return MatchmakingLoop(); } public IEnumerator AcceptMatchCoroutine() { return AcceptMatch(); } public void InitiateHostSequencePublic() { InitiateHostSequence(); } public IEnumerator ClaimHostRoleCoroutine() { if (_currentSession == null || _userProfile == null || _isHost) { yield break; } Log("[PDS] Claiming host role for session " + _currentSession.id + "..."); yield return CallAPI(json: "{\"host_player_id\":\"" + _userProfile.id + "\"}", endpoint: "/matchmaking_session?id=eq." + _currentSession.id, method: "PATCH", onSuccess: delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { _currentSession.host_player_id = _userProfile.id; _isHost = true; Log("[PDS] ✓ Host role claimed — session host_player_id set to our player ID."); } else { Log("[PDS] Failed to claim host role — API response was null."); } }); } private static bool IsProSeriesMatchType(string matchType) { return !string.IsNullOrWhiteSpace(matchType) && matchType.IndexOf("pro_series", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsCasualMatchType(string matchType) { return !string.IsNullOrWhiteSpace(matchType) && matchType.IndexOf("casual", StringComparison.OrdinalIgnoreCase) >= 0; } private bool IsCurrentMatchCasual() { if (IsCasualMatchType(_currentSession?.match_type)) { return true; } return IsCasualMatchType(PlayerPrefs.GetString("MatchType", "")); } private string GetQueueMatchTypePayload() { string internalMatchType = PlayerPrefs.GetString("MatchType", "ranked_season_2"); return Season2RuleSet.ToQueueMatchType(internalMatchType); } private void Awake() { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_002a: 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) _solidBgTex = new Texture2D(1, 1); _solidBgTex.SetPixel(0, 0, new Color(0.02f, 0.02f, 0.02f, 1f)); _solidBgTex.Apply(); SceneManager.sceneLoaded += OnSceneLoaded; CourseManager.MatchStateChanged += OnCourseManagerMatchStateChanged; CourseManager.ForceDisplayScoreboardChanged += OnCourseManagerForceDisplayScoreboardChanged; new Harmony("com.sbgl.matchmaking").PatchAll(); Log("Plugin Loaded v6.2.1. Self-Sync Reconciliation Active."); _matchResultSubmission = new MatchResultSubmissionService(GetBaseApiUrl, Log, CallAPI, ParseApiSingleObject, delegate(IEnumerator coro) { ((MonoBehaviour)this).StartCoroutine(coro); }); UnifiedPlugin.ApiConfigChanged += OnApiConfigChanged; ((MonoBehaviour)this).StartCoroutine(BackgroundSyncLoop()); } private void OnApiConfigChanged() { Log("⚡ API Configuration changed - Switching environments and refreshing profile"); _userProfile = null; _isInitializing = true; _webStatus = "ENVIRONMENT SWITCHING..."; ResetPluginState(); } private void OnEnable() { try { SteamMatchmaking.OnLobbyCreated += OnLobbyCreatedCallback; } catch (Exception ex) { Log("Warning: Could not hook LobbyCreated event: " + ex.Message); } } private void OnDisable() { try { SteamMatchmaking.OnLobbyCreated -= OnLobbyCreatedCallback; } catch { } } private void OnLobbyCreatedCallback(Result result, Lobby lobby) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0041: 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_0013: 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_0094: 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) if ((int)result != 1) { _lastUploadError = $"Lobby create callback failed: {result}"; Log($"Lobby creation failed: {result}"); return; } SteamId id = ((Lobby)(ref lobby)).Id; if (!((SteamId)(ref id)).IsValid) { _lastUploadError = "Lobby created callback had invalid lobby id."; Log("Lobby ID is invalid!"); } else { if (!_isHost || _currentSession == null) { return; } ulong value = ((Lobby)(ref lobby)).Id.Value; ulong num = 0uL; try { if (SteamClient.IsValid) { num = SteamClient.SteamId.Value; } } catch { } if (num != 0) { string text = $"steam://joinlobby/4069520/{value}/{num}"; _lobbyCreatedEventCount++; _lastLobbyCreatedAt = DateTime.Now; _lastGeneratedSteamLink = text; Log($"Lobby created! ID: {value}, uploading link..."); ((MonoBehaviour)this).StartCoroutine(UploadSteamLobbyLink(text)); } else { _lastUploadError = "Steam client invalid while trying to build host steam link."; Log("Lobby created but Steam ID could not be resolved."); } } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { string text = ((Scene)(ref scene)).name.ToLower(); _cachedIsMenuScene = text.Contains("menu"); Log("[Scene] Loaded: " + ((Scene)(ref scene)).name + ""); string text2 = ResolveCurrentLobbyName(); bool flag = !string.IsNullOrEmpty(text2) && text2.StartsWith("SBGL-", StringComparison.OrdinalIgnoreCase); bool flag2 = IgnoreSbglLobbyRequirementEnabled || IsRankedTriggered || flag; if (!text.Contains("menu") && !text.Contains("drivingrange") && !text.Contains("driving range") && !text.Contains("lobby")) { _isInGameplay = true; if (_currentSession != null && string.Equals(_currentSession.status, "completed", StringComparison.OrdinalIgnoreCase)) { Log("[Match] Previous MatchmakingSession status was 'completed' — clearing session so new match can be created"); _currentSession = null; } if (flag2 && ((_currentSession != null && (_currentSession.status == "ready" || _currentSession.status == "in_progress")) || _currentSession == null) && !_matchEntriesCreated) { Log("[Match] Entering gameplay - validating match eligibility..."); ((MonoBehaviour)this).StartCoroutine(ValidateMatchUpload(delegate(bool shouldUpload) { if (shouldUpload) { Log("[Match] Creating match records..."); ((MonoBehaviour)this).StartCoroutine(CreateMatchAndEntries()); } else { Log("[Match] Match does not meet upload criteria at load - starting lobby rename monitor"); } })); } if (_monitorCoroutine == null) { _monitorCoroutine = ((MonoBehaviour)this).StartCoroutine(MonitorAndUpdateScores()); } if (_lobbyMonitorCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_lobbyMonitorCoroutine); } _lobbyMonitorCoroutine = ((MonoBehaviour)this).StartCoroutine(MonitorLobbyNameForUpload()); } if (_isInGameplay && (text.Contains("drivingrange") || text.Contains("driving range"))) { _isInGameplay = false; StopGameplayCoroutinesForMatchEnd("scene transition to driving range"); TryCaptureFinalLeaderboardSnapshot("scene transition"); } if (text.Contains("drivingrange") || text.Contains("driving range")) { if (!_matchStatsSubmitted && !string.IsNullOrEmpty(_currentMatchId)) { Log("[Match Stats] Returned to Driving Range - running final match finalization before reset..."); ((MonoBehaviour)this).StartCoroutine(FinalizeAndResetAfterDrivingRange()); } else { if (_matchStatsSubmitted && !string.IsNullOrWhiteSpace(_currentMatchId)) { _finalizedMatchIds.Add(_currentMatchId); } ResetPerMatchState(); } if (_isQueueing && !_hasAccepted) { Log("[Queue] Player entered Driving Range while queued - cancelling queue entry..."); ((MonoBehaviour)this).StartCoroutine(LeaveQueue()); } } if (text.Contains("menu")) { if (_isHost && _hostLobbyStarted && _hostServerWasActive && _currentSession != null) { ((MonoBehaviour)this).StartCoroutine(CancelSessionAsHost("host_returned_to_menu")); } ResetPluginState(); Log("Returned to Menu: State Reset."); } } private void OnCourseManagerMatchStateChanged(MatchState previousState, MatchState currentState) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (_isInGameplay) { Log($"[Match Signal] CourseManager.MatchStateChanged: {previousState} -> {currentState} (ignored for course-end)"); } } private void OnCourseManagerForceDisplayScoreboardChanged() { if (_isInGameplay && CourseManager.ForceDisplayScoreboard) { Log("[Match Signal] CourseManager forced the scoreboard — hole complete"); if (_matchScreenshotSignalCoroutine == null && !_matchScreenshotUploadCompleted) { _matchScreenshotSignalCoroutine = ((MonoBehaviour)this).StartCoroutine(CaptureAndStorePerHoleScreenshot("CourseManager.ForceDisplayScoreboardChanged")); } if (!string.IsNullOrEmpty(_currentMatchId)) { ((MonoBehaviour)this).StartCoroutine(PushPerHoleScoreUpdate()); } } } private IEnumerator PushPerHoleScoreUpdate() { LiveLeaderboardPlugin liveLeaderboard = Object.FindAnyObjectByType((FindObjectsInactive)1); if ((Object)(object)liveLeaderboard == (Object)null) { yield break; } List players = liveLeaderboard.GetCurrentLeaderboard(); if (players == null || players.Count == 0) { yield break; } List names = (from p in players where p != null && !string.IsNullOrWhiteSpace(p.Name) select p.Name).ToList(); Dictionary testOverrides = GetTestPlayerOverrides(names); foreach (LiveLeaderboardPlugin.SBGLPlayer player in players) { if (player == null || string.IsNullOrWhiteSpace(player.Name) || player.BaseScore == 0) { continue; } int gamePoints = player.BaseScore; int scoreVsPar = 0; if (!string.IsNullOrEmpty(player.RawStrokes)) { string s = player.RawStrokes.Replace("±", "").Trim(); int.TryParse(s, out scoreVsPar); } string playerId = null; if (!TryGetPlayerIdForName(player.Name, out playerId)) { string overrideName = ApplyTestPlayerOverride(player.Name, testOverrides); yield return ResolvePlayerIdByNameFromApi(overrideName, delegate(string id) { playerId = id; }); if (!string.IsNullOrEmpty(playerId)) { _playerIdsByName[player.Name.Trim()] = playerId; } } if (string.IsNullOrEmpty(playerId)) { continue; } string entryId = null; _playerMatchEntryIds.TryGetValue(playerId, out entryId); string preHoleMmr = null; if (_userProfile != null && string.Equals(player.Name, _userProfile.display_name, StringComparison.OrdinalIgnoreCase)) { preHoleMmr = ((_userProfile.current_mmr > 0f) ? _userProfile.current_mmr.ToString(CultureInfo.InvariantCulture) : null); } float result; if (string.IsNullOrEmpty(preHoleMmr) && !string.IsNullOrEmpty(playerId)) { string lbMmr = player.MMR; if (!string.IsNullOrEmpty(lbMmr) && lbMmr != "..." && lbMmr != "--" && float.TryParse(lbMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { preHoleMmr = lbMmr; } else { yield return CallAPI("/player?id=eq." + playerId + "&select=current_mmr&limit=1", "GET", "", delegate(string res) { JObject val = ParseApiSingleObject(res); object obj = ((val != null) ? val["current_mmr"] : null); if (obj != null) { preHoleMmr = obj.ToString(); } }); } } string postHoleMmr = null; if (!string.IsNullOrEmpty(preHoleMmr) && float.TryParse(preHoleMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out var preHoleMmrF)) { int delta = ParseProjectedMmrDelta(player.ProjectedDisplay); result = preHoleMmrF + (float)delta; postHoleMmr = result.ToString(CultureInfo.InvariantCulture); } if (string.IsNullOrEmpty(entryId)) { yield return EnsureMatchEntryForPlayer("Per-Hole Push", playerId, player.Name, preHoleMmr, postHoleMmr, gamePoints, scoreVsPar, 0, "Created on hole completion", delegate(string id) { entryId = id; }); } if (!string.IsNullOrEmpty(entryId)) { yield return UpdateMatchEntry(entryId, playerId, player.Name, gamePoints, scoreVsPar, 0, postHoleMmr, preHoleMmr); _lastSubmittedScores[player.Name] = gamePoints; _lastSubmittedScoresVsPar[player.Name] = scoreVsPar; } } } private IEnumerator CaptureAndStorePerHoleScreenshot(string source) { try { yield return CaptureMatchScreenshotForReview(source + " (per-hole immediate)"); } finally { _matchScreenshotSignalCoroutine = null; } } private IEnumerator CaptureAndUploadScreenshotFromForcedScoreboard(string source) { try { yield return (object)new WaitForSeconds(2f); float waited = 0f; float snapshotTimeout = 6f; bool gotFinalSnapshot = false; for (; waited < snapshotTimeout; waited += 0.25f) { if (_matchEndedReceived) { gotFinalSnapshot = true; break; } try { if (TryCaptureFinalLeaderboardSnapshot(source + " (screenshot-wait)")) { gotFinalSnapshot = true; break; } } catch { } yield return (object)new WaitForSeconds(0.25f); } if (!gotFinalSnapshot) { Log($"[Match Screenshot] Final leaderboard snapshot not available within {snapshotTimeout}s - skipping forced-scoreboard capture ({source})"); yield break; } yield return CaptureMatchScreenshotForReview(source); yield return UploadCapturedMatchScreenshotIfNeeded(source); } finally { _matchScreenshotSignalCoroutine = null; } } private void TryBeginAssemblyDrivenFinalization(string source) { if (_endOfMatchSignalCoroutine == null && !_matchStatsSubmitted && !string.IsNullOrWhiteSpace(_currentMatchId)) { _endOfMatchSignalCoroutine = ((MonoBehaviour)this).StartCoroutine(FinalizeFromGameplaySignal(source)); } } private IEnumerator FinalizeFromGameplaySignal(string source) { Log("[Match Signal] " + source + " detected - attempting early end-of-match finalization..."); StopGameplayCoroutinesForMatchEnd(source); yield return CaptureMatchScreenshotForReview(source); for (int attempt = 0; attempt < 12; attempt++) { if (TryCaptureFinalLeaderboardSnapshot(source)) { break; } yield return (object)new WaitForSeconds(0.25f); } if (_finalLeaderboardSnapshot == null || _finalLeaderboardSnapshot.Count == 0) { yield return UploadCapturedMatchScreenshotIfNeeded(source + " (no final snapshot yet)"); Log("[Match Signal] " + source + " did not yield a final snapshot yet - falling back to Driving Range finalization"); _endOfMatchSignalCoroutine = null; yield break; } if (!_matchStatsSubmitted && !string.IsNullOrWhiteSpace(_currentMatchId)) { yield return FinalizeMatchStats(); if (IsRankedTriggered && _currentSession != null) { yield return UpdateSessionStatus("completed"); } } yield return UploadCapturedMatchScreenshotIfNeeded(source); _endOfMatchSignalCoroutine = null; } private void StopGameplayCoroutinesForMatchEnd(string source) { if (_monitorCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_monitorCoroutine); _monitorCoroutine = null; Log("[Match] Score monitoring stopped (" + source + ")"); } if (_lobbyMonitorCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_lobbyMonitorCoroutine); _lobbyMonitorCoroutine = null; } } private bool TryCaptureFinalLeaderboardSnapshot(string source) { try { LiveLeaderboardPlugin liveLeaderboardPlugin = Object.FindAnyObjectByType((FindObjectsInactive)1); if ((Object)(object)liveLeaderboardPlugin == (Object)null) { return _finalLeaderboardSnapshot != null && _finalLeaderboardSnapshot.Count > 0; } liveLeaderboardPlugin.CaptureLeaderboardSnapshot(); CacheLeaderboardSnapshot(liveLeaderboardPlugin.GetFinalLeaderboardSnapshot(), source); if (_finalLeaderboardSnapshot != null && _finalLeaderboardSnapshot.Count > 0) { Log("[Match] ✓ Leaderboard snapshot captured from " + source + ""); return true; } } catch (Exception ex) { Log("[Match] Could not capture " + source + " snapshot: " + ex.Message + ""); } return _finalLeaderboardSnapshot != null && _finalLeaderboardSnapshot.Count > 0; } private IEnumerator CaptureMatchScreenshotForReview(string source) { if (_matchScreenshotUploadCompleted || _matchScreenshotUploadInProgress) { yield break; } if (_pendingMatchScreenshotBytes != null && _pendingMatchScreenshotBytes.Length != 0) { Log("[Match Screenshot] Replacing previous pending screenshot with a newer capture (" + source + ")"); } float waitedSeconds = 0f; while (!CourseManager.ForceDisplayScoreboard && waitedSeconds < 4f) { yield return null; waitedSeconds += Mathf.Max(Time.unscaledDeltaTime, 0.016f); } if (!CourseManager.ForceDisplayScoreboard) { Log("[Match Screenshot] Forced scoreboard was not visible in time for " + source + " - skipping capture for now"); yield break; } yield return (object)new WaitForEndOfFrame(); int screenWidth = Screen.width; int screenHeight = Screen.height; if (screenWidth <= 0 || screenHeight <= 0) { Log("[Match Screenshot] Screen dimensions were invalid - skipping capture"); yield break; } Texture2D capturedTexture = null; Texture2D encodedTexture = null; try { capturedTexture = new Texture2D(screenWidth, screenHeight, (TextureFormat)3, false); capturedTexture.ReadPixels(new Rect(0f, 0f, (float)screenWidth, (float)screenHeight), 0, 0, false); capturedTexture.Apply(false, false); encodedTexture = ResizeTextureToFit(capturedTexture, 1920, 1080); byte[] imageBytes = EncodeScreenshotBytes(encodedTexture); if (imageBytes == null || imageBytes.Length == 0) { Log("[Match Screenshot] JPEG encoding returned no data - skipping capture"); yield break; } _pendingMatchScreenshotBytes = imageBytes; _pendingMatchScreenshotFileName = BuildMatchScreenshotFileName(); _pendingUploadedScreenshotUrl = null; Log($"[Match Screenshot] ✓ Captured final scoreboard screenshot from {source} ({((Texture)encodedTexture).width}x{((Texture)encodedTexture).height}, {(float)imageBytes.Length / 1024f:0.#} KB)"); } catch (Exception ex) { Exception ex2 = ex; Log("[Match Screenshot] Could not capture screenshot from " + source + ": " + ex2.Message + ""); } finally { if ((Object)(object)encodedTexture != (Object)null && encodedTexture != capturedTexture) { Object.Destroy((Object)(object)encodedTexture); } if ((Object)(object)capturedTexture != (Object)null) { Object.Destroy((Object)(object)capturedTexture); } } } private IEnumerator UploadCapturedMatchScreenshotIfNeeded(string source) { if (_matchScreenshotUploadCompleted || _matchScreenshotUploadInProgress || (string.IsNullOrWhiteSpace(_pendingUploadedScreenshotUrl) && (_pendingMatchScreenshotBytes == null || _pendingMatchScreenshotBytes.Length == 0)) || string.IsNullOrWhiteSpace(_currentMatchId)) { yield break; } _matchScreenshotUploadInProgress = true; try { float staggerDelay = GetCurrentPlayerScreenshotUploadDelaySeconds(); if (staggerDelay > 0f) { yield return (object)new WaitForSeconds(staggerDelay); } string existingScreenshotUrl = null; bool readSucceeded = false; yield return CallAPI("/match?id=eq." + _currentMatchId + "&select=*", "GET", "", delegate(string res) { readSucceeded = true; JObject val = ParseApiSingleObject(res); existingScreenshotUrl = (string)((val != null) ? val["screenshot_url"] : null); }); if (readSucceeded && !string.IsNullOrWhiteSpace(existingScreenshotUrl)) { _matchScreenshotUploadCompleted = true; ClearPendingMatchScreenshot(); Log("[Match Screenshot] Match " + _currentMatchId + " already has a screenshot - skipping duplicate upload (" + source + ")"); yield break; } if (string.IsNullOrWhiteSpace(_pendingUploadedScreenshotUrl)) { string uploadedFileUrl = null; yield return UploadMatchScreenshotFile(delegate(string url) { uploadedFileUrl = url; }); if (string.IsNullOrWhiteSpace(uploadedFileUrl)) { Log("[Match Screenshot] UploadFile did not return a file URL (" + source + ") - keeping screenshot for retry"); yield break; } _pendingUploadedScreenshotUrl = uploadedFileUrl; } JObject payload = new JObject { ["screenshot_url"] = JToken.op_Implicit(_pendingUploadedScreenshotUrl) }; bool uploadSucceeded = false; yield return CallAPI("/match?id=eq." + _currentMatchId, "PATCH", ((JToken)payload).ToString((Formatting)0, Array.Empty()), delegate(string res) { JObject val = ParseApiSingleObject(res); uploadSucceeded = val != null; }); if (uploadSucceeded) { _matchScreenshotUploadCompleted = true; ClearPendingMatchScreenshot(); Log("[Match Screenshot] ✓ Attached scoreboard screenshot to Match " + _currentMatchId + ""); ShowUploadNotification("Attached end-of-match screenshot for review."); } else { Log("[Match Screenshot] Screenshot upload did not succeed yet (" + source + ") - will keep the screenshot for fallback retry"); } } finally { _matchScreenshotUploadInProgress = false; } } private IEnumerator UploadMatchScreenshotFile(Action onUploaded) { try { int num = default(int); switch (num) { default: yield break; case 0: onUploaded?.Invoke(null); yield break; case 1: { UnityWebRequest req = default(UnityWebRequest); if ((int)req.result == 1) { JObject response = ParseApiSingleObject(req.downloadHandler.text); string text = (string)((response != null) ? response["file_url"] : null); if (text == null) { text = (string)((response != null) ? response["url"] : null); } string fileUrl = text; if (!string.IsNullOrWhiteSpace(fileUrl)) { Log("[Match Screenshot] ✓ Screenshot uploaded to file storage"); onUploaded?.Invoke(fileUrl); } else { Log("[Match Screenshot] UploadFile succeeded but response did not contain file_url"); onUploaded?.Invoke(null); } } else { string errorMsg = $"[Match Screenshot] UploadFile failed: {req.result}"; if (!string.IsNullOrEmpty(req.error)) { errorMsg = errorMsg + " - " + req.error; } if (req.responseCode > 0) { errorMsg += $" (HTTP {req.responseCode})"; } DownloadHandler downloadHandler = req.downloadHandler; if (!string.IsNullOrEmpty((downloadHandler != null) ? downloadHandler.text : null)) { int length = Math.Min(240, req.downloadHandler.text.Length); errorMsg = errorMsg + " - Response: " + req.downloadHandler.text.Substring(0, length); } Log("" + errorMsg + ""); onUploaded?.Invoke(null); } ((d__153)/*Error near IL_0291: stateMachine*/).<>m__Finally1(); break; } } } catch { //try-fault ((IDisposable)/*Error near IL_02af: stateMachine*/).Dispose(); throw; } ((IDisposable)/*Error near IL_02b7: stateMachine*/).Dispose(); /*Error near IL_02c1: Unexpected return in MoveNext()*/; } private string BuildMatchScreenshotFileName() { string text = ((!string.IsNullOrWhiteSpace(_currentMatchId)) ? _currentMatchId : DateTime.UtcNow.ToString("yyyyMMddHHmmss")); return "match-" + text + ".jpg"; } private string GetCoreIntegrationEndpointUrl(string functionName) { string baseApiUrl = GetBaseApiUrl(); int num = baseApiUrl.LastIndexOf("/entities", StringComparison.OrdinalIgnoreCase); string text = ((num >= 0) ? baseApiUrl.Substring(0, num) : baseApiUrl); return text + "/integration-endpoints/Core/" + functionName; } private void ApplyIntegrationHeaders(UnityWebRequest req) { if (req != null) { string authToken = GetAuthToken(); if (!string.IsNullOrWhiteSpace(authToken)) { req.SetRequestHeader("apikey", authToken); req.SetRequestHeader("Authorization", "Bearer " + authToken); } } } private void ClearPendingMatchScreenshot() { _pendingMatchScreenshotBytes = null; _pendingMatchScreenshotFileName = null; _pendingUploadedScreenshotUrl = null; } public void OnCourseRpcInformEndingCourse() { try { if (_courseEndRpcCoroutine == null) { _courseEndRpcCoroutine = ((MonoBehaviour)this).StartCoroutine(HandleCourseEndRpcCoroutine()); } } catch (Exception ex) { Log("[Match Signal] Error starting CourseEnd RPC handler: " + ex.Message + ""); } } private IEnumerator HandleCourseEndRpcCoroutine() { try { Log("[Match Signal] CourseManager client RPC InformEndingCourse received — preparing final upload"); for (int i = 0; i < 8; i++) { try { if (TryCaptureFinalLeaderboardSnapshot("CourseManager.RpcInformEndingCourse")) { break; } } catch { } yield return (object)new WaitForSeconds(0.125f); } yield return CaptureMatchScreenshotForReview("CourseManager.RpcInformEndingCourse (forced)"); float waited = 0f; while (string.IsNullOrWhiteSpace(_currentMatchId) && waited < 6f) { yield return (object)new WaitForSeconds(0.25f); waited += 0.25f; } yield return UploadCapturedMatchScreenshotIfNeeded("CourseManager.RpcInformEndingCourse"); } finally { _courseEndRpcCoroutine = null; } } private float GetCurrentPlayerScreenshotUploadDelaySeconds() { if (_currentSession?.player_ids == null || string.IsNullOrWhiteSpace(_userProfile?.id)) { return 0f; } List list = _currentSession.player_ids.Where((string id) => !string.IsNullOrWhiteSpace(id)).Distinct(StringComparer.Ordinal).OrderBy((string id) => id, StringComparer.Ordinal) .ToList(); if (list.Count == 0) { return 0f; } int num = list.FindIndex((string id) => string.Equals(id, _userProfile.id, StringComparison.Ordinal)); if (num <= 0) { return 0f; } float num2 = (float)num * 0.5f; Log($"[Match Screenshot] Upload slot {num + 1}/{list.Count}; waiting {num2:0.#}s before checking Match.screenshot_url"); return num2; } private Texture2D ResizeTextureToFit(Texture2D source, int maxWidth, int maxHeight) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || ((Texture)source).width <= 0 || ((Texture)source).height <= 0) { return source; } if (((Texture)source).width <= maxWidth && ((Texture)source).height <= maxHeight) { return source; } float num = Mathf.Min((float)maxWidth / (float)((Texture)source).width, (float)maxHeight / (float)((Texture)source).height); int num2 = Mathf.Max(1, Mathf.RoundToInt((float)((Texture)source).width * num)); int num3 = Mathf.Max(1, Mathf.RoundToInt((float)((Texture)source).height * num)); RenderTexture active = RenderTexture.active; RenderTexture temporary = RenderTexture.GetTemporary(num2, num3, 0, (RenderTextureFormat)0); try { Graphics.Blit((Texture)(object)source, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(num2, num3, (TextureFormat)3, false); val.ReadPixels(new Rect(0f, 0f, (float)num2, (float)num3), 0, 0, false); val.Apply(false, false); return val; } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } private byte[] EncodeScreenshotBytes(Texture2D texture) { if ((Object)(object)texture == (Object)null) { return null; } byte[] array = null; int[] array2 = new int[5] { 90, 80, 70, 60, 50 }; int[] array3 = array2; foreach (int num in array3) { byte[] array4 = ImageConversion.EncodeToJPG(texture, num); if (array4 != null && array4.Length != 0) { array = array4; if (array4.Length <= 819200) { return array4; } } } if (array != null && array.Length <= 1048576) { return array; } if (array != null) { Log($"[Match Screenshot] Best JPEG was still too large ({(float)array.Length / 1024f:0.#} KB) - skipping screenshot upload"); } return null; } private IEnumerator CheckAndSubmitMatchStats() { if (_userProfile == null || _currentSession == null) { Log("[Match Stats] Failed: Missing profile or session"); yield break; } Log("[Match Stats] Waiting for leaderboard to populate..."); yield return (object)new WaitForSeconds(1.5f); string fullUrl = GetBaseApiUrl() + "/match?matchmaking_session_id=eq." + _currentSession.id; Log("[Match Stats] Checking for existing match entry..."); Log("[Match Stats] Query URL: " + fullUrl.Substring(0, Math.Min(150, fullUrl.Length)) + "..."); UnityWebRequest req = UnityWebRequest.Get(fullUrl); try { ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { List existingMatches = ParseApiObjectList(req.downloadHandler.text); if (existingMatches.Count > 0) { Log($"[Match Stats] Match already exists for this session (found {existingMatches.Count} entries)"); _matchStatsSubmitted = true; yield break; } Log("[Match Stats] No existing match found - proceeding with submission"); } else { Log($"[Match Stats] Could not query existing matches: {req.result} - proceeding anyway"); } } finally { ((IDisposable)req)?.Dispose(); } yield return SubmitMatchStats(); } private IEnumerator ResolveExistingMatchIdForCurrentSession(Action onResolved, bool logMisses = true) { string resolvedId = null; string activeSessionId = ((_currentSession != null) ? _currentSession.id : _localManualSessionId); if (string.IsNullOrWhiteSpace(activeSessionId)) { onResolved?.Invoke(null); yield break; } if (!string.IsNullOrWhiteSpace(_currentSession?.match_id)) { resolvedId = _currentSession.match_id; if (!_finalizedMatchIds.Contains(resolvedId)) { Log("[Match Creation] Session already linked to Match ID " + resolvedId + ""); onResolved?.Invoke(resolvedId); yield break; } Log("[Match Creation] Session match_id " + resolvedId + " was already finalized — clearing link and forcing new match"); _currentSession.match_id = null; resolvedId = null; } string fullUrl = GetBaseApiUrl() + "/match?matchmaking_session_id=eq." + activeSessionId + "&order=created_at.asc"; UnityWebRequest req = UnityWebRequest.Get(fullUrl); try { ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { JObject existingMatch = SelectNewestReusableMatch(ParseApiObjectList(req.downloadHandler.text)); resolvedId = (string)((existingMatch != null) ? existingMatch["id"] : null); if (!string.IsNullOrWhiteSpace(resolvedId)) { if (_currentSession != null) { _currentSession.match_id = resolvedId; } Log("[Match Creation] ✓ Reusing newest active Match ID from API for session " + activeSessionId + ": " + resolvedId + ""); } else if (logMisses) { Log("[Match Creation] No existing Match found yet for session " + activeSessionId + ""); } } else { Log($"[Match Creation] Existing Match lookup failed: {req.result} - {req.error}"); } } finally { ((IDisposable)req)?.Dispose(); } onResolved?.Invoke(resolvedId); } private JObject SelectNewestReusableMatch(IEnumerable matches) { return (from match in matches?.Where((JObject match) => match != null).Where(delegate(JObject match) { string text = (string)match["id"]; return !string.IsNullOrWhiteSpace(text) && !_finalizedMatchIds.Contains(text); }) orderby ParseApiTimestamp((string)match["created_at"]) descending select match).FirstOrDefault(); } private static DateTime ParseApiTimestamp(string value) { if (DateTime.TryParse(value, null, DateTimeStyles.RoundtripKind, out var result)) { return result; } return DateTime.MinValue; } private float GetCurrentPlayerMatchUploadDelaySeconds() { if (_currentSession?.player_ids == null || string.IsNullOrWhiteSpace(_userProfile?.id)) { return 0f; } List list = _currentSession.player_ids.Where((string id) => !string.IsNullOrWhiteSpace(id)).Distinct(StringComparer.Ordinal).OrderBy((string id) => id, StringComparer.Ordinal) .ToList(); if (list.Count == 0) { return 0f; } int num = list.FindIndex((string id) => string.Equals(id, _userProfile.id, StringComparison.Ordinal)); if (num <= 0) { return 0f; } float num2 = (float)num * 2f; Log($"[Match Creation] Fallback upload slot {num + 1}/{list.Count}; waiting {num2:0.#}s before POST"); return num2; } private IEnumerator WaitForExistingMatchBeforeFallback(float delaySeconds, Action onResolved) { string activeSessionId = _currentSession?.id ?? _localManualSessionId; if (string.IsNullOrWhiteSpace(activeSessionId) || delaySeconds <= 0f) { onResolved?.Invoke(null); yield break; } for (float waited = 0f; waited < delaySeconds; waited += 1f) { string existingMatchId = null; yield return ResolveExistingMatchIdForCurrentSession(delegate(string id) { existingMatchId = id; }, logMisses: false); if (!string.IsNullOrEmpty(existingMatchId)) { onResolved?.Invoke(existingMatchId); yield break; } yield return (object)new WaitForSeconds(1f); } onResolved?.Invoke(null); } private string BuildStableManualLocalSessionId() { string currentSteamLobbyId = CompetitivePluginCheck.GetCurrentSteamLobbyId(); if (!string.IsNullOrWhiteSpace(currentSteamLobbyId)) { return "local-lobby-" + currentSteamLobbyId; } string text = ResolveCurrentLobbyName(); if (!string.IsNullOrWhiteSpace(text)) { string text2 = Regex.Replace(text.Trim(), "[^A-Za-z0-9_-]+", "-").Trim('-'); if (!string.IsNullOrWhiteSpace(text2)) { return "local-name-" + text2; } } return "local-manual"; } private IEnumerator ResolveExistingMatchEntryId(string matchId, string playerId, Action onResolved, bool logMisses = false) { string resolvedId = null; if (string.IsNullOrWhiteSpace(matchId) || string.IsNullOrWhiteSpace(playerId)) { onResolved?.Invoke(null); yield break; } yield return CallAPI("/match_entry?match_id=eq." + matchId + "&player_id=eq." + playerId + "&limit=1&order=created_at.asc", "GET", "", delegate(string res) { JObject val = ParseApiObjectList(res).FirstOrDefault(); if (val != null) { resolvedId = (string)val["id"]; } }); if (logMisses && string.IsNullOrWhiteSpace(resolvedId)) { Log("[Match Entry] No existing MatchEntry found yet for player " + playerId + ""); } onResolved?.Invoke(resolvedId); } private IEnumerator WaitForPendingMatchEntryCreation(string playerId, Action onResolved) { float waited = 0f; while (!string.IsNullOrWhiteSpace(playerId) && _matchEntryCreationInProgress.Contains(playerId) && waited < 3f) { if (_playerMatchEntryIds.TryGetValue(playerId, out var entryId) && !string.IsNullOrWhiteSpace(entryId)) { onResolved?.Invoke(entryId); yield break; } yield return (object)new WaitForSeconds(0.25f); waited += 0.25f; entryId = null; } if (!string.IsNullOrWhiteSpace(playerId) && _playerMatchEntryIds.TryGetValue(playerId, out var resolvedEntryId) && !string.IsNullOrWhiteSpace(resolvedEntryId)) { onResolved?.Invoke(resolvedEntryId); } else { onResolved?.Invoke(null); } } private IEnumerator EnsureMatchEntryForPlayer(string sourceTag, string playerId, string playerName, string preMatchMmr, string postMatchMmr, int gamePoints, int scoreVsPar, int finishPosition, string notes, Action onResolved) { if (string.IsNullOrWhiteSpace(_currentMatchId) || string.IsNullOrWhiteSpace(playerId)) { onResolved?.Invoke(null); yield break; } if (_playerMatchEntryIds.TryGetValue(playerId, out var cachedEntryId) && !string.IsNullOrWhiteSpace(cachedEntryId)) { onResolved?.Invoke(cachedEntryId); yield break; } if (_matchEntryCreationInProgress.Contains(playerId)) { yield return WaitForPendingMatchEntryCreation(playerId, onResolved); yield break; } _matchEntryCreationInProgress.Add(playerId); try { string existingEntryId = null; yield return ResolveExistingMatchEntryId(_currentMatchId, playerId, delegate(string id) { existingEntryId = id; }); string resolvedPlayerName = (string.IsNullOrWhiteSpace(playerName) ? playerId : playerName.Trim()); if (!string.IsNullOrWhiteSpace(existingEntryId)) { _playerMatchEntryIds[playerId] = existingEntryId; if (!string.IsNullOrWhiteSpace(resolvedPlayerName)) { _playerIdsByName[resolvedPlayerName] = playerId; } Log("[" + sourceTag + "] ✓ Adopted existing MatchEntry for " + resolvedPlayerName + ": " + existingEntryId + ""); onResolved?.Invoke(existingEntryId); yield break; } int adjustedScore = gamePoints + scoreVsPar * -10; if (gamePoints == 0) { Log("[" + sourceTag + "] Skipping MatchEntry POST for " + resolvedPlayerName + " — score is 0"); onResolved?.Invoke(null); yield break; } JObject payload = new JObject { ["id"] = JToken.op_Implicit(Guid.NewGuid().ToString()), ["match_id"] = JToken.op_Implicit(_currentMatchId), ["player_id"] = JToken.op_Implicit(playerId), ["player_name"] = JToken.op_Implicit(resolvedPlayerName), ["game_points"] = JToken.op_Implicit(gamePoints), ["over_under"] = JToken.op_Implicit(scoreVsPar), ["score_vs_par"] = JToken.op_Implicit(scoreVsPar), ["adjusted_match_score"] = JToken.op_Implicit(adjustedScore), ["notes"] = JToken.op_Implicit(notes ?? string.Empty) }; float preMmr = 0f; float postMmr = 0f; if (!string.IsNullOrWhiteSpace(preMatchMmr) && float.TryParse(preMatchMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out preMmr)) { payload["pre_match_mmr"] = JToken.op_Implicit(preMmr); } if (!string.IsNullOrWhiteSpace(postMatchMmr) && float.TryParse(postMatchMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out postMmr)) { payload["post_match_mmr"] = JToken.op_Implicit(postMmr); payload["mmr_change"] = JToken.op_Implicit(postMmr - preMmr); } if (finishPosition > 0) { payload["finish_position"] = JToken.op_Implicit(finishPosition); } string createdEntryId = null; yield return CallAPI("/match_entry", "POST", ((JToken)payload).ToString((Formatting)0, Array.Empty()), delegate(string res) { try { JObject val = ParseApiSingleObject(res); if (val != null) { createdEntryId = (string)val["id"]; } } catch (Exception ex) { Log("[" + sourceTag + "] Could not parse MatchEntry response: " + ex.Message + ""); } }); if (!string.IsNullOrWhiteSpace(createdEntryId)) { _playerMatchEntryIds[playerId] = createdEntryId; if (!string.IsNullOrWhiteSpace(resolvedPlayerName)) { _playerIdsByName[resolvedPlayerName] = playerId; } Log("[" + sourceTag + "] ✓ MatchEntry created for " + resolvedPlayerName + ": " + createdEntryId + ""); } onResolved?.Invoke(createdEntryId); } finally { _matchEntryCreationInProgress.Remove(playerId); } } private IEnumerator FinalizeAndResetAfterDrivingRange() { if (!_matchStatsSubmitted && !string.IsNullOrEmpty(_currentMatchId)) { yield return FinalizeMatchStats(); } yield return UploadCapturedMatchScreenshotIfNeeded("Driving Range fallback"); if (IsRankedTriggered && _currentSession != null) { yield return UpdateSessionStatus("completed"); } if (!string.IsNullOrWhiteSpace(_currentMatchId)) { _finalizedMatchIds.Add(_currentMatchId); } ResetPerMatchState(); } private void ResetPerMatchState() { if (_endOfMatchSignalCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_endOfMatchSignalCoroutine); _endOfMatchSignalCoroutine = null; } if (_matchScreenshotSignalCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_matchScreenshotSignalCoroutine); _matchScreenshotSignalCoroutine = null; } _currentMatchId = null; if (_currentSession != null) { _currentSession.match_id = null; } _playerMatchEntryIds.Clear(); _matchEntryCreationInProgress.Clear(); _playerIdsByName.Clear(); _lastSubmittedScores.Clear(); _lastSubmittedScoresVsPar.Clear(); _matchEntriesCreated = false; _matchStatsSubmitted = false; _proSeriesSkipLogged = false; _matchEndedReceived = false; ClearPendingMatchScreenshot(); _matchScreenshotUploadInProgress = false; _matchScreenshotUploadCompleted = false; _localManualSessionId = null; _cachedLeaderboardScores.Clear(); _cachedLeaderboardScoresVsPar.Clear(); _finalLeaderboardSnapshot.Clear(); MatchResultSubmissionService.ReceivedP2PMatchId = null; _lastUploadedPlayerCount = -1; Log("[Match] Per-match state reset - ready for new round"); } private void ResetPluginState() { if (_monitorCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_monitorCoroutine); _monitorCoroutine = null; } if (_endOfMatchSignalCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_endOfMatchSignalCoroutine); _endOfMatchSignalCoroutine = null; } if (_matchScreenshotSignalCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_matchScreenshotSignalCoroutine); _matchScreenshotSignalCoroutine = null; } IsRankedTriggered = false; _isQueueing = false; _webStatus = "IDLE"; _currentQueueId = ""; _currentSession = null; _isHost = false; _hasAccepted = false; _hostRulesetSelection = "ranked"; _queueStartTime = null; _hostLobbyStarted = false; _hostServerWasActive = false; _hostCancelSent = false; _matchStatsSubmitted = false; _proSeriesSkipLogged = false; _matchStartTime = null; _syncTickCount = 0; _lobbyCreatedEventCount = 0; _steamLinkUploadAttempts = 0; _steamLinkUploadSuccesses = 0; _steamLinkUploadFailures = 0; _autoJoinAttempts = 0; _autoJoinSuccesses = 0; _autoJoinFailures = 0; _lastLobbyCreatedAt = null; _lastUploadAttemptAt = null; _lastUploadSuccessAt = null; _lastAutoJoinAttemptAt = null; _lastAutoJoinSuccessAt = null; _lastGeneratedSteamLink = "-"; _lastUploadedSteamLink = "-"; _lastAutoJoinSteamLink = "-"; _lastUploadError = "-"; _lastAutoJoinError = "-"; if (_lobbyMonitorCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_lobbyMonitorCoroutine); _lobbyMonitorCoroutine = null; } _currentMatchId = null; _finalizedMatchIds.Clear(); _playerMatchEntryIds.Clear(); _matchEntryCreationInProgress.Clear(); _playerIdsByName.Clear(); _lastSubmittedScores.Clear(); _lastSubmittedScoresVsPar.Clear(); _matchCreationInProgress = false; _matchEntriesCreated = false; _isInGameplay = false; _matchEndedReceived = false; ClearPendingMatchScreenshot(); _matchScreenshotUploadInProgress = false; _matchScreenshotUploadCompleted = false; _localManualSessionId = null; _cachedLeaderboardScores.Clear(); _cachedLeaderboardScoresVsPar.Clear(); _finalLeaderboardSnapshot.Clear(); _lastUploadedPlayerCount = -1; _nextEnsureMatchCreateAttemptAt = 0f; } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; CourseManager.MatchStateChanged -= OnCourseManagerMatchStateChanged; CourseManager.ForceDisplayScoreboardChanged -= OnCourseManagerForceDisplayScoreboardChanged; UnifiedPlugin.ApiConfigChanged -= OnApiConfigChanged; } private void OnApplicationQuit() { if (_isHost && _hostLobbyStarted && _hostServerWasActive && _currentSession != null) { ((MonoBehaviour)this).StartCoroutine(CancelSessionAsHost("host_application_quit")); } } private void Log(string msg) { string item = $"[{DateTime.Now:HH:mm:ss}] {msg}"; _debugLogs.Add(item); if (_debugLogs.Count > 30) { _debugLogs.RemoveAt(0); } _logScroll.y = float.MaxValue; if (_bepinexLogger != null) { string text = Regex.Replace(msg, "<[^>]+>", ""); _bepinexLogger.LogInfo((object)("[MatchmakingAssistant] " + text)); } } private bool TryUseSharedProfile(string logContext) { SharedPlayerProfile playerProfile = UnifiedPlugin.GetPlayerProfile(); if (playerProfile == null || !UnifiedPlugin.IsPlayerProfileResolved() || string.IsNullOrWhiteSpace(playerProfile.ID)) { return false; } _userProfile = new PlayerProfile { id = playerProfile.ID, display_name = playerProfile.DisplayName, current_mmr = playerProfile.CurrentMMR, region = playerProfile.Region, state_province = string.Empty }; Log("[" + logContext + "] ✓ Using shared profile: " + _userProfile.display_name + " (" + _userProfile.id + ")"); return true; } private string TryGetSteamPlayerName() { try { if (SteamClient.IsValid) { return SteamClient.Name; } } catch { } return string.Empty; } private IEnumerator EnsureUserProfileResolved(string logContext, float maxWaitSeconds = 0f) { if (_userProfile != null) { yield break; } bool attemptedSteamLookup = false; float waited = 0f; while (_userProfile == null && !TryUseSharedProfile(logContext)) { if (!attemptedSteamLookup) { string steamName = TryGetSteamPlayerName(); if (!string.IsNullOrWhiteSpace(steamName) && steamName != "Player") { attemptedSteamLookup = true; Log("[" + logContext + "] Attempting local profile resolution for '" + steamName + "'"); yield return ResolveProfile(steamName); if (_userProfile != null || TryUseSharedProfile(logContext)) { break; } } } if (waited >= maxWaitSeconds) { break; } yield return (object)new WaitForSeconds(0.5f); waited += 0.5f; } } private IEnumerator BackgroundSyncLoop() { int retryCount = 0; while (true) { _syncTickCount++; if (_userProfile == null) { yield return EnsureUserProfileResolved("Sync"); if (_userProfile != null) { retryCount = 0; } else { retryCount++; if (retryCount == 1) { Log($"Waiting for player profile resolution... (Attempt {retryCount}/{10})"); } if (retryCount % 3 == 0) { Log($"Still waiting for player profile... (Attempt {retryCount}/{10})"); } if (retryCount >= 10) { break; } } } if (_userProfile != null) { if (!_activeSeasonFetched) { yield return FetchActiveSeasonId(); } Scene activeScene = SceneManager.GetActiveScene(); bool isInMenuScene = ((Scene)(ref activeScene)).name.ToLower().Contains("menu"); if (!_isQueueing && _currentSession == null && !_isInGameplay) { yield return CheckExistingQueueEntry(); } if (isInMenuScene && !IsRankedTriggered && !_isInGameplay) { yield return RefreshPlayerList(); } if (_isInitializing) { _isInitializing = false; Log("Initialization Complete: Queue Unlocked."); } if (_isQueueing && _currentSession == null) { yield return CheckForMatch(); } if (_currentSession != null && _currentSession.status == "pending_accept") { yield return PollSessionStatus(); } if (_isHost && _hostLobbyStarted && _currentSession != null && !_hostCancelSent) { if (NetworkServer.active) { _hostServerWasActive = true; } else if (_hostServerWasActive && _currentSession.status != "completed") { Log("Host lobby appears closed. Marking MatchmakingSession as completed..."); yield return CancelSessionAsHost("host_left_lobby"); } } if (_currentSession != null && !_isHost && _currentSession.status == "ready" && !string.IsNullOrEmpty(_currentSession.steam_lobby_link) && !_hasAccepted) { Log("Auto-joining match as non-host..."); yield return AutoJoinMatch(); } } yield return _syncLoopDelay; } Log("Player profile resolution timed out. Background sync will stop until the next reload."); } private IEnumerator RefreshPlayerList() { string fullUrl = GetBaseApiUrl() + "/matchmaking_queue?status=in.(queued,matched)"; Log("[Sync] GET /matchmaking_queue (fetching queued players)"); Log("[Sync] Full URL: " + fullUrl.Substring(0, Math.Min(150, fullUrl.Length)) + "..."); UnityWebRequest req = UnityWebRequest.Get(fullUrl); try { ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { string rawJson = req.downloadHandler.text; List queueEntries = ParseApiObjectList(rawJson); List queuedPlayers = new List(queueEntries.Count); JObject myEntry = null; string myStatus = null; int countQueued = 0; int countMatched = 0; foreach (JObject entry in queueEntries) { string status = (string)entry["status"]; if (string.Equals(status, "queued", StringComparison.OrdinalIgnoreCase)) { countQueued++; queuedPlayers.Add(new PlayerData { name = (((string)entry["display_name"]) ?? ((string)entry["user_id"]) ?? "Unknown"), mmr = (((object)entry["mmr_snapshot"])?.ToString() ?? "0") }); } else if (string.Equals(status, "matched", StringComparison.OrdinalIgnoreCase)) { countMatched++; } if (_userProfile != null && myEntry == null && string.Equals((string)entry["player_id"], _userProfile.id, StringComparison.Ordinal)) { myEntry = entry; myStatus = status; } } _queuedPlayers = queuedPlayers; _onlineCount = countQueued + countMatched; _queuedCount = countQueued; _matchedCount = countMatched; if (_userProfile == null || myEntry == null) { yield break; } string status2 = myStatus; if (status2 == "queued" && !_isQueueing) { _isQueueing = true; _currentQueueId = ((string)myEntry["id"]) ?? _currentQueueId; _webStatus = "QUEUED (SYNCED)"; string serverTime = (string)myEntry["queued_at"]; if (DateTime.TryParse(serverTime, null, DateTimeStyles.RoundtripKind, out var parsedTime)) { _queueStartTime = parsedTime.ToUniversalTime(); } Log("Sync: Found active session on server. Reconnecting UI..."); } else if (status2 == "matched" && _currentSession == null) { _webStatus = "MATCH FOUND (SYNCED)"; yield return CheckForMatch(); } yield break; } Log($"[Sync] RefreshPlayerList failed: {req.result} - {req.error}"); } finally { ((IDisposable)req)?.Dispose(); } } private IEnumerator CheckExistingQueueEntry() { if (_userProfile == null) { yield break; } string checkUrl = GetBaseApiUrl() + "/matchmaking_queue?player_id=eq." + _userProfile.id + "&status=eq.queued"; UnityWebRequest req = UnityWebRequest.Get(checkUrl); try { ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result != 1) { yield break; } List existing = ParseApiObjectList(req.downloadHandler.text); if (existing == null || existing.Count <= 0) { yield break; } _currentQueueId = (string)existing[0]["id"]; _isQueueing = true; _webStatus = "QUEUED"; _queueStartTime = DateTime.Now; Log("[Queue] Detected existing queue entry " + _currentQueueId + " from website — rejoining."); yield return CallAPI(json: "{\"has_mod\":true,\"created_by\":\"SBGL_UnifiedMod\",\"match_type\":\"" + GetQueueMatchTypePayload() + "\"}", endpoint: "/matchmaking_queue?id=eq." + _currentQueueId, method: "PATCH", onSuccess: delegate(string res) { if (ParseApiSingleObject(res) != null) { Log("[Queue] ✓ Patched has_mod:true onto existing entry " + _currentQueueId + "."); } }); } finally { ((IDisposable)req)?.Dispose(); } } private IEnumerator MatchmakingLoop() { if (_userProfile == null) { Log("No profile loaded."); yield break; } _isQueueing = true; _webStatus = "JOINING..."; _queueStartTime = DateTime.Now; string existingId = null; string checkUrl = GetBaseApiUrl() + "/matchmaking_queue?player_id=eq." + _userProfile.id + "&status=eq.queued"; Log("[Queue] Checking for existing entry for player " + _userProfile.id + "..."); UnityWebRequest checkReq = UnityWebRequest.Get(checkUrl); try { ApplyApiHeaders(checkReq); yield return checkReq.SendWebRequest(); if ((int)checkReq.result == 1) { List existing = ParseApiObjectList(checkReq.downloadHandler.text); if (existing != null && existing.Count > 0) { existingId = (string)existing[0]["id"]; string foundUserId = (string)existing[0]["user_id"]; if (!string.IsNullOrEmpty(foundUserId)) { PlayerPrefs.SetString("SBGLUserAuthUUID", foundUserId); PlayerPrefs.Save(); } Log("[Queue] Found existing entry " + existingId + " — patching has_mod:true"); } else { Log("[Queue] No existing entry found — will create new."); } } else { Log($"[Queue] Could not check existing entries: {checkReq.result} — will attempt POST anyway."); } } finally { ((IDisposable)checkReq)?.Dispose(); } if (existingId != null) { yield return CallAPI(json: "{\"has_mod\":true,\"created_by\":\"SBGL_UnifiedMod\",\"match_type\":\"" + GetQueueMatchTypePayload() + "\"," + $"\"queued_at\":\"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ss.fffZ}\"" + "}", endpoint: "/matchmaking_queue?id=eq." + existingId, method: "PATCH", onSuccess: delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { _currentQueueId = existingId; _webStatus = "QUEUED"; Log("[Queue] ✓ Updated existing entry " + existingId + " with has_mod:true."); } else { Log("[Queue] Failed to update existing entry " + existingId + "."); } }); yield break; } string json = "{" + $"\"id\":\"{Guid.NewGuid()}\"," + "\"user_id\":\"" + GetOrDeriveUserUUID() + "\",\"player_id\":\"" + _userProfile.id + "\"," + $"\"mmr_snapshot\":{_userProfile.current_mmr}," + "\"region\":\"" + _userProfile.region + "\",\"state_province\":\"" + _userProfile.state_province + "\",\"match_type\":\"" + GetQueueMatchTypePayload() + "\",\"session_id\":\"\",\"has_mod\":true,\"created_by\":\"SBGL_UnifiedMod\",\"status\":\"queued\"," + $"\"queued_at\":\"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ss.fffZ}\"" + "}"; yield return CallAPI("/matchmaking_queue", "POST", json, delegate(string res) { JObject val = ParseApiSingleObject(res); _currentQueueId = ((string)((val != null) ? val["id"] : null)) ?? _currentQueueId; _webStatus = "QUEUED"; Log("[Queue] ✓ Created new entry " + _currentQueueId + " with has_mod:true."); }); } private IEnumerator LeaveQueue() { if (string.IsNullOrEmpty(_currentQueueId)) { ResetPluginState(); yield break; } _webStatus = "LEAVING..."; string json = "{\"player_id\":\"" + _userProfile.id + "\",\"status\":\"cancelled\"}"; bool leaveSuccess = false; yield return CallAPI("/matchmaking_queue?id=eq." + _currentQueueId, "PATCH", json, delegate(string res) { if (ParseApiSingleObject(res) != null) { ResetPluginState(); Log("✓ Left Queue."); leaveSuccess = true; } }); if (!leaveSuccess) { Log("Leave queue request failed. Check logs above for details."); ResetPluginState(); } } private IEnumerator CheckForMatch() { Log("[Sync] GET /matchmaking_session (checking for pending matches)"); JObject activeSession = null; string urlHost = GetBaseApiUrl() + "/matchmaking_session?host_player_id=eq." + _userProfile.id + "&status=eq.pending_accept"; Log("[Sync] Full URL: " + urlHost.Substring(0, Math.Min(150, urlHost.Length)) + "..."); UnityWebRequest req = UnityWebRequest.Get(urlHost); try { ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { List sessions = ParseApiObjectList(req.downloadHandler.text); activeSession = ((IEnumerable)sessions).FirstOrDefault((Func)((JObject s) => !string.IsNullOrEmpty((string)s["lobby_name"]))); } else { SBGLPlugin sBGLPlugin = this; object arg = req.result; string error = req.error; DownloadHandler downloadHandler = req.downloadHandler; sBGLPlugin.Log($"[Sync] CheckForMatch (host) failed: {arg} - {error} | body: {((downloadHandler != null) ? downloadHandler.text : null)}"); } } finally { ((IDisposable)req)?.Dispose(); } if (activeSession == null) { string urlPlayer = string.Concat(str2: "%5B%22" + _userProfile.id + "%22%5D", str0: GetBaseApiUrl(), str1: "/matchmaking_session?player_ids=cs.", str3: "&status=eq.pending_accept"); Log("[Sync] Full URL: " + urlPlayer.Substring(0, Math.Min(150, urlPlayer.Length)) + "..."); UnityWebRequest req2 = UnityWebRequest.Get(urlPlayer); try { ApplyApiHeaders(req2); yield return req2.SendWebRequest(); if ((int)req2.result == 1) { List sessions2 = ParseApiObjectList(req2.downloadHandler.text); activeSession = ((IEnumerable)sessions2).FirstOrDefault((Func)((JObject s) => !string.IsNullOrEmpty((string)s["lobby_name"]))); } else { SBGLPlugin sBGLPlugin2 = this; object arg2 = req2.result; string error2 = req2.error; DownloadHandler downloadHandler2 = req2.downloadHandler; sBGLPlugin2.Log($"[Sync] CheckForMatch (player) failed: {arg2} - {error2} | body: {((downloadHandler2 != null) ? downloadHandler2.text : null)}"); } } finally { ((IDisposable)req2)?.Dispose(); } } if (activeSession != null) { _currentSession = ParseSessionFromJson(activeSession); _isHost = string.Equals(_currentSession?.host_player_id, _userProfile?.id, StringComparison.Ordinal); _webStatus = "MATCH FOUND: PENDING"; Log($"[CheckForMatch] Session {_currentSession?.id} found. Host: {_isHost}"); } } private IEnumerator PollSessionStatus() { string fullUrl = GetBaseApiUrl() + "/matchmaking_session?id=eq." + _currentSession.id + "&select=*"; Log("[Sync] GET /matchmaking_session/" + _currentSession.id + " (polling status)"); Log("[Sync] Full URL: " + fullUrl + ""); UnityWebRequest req = UnityWebRequest.Get(fullUrl); try { ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { string raw = req.downloadHandler.text; JObject session = ParseApiSingleObject(raw); if (session == null) { yield break; } string statusFromApi = (string)session["status"]; _currentSession.status = statusFromApi ?? _currentSession.status; Log("[Sync] Status from API: " + statusFromApi + ""); string acceptedIdsRaw = ((object)session["accepted_player_ids"])?.ToString(); if (!string.IsNullOrEmpty(acceptedIdsRaw)) { List acceptedIds = ParseTextJsonArray(acceptedIdsRaw); _currentSession.accepted_player_ids = acceptedIds; Log($"[Sync] Updated accepted_player_ids: {acceptedIds.Count} players have accepted"); if (_currentSession.status == "pending_accept" && _currentSession.player_ids != null && _currentSession.accepted_player_ids.Count == _currentSession.player_ids.Count) { Log($"[Sync] All {_currentSession.player_ids.Count} players have accepted! Transitioning to ready..."); _currentSession.status = "ready"; _webStatus = (_isHost ? "READY: HOST" : "READY: JOIN"); string readyJson = "{\"status\":\"ready\"}"; yield return CallAPI("/matchmaking_session?id=eq." + _currentSession.id, "PATCH", readyJson, delegate { Log("[Sync] ✓ Session transitioned to READY"); }); } } string latestSteamLobbyLink = (string)session["steam_lobby_link"]; if (!string.IsNullOrEmpty(latestSteamLobbyLink) && latestSteamLobbyLink != _currentSession.steam_lobby_link) { _currentSession.steam_lobby_link = latestSteamLobbyLink; Log("Sync: steam_lobby_link received/updated from API."); } string latestMatchId = (string)session["match_id"]; if (!string.IsNullOrEmpty(latestMatchId) && latestMatchId != _currentSession.match_id) { _currentSession.match_id = latestMatchId; Log("Sync: match_id received/updated from API: " + latestMatchId); } if (_currentSession.status == "ready") { _webStatus = (_isHost ? "READY: HOST" : "READY: JOIN"); Log("[Sync] Session is READY - Host should start game"); } else if (_currentSession.status == "cancelled" || _currentSession.status == "completed") { Log("Match ended with status: " + _currentSession.status + "."); ResetPluginState(); } } else { Log($"[Sync] PollSessionStatus failed: {req.result} - {req.error}"); } } finally { ((IDisposable)req)?.Dispose(); } } private IEnumerator AcceptMatch() { if (_currentSession != null) { _hasAccepted = true; Log("[Accept] Refreshing session data before accepting..."); yield return PollSessionStatus(); List acceptedIds = new List(); if (_currentSession.accepted_player_ids != null) { acceptedIds.AddRange(_currentSession.accepted_player_ids); } if (!acceptedIds.Contains(_userProfile.id)) { acceptedIds.Add(_userProfile.id); } string innerArray = ((JToken)JArray.FromObject((object)acceptedIds)).ToString((Formatting)0, Array.Empty()); JObject acceptPayload = new JObject { ["accepted_player_ids"] = JToken.op_Implicit(innerArray) }; string acceptJson = ((JToken)acceptPayload).ToString((Formatting)0, Array.Empty()); Log("[Accept] Current player accepting: " + _userProfile.id + ""); Log($"[Accept] Total accepted players: {acceptedIds.Count}"); Log("[Accept] JSON payload: " + acceptJson + ""); _currentSession.accepted_player_ids = new List(acceptedIds); yield return CallAPI("/matchmaking_session?id=eq." + _currentSession.id, "PATCH", acceptJson, delegate { Log("✓ Match Accepted. Waiting for all players to accept before transitioning to ready..."); }); } } private IEnumerator AutoJoinMatch() { if (_currentSession != null && !string.IsNullOrEmpty(_currentSession.steam_lobby_link)) { _autoJoinAttempts++; _lastAutoJoinAttemptAt = DateTime.Now; _lastAutoJoinSteamLink = _currentSession.steam_lobby_link; _hasAccepted = true; Log("Attempting automatic join with link: " + _currentSession.steam_lobby_link); JoinBySteamLink(_currentSession.steam_lobby_link, _currentSession.lobby_password); } yield break; } private IEnumerator TransitionToReady() { yield return _readyTransitionDelay; } private void StoreMatchConfigurationInPlayerPrefs(MatchmakingSession session) { try { if (session == null) { Log("[Config] Session is null, cannot store configuration"); return; } string matchType = session.match_type ?? string.Empty; bool flag = IsCasualMatchType(matchType); bool flag2 = IsProSeriesMatchType(matchType); string text = (flag ? "casual" : (flag2 ? "pro_series_season_2" : "ranked_season_2")); int num = ((!flag) ? ((session.season > 0) ? session.season : 2) : 0); string text2 = (flag ? "casual" : (flag2 ? "pro_series" : "ranked")); if (flag) { string text3 = ((!string.IsNullOrWhiteSpace(session.selected_course)) ? session.selected_course : PlayerPrefs.GetString("SelectedCourse", "")); PlayerPrefs.SetString("MatchType", text); if (!string.IsNullOrWhiteSpace(text3)) { PlayerPrefs.SetString("SelectedCourse", text3); } PlayerPrefs.SetInt("Season", num); PlayerPrefs.SetString("HostRuleset", text2); PlayerPrefs.Save(); Log(string.Format("[Config] Stored casual match configuration: Type={0}, Course={1}, Season={2}", text, string.IsNullOrWhiteSpace(text3) ? "manual" : text3, num)); return; } try { RuleSetManager.SetLogger(_bepinexLogger); string text4 = ((!string.IsNullOrEmpty(session.selected_course) && RuleSetManager.ValidateCourseForRanked(session.selected_course)) ? session.selected_course : MapPoolConfig.GetRandomApprovedCourse().Name); PlayerPrefs.SetString("MatchType", text); PlayerPrefs.SetString("SelectedCourse", text4); PlayerPrefs.SetInt("Season", num); PlayerPrefs.SetString("HostRuleset", text2); PlayerPrefs.Save(); Log($"[Config] Stored match configuration: Type={text}, Course={text4}, Season={num}"); RuleSetManager.LogMatchConfiguration(text, text4, num); } catch (Exception ex) { Log("[Config] Error during rule validation, storing fallback config: " + ex.Message + ""); PlayerPrefs.SetString("MatchType", text); PlayerPrefs.SetString("SelectedCourse", string.IsNullOrEmpty(session.selected_course) ? MapPoolConfig.GetRandomApprovedCourse().Name : session.selected_course); PlayerPrefs.SetInt("Season", num); PlayerPrefs.SetString("HostRuleset", text2); PlayerPrefs.Save(); } } catch (Exception ex2) { Log("[Config] Exception storing match configuration: " + ex2.Message + " | StackTrace: " + ex2.StackTrace + ""); } } private void ClearMatchConfigurationFromPlayerPrefs() { PlayerPrefs.DeleteKey("MatchType"); PlayerPrefs.DeleteKey("SelectedCourse"); PlayerPrefs.DeleteKey("Season"); PlayerPrefs.Save(); Log("[Config] Cleared match configuration from PlayerPrefs"); } private void InitiateHostSequence() { if (_currentSession == null) { return; } if (!_hasAccepted) { Log("[Host] Must accept match first before initializing lobby"); ((MonoBehaviour)this).StartCoroutine(AcceptMatch()); return; } string a = PlayerPrefs.GetString("HostRuleset", ""); if (string.Equals(a, "pro_series", StringComparison.OrdinalIgnoreCase)) { _hostRulesetSelection = "pro_series"; } else if (string.Equals(a, "casual", StringComparison.OrdinalIgnoreCase)) { _hostRulesetSelection = "casual"; } else { _hostRulesetSelection = "ranked"; } MainMenu val = Object.FindAnyObjectByType(); if ((Object)(object)val != (Object)null) { IsRankedTriggered = true; _hostLobbyStarted = true; _hostServerWasActive = false; _hostCancelSent = false; _matchStartTime = DateTime.UtcNow; _matchStatsSubmitted = false; PlayerPrefs.SetString("LobbyName", _currentSession.lobby_name); PlayerPrefs.SetString("LobbyPassword", _currentSession.lobby_password); PlayerPrefs.SetString("HostRuleset", _hostRulesetSelection); PlayerPrefs.Save(); Log("Host ruleset stored: " + _hostRulesetSelection); val.StartHost(); Log("Host lobby initiated. Waiting for Steamworks lobby creation callback..."); ((MonoBehaviour)this).StartCoroutine(UpdateSessionStatus("in_progress")); } } private IEnumerator CancelSessionAsHost(string reason) { if (_hostCancelSent || !_isHost || _currentSession == null || string.IsNullOrEmpty(_currentSession.id) || _currentSession.id == "DEBUG") { yield break; } _hostCancelSent = true; string sessionId = _currentSession.id; string json = "{\"status\":\"completed\"}"; UnityWebRequest req = new UnityWebRequest(GetBaseApiUrl() + "/matchmaking_session?id=eq." + sessionId, "PATCH"); try { req.uploadHandler = (UploadHandler)new UploadHandlerRaw(Encoding.UTF8.GetBytes(json)); req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { if (_currentSession != null && _currentSession.id == sessionId) { _currentSession.status = "completed"; } Log("Host leave update sent (" + reason + "): session marked completed."); } else { _hostCancelSent = false; Log($"Host cancel failed ({reason}): {req.result} {req.error}"); } } finally { ((IDisposable)req)?.Dispose(); } } private IEnumerator UploadSteamLobbyLink(string steamLink) { if (_currentSession == null || string.IsNullOrEmpty(steamLink)) { yield break; } if (string.Equals(_currentSession.steam_lobby_link, steamLink, StringComparison.Ordinal)) { Log("Steam Lobby Link already synced. Skipping redundant upload."); yield break; } _steamLinkUploadAttempts++; _lastUploadAttemptAt = DateTime.Now; _lastGeneratedSteamLink = steamLink; Log("Uploading Steam Lobby Link to API..."); string json = "{\"steam_lobby_link\":\"" + steamLink + "\"}"; UnityWebRequest req = new UnityWebRequest(GetBaseApiUrl() + "/matchmaking_session?id=eq." + _currentSession.id, "PATCH"); try { req.uploadHandler = (UploadHandler)new UploadHandlerRaw(Encoding.UTF8.GetBytes(json)); req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { _steamLinkUploadSuccesses++; _lastUploadSuccessAt = DateTime.Now; _lastUploadedSteamLink = steamLink; _currentSession.steam_lobby_link = steamLink; _lastUploadError = "-"; Log("Steam Lobby Link uploaded successfully."); } else { _steamLinkUploadFailures++; _lastUploadError = $"{req.result}: {req.error}"; Log("Steam Lobby Link upload failed: " + _lastUploadError + ""); } } finally { ((IDisposable)req)?.Dispose(); } } private IEnumerator CreateMatchAndEntries() { if (_matchCreationInProgress) { Log("[Match Creation] Already in progress - skipping duplicate call"); yield break; } _matchCreationInProgress = true; yield return CreateMatchAndEntriesInternal(); _matchCreationInProgress = false; } private IEnumerator CreateMatchAndEntriesInternal() { if (_userProfile == null) { Log("[Match Creation] Failed: Missing player profile"); yield break; } if (_currentSession == null) { if (string.IsNullOrWhiteSpace(_localManualSessionId)) { _localManualSessionId = BuildStableManualLocalSessionId(); } Log("[Match Creation] Manual local lobby detected. Session surrogate: " + _localManualSessionId + ""); } string currentMatchType = _currentSession?.match_type ?? PlayerPrefs.GetString("MatchType", ""); bool isCasualMatch = IsCurrentMatchCasual(); if (IsProSeriesMatchType(currentMatchType) || IsProSeriesMatchType(PlayerPrefs.GetString("MatchType", ""))) { if (!_proSeriesSkipLogged) { Log("[Match] Pro Series match — automated upload skipped"); _proSeriesSkipLogged = true; } _matchEntriesCreated = true; yield break; } string activeSessionId = ((_currentSession != null) ? _currentSession.id : _localManualSessionId); Log("[Match Creation] Starting new match for session " + activeSessionId + ""); _matchStartTime = DateTime.UtcNow; Dictionary playerScores = new Dictionary(); Dictionary playerScoresVsPar = new Dictionary(); List startingLeaderboard = null; try { LiveLeaderboardPlugin liveLeaderboard = Object.FindAnyObjectByType((FindObjectsInactive)1); if ((Object)(object)liveLeaderboard != (Object)null) { List allLeaderboardPlayers = liveLeaderboard.GetCurrentLeaderboard(); startingLeaderboard = new List(allLeaderboardPlayers); Log($"[Match Creation] Found {allLeaderboardPlayers.Count} players on leaderboard"); foreach (LiveLeaderboardPlugin.SBGLPlayer player in allLeaderboardPlayers) { if (player != null) { playerScores[player.Name] = player.BaseScore; if (!string.IsNullOrEmpty(player.RawStrokes)) { string strokeStr = player.RawStrokes.Replace("±", "").Trim(); int.TryParse(strokeStr, out var vsPar); playerScoresVsPar[player.Name] = vsPar; } } } } } catch (Exception ex) { Log("[Match Creation] Error prefetching leaderboard: " + ex.Message + ""); } _cachedLeaderboardScores = playerScores; _cachedLeaderboardScoresVsPar = playerScoresVsPar; _matchExpectedPlayerCount = startingLeaderboard?.Count ?? 0; Dictionary startingPositionMap = ((startingLeaderboard != null) ? BuildFinishPositionMap(startingLeaderboard) : new Dictionary(StringComparer.OrdinalIgnoreCase)); if (isCasualMatch) { Log("[Casual] Casual match detected - submitting match record for website to process"); } if (_isHost || NetworkServer.active) { Log("[Match Creation] We are the host — creating Match record with priority"); if (!string.IsNullOrEmpty(_currentMatchId)) { Log("[Match Creation] Match already exists for this session: " + _currentMatchId + " — skipping duplicate POST"); } else { if (_currentSession != null) { string existingMatchId = null; yield return ResolveExistingMatchIdForCurrentSession(delegate(string id) { existingMatchId = id; }); if (!string.IsNullOrEmpty(existingMatchId)) { _currentMatchId = existingMatchId; ShowUploadNotification("Match record already exists; reusing existing upload."); goto IL_08ee; } } Log("[Match Creation] No existing Match found — proceeding with POST"); string prePostSessionId = ((_currentSession != null) ? _currentSession.id : _localManualSessionId); if (!string.IsNullOrWhiteSpace(prePostSessionId)) { string finalCheckId = null; yield return CallAPI("/match?matchmaking_session_id=eq." + prePostSessionId + "&order=created_at.asc", "GET", "", delegate(string res) { JObject obj = SelectNewestReusableMatch(ParseApiObjectList(res)); finalCheckId = (string)((obj != null) ? obj["id"] : null); }); if (!string.IsNullOrWhiteSpace(finalCheckId)) { Log("[Match Creation] ✓ Final pre-POST check found existing active match: " + finalCheckId + " — skipping duplicate POST"); _currentMatchId = finalCheckId; if (_currentSession != null) { _currentSession.match_id = finalCheckId; } ShowUploadNotification("Match record already exists; reusing existing upload."); goto IL_08ee; } } string newMatchId = null; yield return SubmitMatchEntry(CollectMatchStats(0f), delegate(string id) { newMatchId = id; }); if (string.IsNullOrEmpty(newMatchId)) { string apiErr = ((!string.IsNullOrEmpty(_lastCallApiError)) ? (" (" + _lastCallApiError + ")") : ""); Log("[Match Creation] Failed to create Match record" + apiErr + ""); ShowUploadNotification("Upload failed: could not create match record." + apiErr, "failure"); yield break; } _currentMatchId = newMatchId; Log("[Match Creation] ✓ Match created: " + newMatchId + ""); ShowUploadNotification("Upload success: match record created.", "success"); MatchResultSubmissionService.BroadcastMatchId(peers: CompetitivePluginCheck.GetKnownPeers(), matchId: newMatchId); } goto IL_08ee; } Log("[Match Creation] Not host — host will handle all uploads"); _matchEntriesCreated = true; yield break; IL_08ee: if (_currentSession != null) { string linkMatchId = _currentMatchId; yield return CallAPI("/matchmaking_session?id=eq." + _currentSession.id, "PATCH", "{\"match_id\":\"" + linkMatchId + "\"}", delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { _currentSession.match_id = linkMatchId; Log("[Match Creation] ✓ MatchmakingSession " + _currentSession.id + " linked to match: " + linkMatchId + ""); } else { Log("[Match Creation] Could not confirm MatchmakingSession update"); } }); } else { Log("[Match Creation] Local lobby mode: skipping MatchmakingSession link"); } _playerMatchEntryIds.Clear(); _playerIdsByName.Clear(); _lastSubmittedScores.Clear(); _lastSubmittedScoresVsPar.Clear(); if (startingLeaderboard == null || startingLeaderboard.Count == 0) { Log("[Match Creation] Leaderboard empty at round start — deferring MatchEntry creation until the gameplay monitor sees players"); if (_monitorCoroutine == null) { _monitorCoroutine = ((MonoBehaviour)this).StartCoroutine(MonitorAndUpdateScores()); } yield break; } List playerIds = ((_currentSession != null && _currentSession.player_ids != null && _currentSession.player_ids.Count > 0) ? _currentSession.player_ids : new List { _userProfile.id }); yield return EnrichPlayerIdsFromLeaderboard(startingLeaderboard, playerIds); _matchExpectedPlayerCount = playerIds.Count; foreach (string playerId in playerIds) { string playerName = null; string preMatchMmr = null; int gamePoints = 0; int scoreVsPar = 0; if (playerId == _userProfile.id) { playerName = _userProfile.display_name; preMatchMmr = _userProfile.current_mmr.ToString(); Log("[Match Creation] Current player: " + playerName + " (MMR: " + preMatchMmr + ")"); } else { yield return CallAPI("/player?id=eq." + playerId + "&limit=1", "GET", "", delegate(string res) { try { JObject val = ParseApiSingleObject(res); if (val != null) { playerName = (string)val["display_name"]; if (string.IsNullOrEmpty(playerName)) { Log("[Match Creation] Player " + playerId + " has no display_name, using ID"); playerName = playerId; } object obj = val["current_mmr"]; if (obj != null) { preMatchMmr = obj.ToString(); } Log("[Match Creation] Fetched player: " + playerName + " (MMR: " + preMatchMmr + ")"); } else { Log("[Match Creation] Failed to fetch Player " + playerId + " - response null"); playerName = playerId; } } catch (Exception ex2) { Log("[Match Creation] Error fetching Player " + playerId + ": " + ex2.Message + ""); playerName = playerId; } }); } if (!string.IsNullOrEmpty(playerName)) { _playerIdsByName[playerName] = playerId; if (_cachedLeaderboardScores.TryGetValue(playerName, out var score)) { gamePoints = score; _cachedLeaderboardScoresVsPar.TryGetValue(playerName, out scoreVsPar); } _lastSubmittedScores[playerName] = gamePoints; _lastSubmittedScoresVsPar[playerName] = scoreVsPar; } int startingPosition = 0; if (!string.IsNullOrWhiteSpace(playerName)) { startingPositionMap.TryGetValue(playerName, out startingPosition); } string entryId = null; yield return EnsureMatchEntryForPlayer("Match Creation", playerId, playerName, preMatchMmr, null, gamePoints, scoreVsPar, startingPosition, "Progressive match tracking - created at round start", delegate(string id) { entryId = id; }); if (string.IsNullOrWhiteSpace(entryId)) { Log("[Match Creation] MatchEntry not ready yet for " + (playerName ?? playerId) + "; gameplay monitor will retry if needed"); } } _matchEntriesCreated = true; Log("[Match Creation] ✓ Match and entries initialized. Starting score monitoring..."); if (_monitorCoroutine == null) { _monitorCoroutine = ((MonoBehaviour)this).StartCoroutine(MonitorAndUpdateScores()); } } private IEnumerator MonitorLobbyNameForUpload() { Log("[LobbyMonitor] Starting mid-round lobby rename monitor"); WaitForSeconds checkDelay = new WaitForSeconds(3f); while (_isInGameplay && !_matchEntriesCreated) { yield return checkDelay; if (!_isInGameplay || _matchEntriesCreated) { break; } string liveLobbyName = CompetitivePluginCheck._currentLobbyName; if (string.IsNullOrEmpty(liveLobbyName) || !liveLobbyName.StartsWith("SBGL-", StringComparison.OrdinalIgnoreCase)) { continue; } Log("[LobbyMonitor] Lobby renamed to '" + liveLobbyName + "' mid-round - triggering upload..."); ((MonoBehaviour)this).StartCoroutine(ValidateMatchUpload(delegate(bool shouldUpload) { if (shouldUpload) { Log("[LobbyMonitor] Eligibility confirmed - creating match records..."); ((MonoBehaviour)this).StartCoroutine(CreateMatchAndEntries()); } else { Log("[LobbyMonitor] Lobby is SBGL-* but failed eligibility check - skipping upload"); _matchEntriesCreated = true; } })); break; } Log("[LobbyMonitor] Lobby rename monitor stopped"); } private IEnumerator MonitorAndUpdateScores() { Log("[Match Monitor] Starting score monitoring for gameplay"); while (_isInGameplay) { yield return (object)new WaitForSeconds(2f); if (!_matchEntriesCreated && string.IsNullOrWhiteSpace(_currentMatchId) && !_matchCreationInProgress && Time.realtimeSinceStartup >= _nextEnsureMatchCreateAttemptAt) { _nextEnsureMatchCreateAttemptAt = Time.realtimeSinceStartup + 6f; bool shouldUpload = false; yield return ValidateMatchUpload(delegate(bool ok) { shouldUpload = ok; }); if (shouldUpload) { Log("[Match Monitor] Missing match records mid-round - attempting creation/adoption..."); yield return CreateMatchAndEntries(); } } if (_currentMatchId == null) { continue; } LiveLeaderboardPlugin liveLeaderboard = Object.FindAnyObjectByType((FindObjectsInactive)1); if ((Object)(object)liveLeaderboard == (Object)null) { continue; } List allLeaderboardPlayers = liveLeaderboard.GetCurrentLeaderboard(); if (allLeaderboardPlayers == null || allLeaderboardPlayers.Count == 0) { continue; } CacheLeaderboardSnapshot(allLeaderboardPlayers, "live gameplay"); Dictionary livePositionMap = BuildFinishPositionMap(allLeaderboardPlayers); int activePlayerCount = allLeaderboardPlayers.Count((LiveLeaderboardPlugin.SBGLPlayer p) => p != null && !string.IsNullOrWhiteSpace(p.Name) && p.BaseScore != 0); if (activePlayerCount > 0) { _matchExpectedPlayerCount = activePlayerCount; yield return UpdateMatchPlayerCountIfNeeded(activePlayerCount, "live leaderboard"); } List leaderboardNames = (from p in allLeaderboardPlayers?.Where((LiveLeaderboardPlugin.SBGLPlayer p) => p != null && !string.IsNullOrWhiteSpace(p.Name)) select p.Name).ToList() ?? new List(); Dictionary testOverrides = GetTestPlayerOverrides(leaderboardNames); foreach (LiveLeaderboardPlugin.SBGLPlayer player in allLeaderboardPlayers) { if (player == null) { continue; } int newGamePoints = player.BaseScore; int newScoreVsPar = 0; if (!string.IsNullOrEmpty(player.RawStrokes)) { string strokeStr = player.RawStrokes.Replace("±", "").Trim(); int.TryParse(strokeStr, out newScoreVsPar); } if (newGamePoints == 0) { continue; } bool scoresChanged = false; if (_lastSubmittedScores.TryGetValue(player.Name, out var lastScore)) { if (lastScore != newGamePoints || !_lastSubmittedScoresVsPar.TryGetValue(player.Name, out var lastVsPar) || lastVsPar != newScoreVsPar) { scoresChanged = true; } } else { scoresChanged = true; } if (!scoresChanged) { continue; } string playerId = null; string entryId = null; if (!TryGetPlayerIdForName(player.Name, out playerId)) { string monitorLookupName = ApplyTestPlayerOverride(player.Name, testOverrides); yield return ResolvePlayerIdByNameFromApi(monitorLookupName, delegate(string id) { playerId = id; }); if (!string.IsNullOrEmpty(playerId)) { _playerIdsByName[player.Name.Trim()] = playerId; } } if (!string.IsNullOrEmpty(playerId)) { _playerMatchEntryIds.TryGetValue(playerId, out entryId); } if (!string.IsNullOrEmpty(playerId) && string.IsNullOrEmpty(entryId)) { string preMatchMmr = null; yield return CallAPI("/player?id=eq." + playerId + "&limit=1", "GET", "", delegate(string res) { JObject val = ParseApiSingleObject(res); object obj = ((val != null) ? val["current_mmr"] : null); if (obj != null) { preMatchMmr = obj.ToString(); } }); int finishPosition = 0; if (!string.IsNullOrWhiteSpace(player.Name)) { livePositionMap.TryGetValue(player.Name, out finishPosition); } yield return EnsureMatchEntryForPlayer("Match Monitor", playerId, player.Name, preMatchMmr, null, newGamePoints, newScoreVsPar, finishPosition, "Live leaderboard backfill during round", delegate(string id) { entryId = id; }); } if (!string.IsNullOrEmpty(entryId) && !string.IsNullOrEmpty(playerId)) { _matchEntriesCreated = true; int finishPosition2 = 0; if (!string.IsNullOrWhiteSpace(player.Name)) { livePositionMap.TryGetValue(player.Name, out finishPosition2); } string perHolePreMmr = ((!string.IsNullOrWhiteSpace(player.MMR) && player.MMR != "..." && player.MMR != "--") ? player.MMR : null); string perHolePostMmr = null; if (perHolePreMmr != null && float.TryParse(perHolePreMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out var perHolePreF)) { int projDelta = ParseProjectedMmrDelta(player.ProjectedDisplay); perHolePostMmr = (perHolePreF + (float)projDelta).ToString(CultureInfo.InvariantCulture); } yield return UpdateMatchEntry(entryId, playerId, player.Name, newGamePoints, newScoreVsPar, 0, perHolePostMmr, perHolePreMmr); _lastSubmittedScores[player.Name] = newGamePoints; _lastSubmittedScoresVsPar[player.Name] = newScoreVsPar; } } } Log("[Match Monitor] Score monitoring ended"); _monitorCoroutine = null; } private IEnumerator UpdateMatchEntry(string entryId, string playerId, string playerName, int gamePoints, int scoreVsPar, int finishPosition = 0, string postMatchMmr = null, string preMatchMmr = null) { Log($"[Score Update] Hole completed for {playerName}: {gamePoints} pts, {scoreVsPar} vs par"); int adjustedScore = gamePoints + scoreVsPar * -10; JObject payload = new JObject { ["game_points"] = JToken.op_Implicit(gamePoints), ["over_under"] = JToken.op_Implicit(scoreVsPar), ["score_vs_par"] = JToken.op_Implicit(scoreVsPar), ["adjusted_match_score"] = JToken.op_Implicit(adjustedScore), ["notes"] = JToken.op_Implicit("Updated after hole completion") }; if (finishPosition > 0) { payload["finish_position"] = JToken.op_Implicit(finishPosition); } if (!string.IsNullOrWhiteSpace(preMatchMmr) && float.TryParse(preMatchMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out var preMmrF)) { payload["pre_match_mmr"] = JToken.op_Implicit(preMmrF); } if (!string.IsNullOrWhiteSpace(postMatchMmr) && float.TryParse(postMatchMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out var postMmrF)) { payload["post_match_mmr"] = JToken.op_Implicit(postMmrF); if (!string.IsNullOrWhiteSpace(preMatchMmr) && float.TryParse(preMatchMmr, NumberStyles.Float, CultureInfo.InvariantCulture, out var preMmrF2)) { payload["mmr_change"] = JToken.op_Implicit(postMmrF - preMmrF2); } } yield return CallAPI(json: ((JToken)payload).ToString((Formatting)0, Array.Empty()), endpoint: "/match_entry?id=eq." + entryId, method: "PATCH", onSuccess: delegate(string res) { try { JObject val = ParseApiSingleObject(res); if (val != null) { Log("[Score Update] ✓ MatchEntry updated for " + playerName + ""); } } catch (Exception ex) { Log("[Score Update] Could not update MatchEntry: " + ex.Message + ""); } }); } private int GetPlayerFinishPosition(string playerName, List finalLeaderboard) { if (string.IsNullOrEmpty(playerName) || finalLeaderboard == null || finalLeaderboard.Count == 0) { return 0; } Dictionary dictionary = BuildFinishPositionMap(finalLeaderboard); if (dictionary.TryGetValue(playerName, out var value)) { return value; } return 0; } private int GetPlayerFinishPosition(string playerName, List finalLeaderboard) { if (string.IsNullOrEmpty(playerName) || finalLeaderboard == null || finalLeaderboard.Count == 0) { return 0; } for (int i = 0; i < finalLeaderboard.Count; i++) { if (finalLeaderboard[i] != null && string.Equals(finalLeaderboard[i].Name, playerName, StringComparison.OrdinalIgnoreCase)) { return i + 1; } } return 0; } private bool TryGetPlayerIdForName(string playerName, out string playerId) { playerId = null; if (string.IsNullOrWhiteSpace(playerName)) { return false; } string text = playerName.Trim(); if (_playerIdsByName.TryGetValue(text, out var value) && !string.IsNullOrWhiteSpace(value)) { playerId = value; return true; } if (_userProfile != null && string.Equals(text, _userProfile.display_name, StringComparison.OrdinalIgnoreCase)) { playerId = _userProfile.id; return !string.IsNullOrWhiteSpace(playerId); } return false; } private IEnumerator ResolvePlayerIdByNameFromApi(string playerName, Action onResolved) { if (onResolved == null) { yield break; } if (string.IsNullOrWhiteSpace(playerName)) { onResolved(null); yield break; } string normalizedName = playerName.Trim(); string resolvedId = null; yield return CallAPI("/player?display_name=ilike." + UnityWebRequest.EscapeURL(normalizedName) + "&limit=1", "GET", "", delegate(string res) { JObject val = ParseApiObjectList(res)?.FirstOrDefault(); if (val != null) { resolvedId = (string)val["id"]; } }); if (string.IsNullOrWhiteSpace(resolvedId)) { yield return CallAPI("/player?display_name=ilike.*" + UnityWebRequest.EscapeURL(normalizedName) + "*&limit=5", "GET", "", delegate(string res) { List list = ParseApiObjectList(res); if (list != null && list.Count != 0) { JObject val = ((IEnumerable)list).FirstOrDefault((Func)((JObject r) => string.Equals((string)((r != null) ? r["display_name"] : null), normalizedName, StringComparison.OrdinalIgnoreCase))); JObject val2 = val ?? list.FirstOrDefault(); if (val2 != null) { resolvedId = (string)val2["id"]; } } }); } onResolved(resolvedId); } private IEnumerator UpdateMatchPlayerCountIfNeeded(int actualPlayerCount, string source) { if (actualPlayerCount <= 0 || string.IsNullOrEmpty(_currentMatchId) || _lastUploadedPlayerCount == actualPlayerCount) { yield break; } yield return CallAPI(json: "{" + $"\"player_count\":{actualPlayerCount}" + "}", endpoint: "/match?id=eq." + _currentMatchId, method: "PATCH", onSuccess: delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { _lastUploadedPlayerCount = actualPlayerCount; Log($"[Match Count] ✓ Updated match player_count to {actualPlayerCount} ({source})"); } else { Log($"[Match Count] Could not confirm player_count update to {actualPlayerCount} ({source})"); } }); } private void CacheLeaderboardSnapshot(List players, string source) { if (players == null || players.Count == 0) { return; } List list = (from player in players where player != null && !string.IsNullOrWhiteSpace(player.Name) select new CachedLeaderboardPlayer { Name = player.Name.Trim(), BaseScore = player.BaseScore, RawStrokes = (player.RawStrokes ?? string.Empty), MMR = player.MMR, ProjectedDelta = ParseProjectedMmrDelta(player.ProjectedDisplay) }).ToList(); if (list.Count != 0) { bool flag = SnapshotHasMeaningfulScores(list); bool flag2 = SnapshotHasMeaningfulScores(_finalLeaderboardSnapshot); if (!flag && flag2) { Log("[Match Snapshot] Ignoring zeroed " + source + " snapshot and keeping last in-game results"); } else { _finalLeaderboardSnapshot = list; } } } private bool SnapshotHasMeaningfulScores(List snapshot) { if (snapshot == null || snapshot.Count == 0) { return false; } foreach (CachedLeaderboardPlayer item in snapshot) { if (item != null) { if (item.BaseScore != 0) { return true; } if (ParseScoreVsPar(item.RawStrokes) != 0) { return true; } } } return false; } private int ParseScoreVsPar(string rawStrokes) { if (string.IsNullOrWhiteSpace(rawStrokes)) { return 0; } string s = rawStrokes.Replace("±", "").Trim(); int.TryParse(s, out var result); return result; } private int ParseProjectedMmrDelta(string projectedDisplay) { if (string.IsNullOrWhiteSpace(projectedDisplay)) { return 0; } Match match = Regex.Match(projectedDisplay, "\\(([+-]?\\d+)\\)"); if (match.Success && int.TryParse(match.Groups[1].Value, out var result)) { return result; } return 0; } private Dictionary BuildFinishPositionMap(List leaderboard) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (leaderboard == null || leaderboard.Count == 0) { return dictionary; } var list = (from p in leaderboard where p != null && !string.IsNullOrWhiteSpace(p.Name) select new { Player = p, Stroke = ParseScoreVsPar(p.RawStrokes) } into x orderby x.Player.BaseScore descending, x.Stroke select x).ThenBy(x => x.Player.Name, StringComparer.OrdinalIgnoreCase).ToList(); int num = 1; foreach (var item in list) { if (!dictionary.ContainsKey(item.Player.Name)) { dictionary[item.Player.Name] = num++; } } return dictionary; } private Dictionary BuildFinishPositionMap(List leaderboard) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (leaderboard == null || leaderboard.Count == 0) { return dictionary; } var list = (from p in leaderboard where p != null && !string.IsNullOrWhiteSpace(p.Name) select new { Player = p, Stroke = ParseScoreVsPar(p.RawStrokes) } into x orderby x.Player.BaseScore descending, x.Stroke select x).ThenBy(x => x.Player.Name, StringComparer.OrdinalIgnoreCase).ToList(); int num = 1; foreach (var item in list) { if (!dictionary.ContainsKey(item.Player.Name)) { dictionary[item.Player.Name] = num++; } } return dictionary; } private IEnumerator EnrichPlayerIdsFromLeaderboard(List startingLeaderboard, List playerIds) { if (startingLeaderboard == null || startingLeaderboard.Count == 0 || playerIds == null) { yield break; } List leaderboardNames = (from p in startingLeaderboard?.Where((LiveLeaderboardPlugin.SBGLPlayer p) => p != null && !string.IsNullOrWhiteSpace(p.Name)) select p.Name).ToList() ?? new List(); Dictionary testOverrides = GetTestPlayerOverrides(leaderboardNames); if (testOverrides.Count > 0) { Log($"[Test Override] {testOverrides.Count} player override(s) active for this match"); } HashSet knownIds = new HashSet(playerIds.Where((string id) => !string.IsNullOrWhiteSpace(id)), StringComparer.OrdinalIgnoreCase); HashSet knownNames = new HashSet(StringComparer.OrdinalIgnoreCase); if (_userProfile != null && !string.IsNullOrWhiteSpace(_userProfile.display_name) && !string.IsNullOrWhiteSpace(_userProfile.id)) { knownNames.Add(_userProfile.display_name); knownIds.Add(_userProfile.id); if (!playerIds.Contains(_userProfile.id)) { playerIds.Add(_userProfile.id); } } foreach (string existingId in knownIds.ToList()) { if (_userProfile != null && string.Equals(existingId, _userProfile.id, StringComparison.OrdinalIgnoreCase)) { continue; } yield return CallAPI("/player?id=eq." + existingId + "&limit=1", "GET", "", delegate(string res) { JObject val = ParseApiSingleObject(res); string text = (string)((val != null) ? val["display_name"] : null); if (!string.IsNullOrWhiteSpace(text)) { knownNames.Add(text); } }); } foreach (LiveLeaderboardPlugin.SBGLPlayer lbPlayer in startingLeaderboard) { if (lbPlayer == null || string.IsNullOrWhiteSpace(lbPlayer.Name) || knownNames.Contains(lbPlayer.Name)) { continue; } string lookupName = ApplyTestPlayerOverride(lbPlayer.Name, testOverrides); string playerIdFromName = null; yield return ResolvePlayerIdByNameFromApi(lookupName, delegate(string id) { playerIdFromName = id; }); if (!string.IsNullOrWhiteSpace(playerIdFromName)) { knownNames.Add(lbPlayer.Name); if (knownIds.Add(playerIdFromName)) { playerIds.Add(playerIdFromName); Log("[Match Creation] Resolved leaderboard player " + lbPlayer.Name + " -> " + playerIdFromName + ""); } } else { Log("[Match Creation] Could not resolve leaderboard player '" + lbPlayer.Name + "' to a Player ID"); } } } private IEnumerator FinalizeMatchStats() { if (IsProSeriesMatchType(PlayerPrefs.GetString("MatchType", "")) || IsCurrentMatchCasual()) { _matchStatsSubmitted = true; yield break; } Log("[Match Finalize] Performing final score update..."); if (_finalLeaderboardSnapshot == null || _finalLeaderboardSnapshot.Count == 0) { Log("[Match Finalize] No cached end-of-match leaderboard snapshot available — skipping final upload to avoid driving range zeros"); _matchStatsSubmitted = true; yield break; } int finalPlayerCount = _finalLeaderboardSnapshot.Count((CachedLeaderboardPlayer cachedLeaderboardPlayer) => cachedLeaderboardPlayer != null && !string.IsNullOrWhiteSpace(cachedLeaderboardPlayer.Name) && cachedLeaderboardPlayer.BaseScore != 0); if (finalPlayerCount > 0) { _matchExpectedPlayerCount = finalPlayerCount; yield return UpdateMatchPlayerCountIfNeeded(finalPlayerCount, "cached final leaderboard"); } Log($"[Match Finalize] Using cached final snapshot with {_finalLeaderboardSnapshot.Count} players"); List leaderboardNames = (from cachedLeaderboardPlayer in _finalLeaderboardSnapshot?.Where((CachedLeaderboardPlayer cachedLeaderboardPlayer) => cachedLeaderboardPlayer != null && !string.IsNullOrWhiteSpace(cachedLeaderboardPlayer.Name)) select cachedLeaderboardPlayer.Name).ToList() ?? new List(); Dictionary testOverrides = GetTestPlayerOverrides(leaderboardNames); Dictionary finalPositionMap = BuildFinishPositionMap(_finalLeaderboardSnapshot); Dictionary finalPreMmrByName = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (CachedLeaderboardPlayer p in _finalLeaderboardSnapshot) { if (p == null || string.IsNullOrWhiteSpace(p.Name) || p.BaseScore == 0) { continue; } if (!string.IsNullOrWhiteSpace(p.MMR) && float.TryParse(p.MMR, NumberStyles.Float, CultureInfo.InvariantCulture, out var snapMmr)) { finalPreMmrByName[p.Name] = snapMmr; continue; } if (_userProfile != null && string.Equals(p.Name, _userProfile.display_name, StringComparison.OrdinalIgnoreCase)) { finalPreMmrByName[p.Name] = _userProfile.current_mmr; Log($"[Match Finalize] MMR for local player {p.Name} resolved from profile: {_userProfile.current_mmr}"); continue; } TryGetPlayerIdForName(p.Name, out var resolveId); if (!string.IsNullOrWhiteSpace(resolveId)) { yield return CallAPI("/player?id=eq." + resolveId + "&limit=1", "GET", "", delegate(string res) { JObject val = ParseApiSingleObject(res); object obj = ((val != null) ? val["current_mmr"] : null); if (obj != null && float.TryParse(obj.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { finalPreMmrByName[p.Name] = result; Log($"[Match Finalize] MMR for {p.Name} resolved from API: {result}"); } }); } resolveId = null; } foreach (CachedLeaderboardPlayer player in _finalLeaderboardSnapshot) { if (player == null) { continue; } int finalGamePoints = player.BaseScore; int finalScoreVsPar = ParseScoreVsPar(player.RawStrokes); if (finalGamePoints == 0) { Log("[Match Finalize] Skipping " + player.Name + " — game points is 0 (spectator/no score)"); continue; } int finishPosition = 0; if (!string.IsNullOrWhiteSpace(player.Name)) { finalPositionMap.TryGetValue(player.Name, out finishPosition); } TryGetPlayerIdForName(player.Name, out var playerId); _playerMatchEntryIds.TryGetValue(playerId ?? "", out var entryId); string cachedPreMmr = null; string cachedPostMmr = null; if (finalPreMmrByName.TryGetValue(player.Name, out var resolvedPreMmr)) { cachedPreMmr = resolvedPreMmr.ToString(CultureInfo.InvariantCulture); cachedPostMmr = (resolvedPreMmr + (float)player.ProjectedDelta).ToString(CultureInfo.InvariantCulture); } if (!string.IsNullOrEmpty(entryId)) { Log(string.Format("[Match Finalize] Final update for {0}: {1} pts, {2} vs par, pos {3}, postMMR {4}", player.Name, finalGamePoints, finalScoreVsPar, finishPosition, cachedPostMmr ?? "n/a")); yield return UpdateMatchEntry(entryId, playerId, player.Name, finalGamePoints, finalScoreVsPar, finishPosition, cachedPostMmr, cachedPreMmr); continue; } Log("[Match Finalize] " + player.Name + " has no entry — creating late entry..."); if (string.IsNullOrEmpty(playerId)) { string finalizeLookupName = ApplyTestPlayerOverride(player.Name, testOverrides); yield return ResolvePlayerIdByNameFromApi(finalizeLookupName, delegate(string id) { playerId = id; }); } if (string.IsNullOrEmpty(playerId)) { Log("[Match Finalize] Cannot resolve player ID for " + player.Name + " — skipping late entry"); continue; } string preMatchMmr = cachedPreMmr; if (string.IsNullOrWhiteSpace(preMatchMmr)) { yield return CallAPI("/player?id=eq." + playerId + "&limit=1", "GET", "", delegate(string res) { JObject val = ParseApiSingleObject(res); object obj = ((val != null) ? val["current_mmr"] : null); if (obj != null) { preMatchMmr = obj.ToString(); } }); } yield return EnsureMatchEntryForPlayer("Match Finalize", playerId, player.Name, preMatchMmr, cachedPostMmr, finalGamePoints, finalScoreVsPar, finishPosition, "Late entry - joined after match start", delegate(string id) { entryId = id; }); if (!string.IsNullOrEmpty(entryId)) { yield return UpdateMatchEntry(entryId, playerId, player.Name, finalGamePoints, finalScoreVsPar, finishPosition, cachedPostMmr, preMatchMmr); } } if (_currentSession?.player_ids != null && _currentSession.player_ids.Count > 0) { foreach (string sessionPlayerId in _currentSession.player_ids.Where((string id) => !string.IsNullOrWhiteSpace(id)).Distinct(StringComparer.Ordinal)) { if (!_playerMatchEntryIds.ContainsKey(sessionPlayerId)) { yield return CreatePlaceholderMatchEntryForMissingSessionPlayer(sessionPlayerId); } } int expectedPlayerCount = _currentSession.player_ids.Count((string id) => !string.IsNullOrWhiteSpace(id)); if (expectedPlayerCount > _matchExpectedPlayerCount) { _matchExpectedPlayerCount = expectedPlayerCount; yield return UpdateMatchPlayerCountIfNeeded(expectedPlayerCount, "session player_ids fallback"); } } _matchStatsSubmitted = true; Log("[Match Finalize] ✓ Match stats finalized"); } private IEnumerator CreatePlaceholderMatchEntryForMissingSessionPlayer(string playerId) { if (string.IsNullOrWhiteSpace(playerId) || string.IsNullOrWhiteSpace(_currentMatchId)) { yield break; } string playerName = null; string preMatchMmr = null; if (_userProfile != null && string.Equals(playerId, _userProfile.id, StringComparison.OrdinalIgnoreCase)) { playerName = _userProfile.display_name; preMatchMmr = _userProfile.current_mmr.ToString(); } else { yield return CallAPI("/player?id=eq." + playerId + "&limit=1", "GET", "", delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { playerName = (string)val["display_name"]; object obj = val["current_mmr"]; if (obj != null) { preMatchMmr = obj.ToString(); } } }); } if (string.IsNullOrWhiteSpace(playerName)) { playerName = playerId; } int gamePoints = 0; int scoreVsPar = 0; int cachedScore; if (_lastSubmittedScores.TryGetValue(playerName, out var lastScore)) { gamePoints = lastScore; } else if (_cachedLeaderboardScores.TryGetValue(playerName, out cachedScore)) { gamePoints = cachedScore; } int cachedVsPar; if (_lastSubmittedScoresVsPar.TryGetValue(playerName, out var lastVsPar)) { scoreVsPar = lastVsPar; } else if (_cachedLeaderboardScoresVsPar.TryGetValue(playerName, out cachedVsPar)) { scoreVsPar = cachedVsPar; } if (gamePoints == 0) { Log("[Match Finalize] Skipping placeholder for session player " + playerName + " — game points is 0"); yield break; } string entryId = null; yield return EnsureMatchEntryForPlayer("Match Finalize", playerId, playerName, preMatchMmr, null, gamePoints, scoreVsPar, 0, "Placeholder entry created because player was in the session but missing from the final leaderboard snapshot", delegate(string id) { entryId = id; }); if (!string.IsNullOrEmpty(entryId)) { _playerMatchEntryIds[playerId] = entryId; _playerIdsByName[playerName.Trim()] = playerId; _lastSubmittedScores[playerName] = gamePoints; _lastSubmittedScoresVsPar[playerName] = scoreVsPar; } else { Log("[Match Finalize] Could not create placeholder MatchEntry for missing session player " + playerName + ""); } } private IEnumerator SubmitMatchStats() { if (_userProfile == null || _currentSession == null) { Log("[Match Stats] Failed: Missing profile or session"); yield break; } if (IsProSeriesMatchType(PlayerPrefs.GetString("MatchType", "")) || IsCurrentMatchCasual()) { _matchStatsSubmitted = true; yield break; } Log("[Match Stats] Collecting data for session: " + _currentSession.id + ""); float matchDuration = 0f; if (_matchStartTime.HasValue) { matchDuration = (float)(DateTime.UtcNow - _matchStartTime.Value).TotalSeconds; } MatchStats stats = CollectMatchStats(matchDuration); if (stats == null) { Log("[Match Stats] Failed to collect stats"); yield break; } Log($"[Match Stats] Duration: {matchDuration}s | Host: {stats.is_host} | Player: {stats.player_name}"); string matchId = null; yield return SubmitMatchEntry(stats, delegate(string id) { matchId = id; }); if (string.IsNullOrEmpty(matchId)) { Log("[Match Stats] Failed to get Match ID from submission"); yield break; } yield return CallAPI("/matchmaking_session?id=eq." + _currentSession.id, "PATCH", "{\"match_id\":\"" + matchId + "\"}", delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { _currentSession.match_id = matchId; Log("[Match Stats] ✓ MatchmakingSession " + _currentSession.id + " linked to match: " + matchId + ""); } else { Log("[Match Stats] Could not confirm MatchmakingSession update"); } }); Dictionary playerScores = new Dictionary(); Dictionary playerScoresVsPar = new Dictionary(); try { LiveLeaderboardPlugin liveLeaderboard = Object.FindAnyObjectByType((FindObjectsInactive)1); if ((Object)(object)liveLeaderboard != (Object)null) { List allLeaderboardPlayers = liveLeaderboard.GetCurrentLeaderboard(); Log($"[Match Stats] Leaderboard has {allLeaderboardPlayers.Count} visible players"); foreach (LiveLeaderboardPlugin.SBGLPlayer leaderboardPlayer in allLeaderboardPlayers) { if (leaderboardPlayer != null) { int gamePoints = leaderboardPlayer.BaseScore; int scoreVsPar = 0; if (!string.IsNullOrEmpty(leaderboardPlayer.RawStrokes)) { string strokeStr = leaderboardPlayer.RawStrokes.Replace("±", "").Trim(); int.TryParse(strokeStr, out scoreVsPar); } playerScores[leaderboardPlayer.Name] = gamePoints; playerScoresVsPar[leaderboardPlayer.Name] = scoreVsPar; Log($"[Match Stats] Cached: {leaderboardPlayer.Name} = {gamePoints} pts, {scoreVsPar} vs par"); } } } else { Log("[Match Stats] LiveLeaderboard not found - will use placeholder scores"); } } catch (Exception ex) { Exception ex2 = ex; Log("[Match Stats] Error fetching leaderboard data: " + ex2.Message + ""); } _cachedLeaderboardScores = playerScores; _cachedLeaderboardScoresVsPar = playerScoresVsPar; if (_currentSession.player_ids != null && _currentSession.player_ids.Count > 0) { Log($"[Match Stats] Creating MatchEntry records for {_currentSession.player_ids.Count} players"); using List.Enumerator enumerator2 = _currentSession.player_ids.GetEnumerator(); while (enumerator2.MoveNext()) { yield return SubmitMatchEntryForPlayer(playerId: enumerator2.Current, matchId: matchId); } } _matchStatsSubmitted = true; Log("[Match Stats] ✓ Match and player entries submitted successfully"); } private MatchStats CollectMatchStats(float duration) { try { string text = ((_currentSession != null) ? _currentSession.id : (_localManualSessionId ?? "local-manual-session")); return new MatchStats { matchmaking_session_id = text, match_id = text, player_id = _userProfile.id, player_name = _userProfile.display_name, match_date = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), duration_seconds = (int)duration, is_host = _isHost, status = "completed" }; } catch (Exception ex) { Log("[Match Stats] Error collecting stats: " + ex.Message + ""); return null; } } private IEnumerator FetchActiveSeasonId() { _activeSeasonFetched = true; string[] statusQueries = new string[3] { "/season?status=eq.Active&limit=1", "/season?status=eq.active&limit=1", "/season?order=created_at.desc&limit=1" }; string[] array = statusQueries; foreach (string query in array) { if (!string.IsNullOrEmpty(_activeSeasonId)) { break; } yield return CallAPI(query, "GET", "", delegate(string res) { if (string.IsNullOrEmpty(res)) { return; } try { JToken val = JToken.Parse(res); JArray val2 = (JArray)(object)((val is JArray) ? val : null); JObject val3 = (JObject)((val2 != null) ? ((object)((IEnumerable)val2).OfType().FirstOrDefault()) : ((object)((val is JObject) ? val : null))); string text = ((val3 == null) ? null : ((object)val3["id"])?.ToString()); string text2 = ((val3 == null) ? null : ((object)val3["name"])?.ToString()); if (!string.IsNullOrEmpty(text)) { _activeSeasonId = text; _activeSeasonName = ((!string.IsNullOrEmpty(text2)) ? text2 : "Active Season"); Log("[Season] Active season: " + _activeSeasonName + " (" + text + ")"); } } catch (Exception ex) { Log("[Season] Error parsing season response: " + ex.Message + ""); } }); } if (string.IsNullOrEmpty(_activeSeasonId)) { yield return CallAPI("/match?status=eq.Verified&order=created_at.desc&limit=1&select=season_id,match_date", "GET", "", delegate(string res) { try { JToken val = JToken.Parse(res); JArray val2 = (JArray)(object)((val is JArray) ? val : null); JObject val3 = (JObject)((val2 != null) ? ((object)((IEnumerable)val2).OfType().FirstOrDefault()) : ((object)((val is JObject) ? val : null))); string text = ((val3 == null) ? null : ((object)val3["season_id"])?.ToString()); if (!string.IsNullOrEmpty(text)) { _activeSeasonId = text; _activeSeasonName = "Active Season"; Log("[Season] Inferred season ID from recent verified match: " + _activeSeasonId + ""); } } catch (Exception ex) { Log("[Season] Error inferring season from match: " + ex.Message + ""); } }); } if (string.IsNullOrEmpty(_activeSeasonId)) { Log("[Season] Could not determine active season ID — match will be submitted without season_id"); } } private IEnumerator SubmitMatchEntry(MatchStats stats, Action onMatchIdReceived) { if (stats == null) { yield break; } string rawMatchType = PlayerPrefs.GetString("MatchType", "ranked_season_2"); string apiMatchType = Season2RuleSet.ToDbMatchType(rawMatchType); bool isProSeries = apiMatchType == "pro_series"; Guid result; JToken sessionIdToken = (JToken)(Guid.TryParse(stats.matchmaking_session_id, out result) ? ((object)JToken.op_Implicit(stats.matchmaking_session_id)) : ((object)JValue.CreateNull())); JToken seasonIdToken = (JToken)((!string.IsNullOrEmpty(_activeSeasonId)) ? ((object)JToken.op_Implicit(_activeSeasonId)) : ((object)JValue.CreateNull())); JObject payload = new JObject { ["id"] = JToken.op_Implicit(Guid.NewGuid().ToString()), ["matchmaking_session_id"] = sessionIdToken, ["season_id"] = seasonIdToken, ["match_date"] = JToken.op_Implicit(stats.match_date), ["match_type"] = JToken.op_Implicit(apiMatchType), ["player_count"] = JToken.op_Implicit(Mathf.Max(0, _matchExpectedPlayerCount)), ["status"] = JToken.op_Implicit("Pending"), ["submitted_by_name"] = JToken.op_Implicit(stats.player_name), ["mode"] = JToken.op_Implicit(""), ["notes"] = JToken.op_Implicit("Auto-submitted via SBGL Unified Mod v0.1.20") }; if (isProSeries) { int proSeriesWeek = PlayerPrefs.GetInt("ProSeriesWeek", 0); if (proSeriesWeek > 0) { payload["pro_series_week"] = JToken.op_Implicit(proSeriesWeek); } string proSeriesEventName = PlayerPrefs.GetString("ProSeriesEventName", ""); if (!string.IsNullOrWhiteSpace(proSeriesEventName)) { payload["pro_series_event_name"] = JToken.op_Implicit(proSeriesEventName); } } string json = ((JToken)payload).ToString((Formatting)0, Array.Empty()); Log("[Match Stats] Submitting Match entry to API"); Log("[Match Stats] Full URL: " + GetBaseApiUrl() + "/match"); Log("[Match Stats] Payload: " + json + ""); yield return CallAPI("/match", "POST", json, delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { string text = ((string)val["id"]) ?? "unknown"; _lastUploadedPlayerCount = Mathf.Max(0, _matchExpectedPlayerCount); Log("[Match Stats] ✓ Match entry created (ID: " + text + ")"); ShowUploadNotification("Upload success: match ID " + text + ".", "success"); onMatchIdReceived?.Invoke(text); } else { Log("[Match Stats] Response received but could not parse ID"); ShowUploadNotification("Upload failed: invalid API response.", "failure"); } }); } private IEnumerator SubmitMatchEntryForPlayer(string matchId, string playerId) { int gamePoints = 0; int scoreVsPar = 0; string playerDisplayName = null; string leaderboardName = null; foreach (KeyValuePair kvp in _playerIdsByName) { if (!string.IsNullOrWhiteSpace(kvp.Value) && string.Equals(kvp.Value, playerId, StringComparison.OrdinalIgnoreCase)) { leaderboardName = kvp.Key?.Trim(); break; } } if (!string.IsNullOrEmpty(leaderboardName)) { playerDisplayName = leaderboardName; Log("[Match Stats] Using leaderboard name mapping for " + playerId + ": " + playerDisplayName + ""); } if (string.IsNullOrEmpty(playerDisplayName) && playerId == _userProfile.id) { playerDisplayName = _userProfile.display_name; Log("[Match Stats] Current user: " + playerDisplayName + ""); } if (string.IsNullOrEmpty(playerDisplayName)) { Log("[Match Stats] Fetching profile for opponent " + playerId + " (fallback)"); yield return CallAPI("/player?id=eq." + playerId + "&select=*", "GET", "", delegate(string res) { try { JObject val = ParseApiSingleObject(res); if (val != null) { playerDisplayName = (string)val["display_name"]; Log("[Match Stats] Opponent display name (API): " + playerDisplayName + ""); } } catch (Exception ex) { Log("[Match Stats] Error parsing opponent profile: " + ex.Message + ""); } }); } bool foundScores = false; int tries = 0; while (!foundScores && tries < 5) { if (!string.IsNullOrEmpty(playerDisplayName)) { if (_cachedLeaderboardScores.TryGetValue(playerDisplayName, out var cachedScore)) { gamePoints = cachedScore; _cachedLeaderboardScoresVsPar.TryGetValue(playerDisplayName, out scoreVsPar); foundScores = true; Log($"[Match Stats] ✓ Found cached scores for {playerDisplayName}: {gamePoints} pts, {scoreVsPar} vs par"); break; } string matchKey = _cachedLeaderboardScores.Keys.FirstOrDefault((string k) => string.Equals(k?.Trim(), playerDisplayName?.Trim(), StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(matchKey)) { gamePoints = _cachedLeaderboardScores[matchKey]; _cachedLeaderboardScoresVsPar.TryGetValue(matchKey, out scoreVsPar); foundScores = true; Log($"[Match Stats] ✓ Found cached scores via fallback key '{matchKey}' for {playerDisplayName}: {gamePoints} pts, {scoreVsPar} vs par"); break; } } tries++; if (!foundScores && tries < 5) { Log($"[Match Stats] Scores not cached yet for {playerDisplayName ?? playerId}, retry {tries}/5..."); yield return (object)new WaitForSeconds(0.5f); } } if (!foundScores) { Log("[Match Stats] ⚠ No leaderboard data found for " + (playerDisplayName ?? playerId) + " after retries"); } if (gamePoints == 0) { Log("[Match Stats] Skipping MatchEntry for " + (playerDisplayName ?? playerId) + " — game points is 0"); yield break; } JObject entryPayload = new JObject { ["id"] = JToken.op_Implicit(Guid.NewGuid().ToString()), ["match_id"] = JToken.op_Implicit(matchId), ["player_id"] = JToken.op_Implicit(playerId), ["player_name"] = JToken.op_Implicit(playerDisplayName ?? "Unknown"), ["game_points"] = JToken.op_Implicit(gamePoints), ["score_vs_par"] = JToken.op_Implicit(scoreVsPar), ["notes"] = JToken.op_Implicit("Auto-submitted player entry via SBGL Unified Mod v0.1.20") }; string json = ((JToken)entryPayload).ToString((Formatting)0, Array.Empty()); Log($"[Match Stats] Submitting MatchEntry for {playerDisplayName ?? playerId}: {gamePoints} pts, {scoreVsPar} vs par"); yield return CallAPI("/match_entry", "POST", json, delegate(string res) { JObject val = ParseApiSingleObject(res); if (val != null) { string text = ((string)val["id"]) ?? "unknown"; Log("[Match Stats] ✓ MatchEntry created (ID: " + text + ") for player " + (playerDisplayName ?? playerId) + ""); } else { Log("[Match Stats] Could not parse MatchEntry response for player " + (playerDisplayName ?? playerId) + ""); } }); } private void OnGUI() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_00e3: Expected O, but got Unknown //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_044c: Unknown result type (might be due to invalid IL or missing references) //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_04da: Unknown result type (might be due to invalid IL or missing references) //IL_04bf: Unknown result type (might be due to invalid IL or missing references) //IL_04f9: Unknown result type (might be due to invalid IL or missing references) //IL_0783: Unknown result type (might be due to invalid IL or missing references) //IL_07c2: Unknown result type (might be due to invalid IL or missing references) //IL_0758: Unknown result type (might be due to invalid IL or missing references) //IL_0870: 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_0324: Unknown result type (might be due to invalid IL or missing references) //IL_054e: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_0b55: Unknown result type (might be due to invalid IL or missing references) //IL_0ba4: Unknown result type (might be due to invalid IL or missing references) //IL_0bd3: Unknown result type (might be due to invalid IL or missing references) //IL_0c2e: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_0570: Unknown result type (might be due to invalid IL or missing references) //IL_0e69: Unknown result type (might be due to invalid IL or missing references) //IL_0e96: Unknown result type (might be due to invalid IL or missing references) //IL_0e9c: Unknown result type (might be due to invalid IL or missing references) //IL_0ec1: Unknown result type (might be due to invalid IL or missing references) //IL_0ec6: Unknown result type (might be due to invalid IL or missing references) //IL_0ecb: Unknown result type (might be due to invalid IL or missing references) //IL_0838: Unknown result type (might be due to invalid IL or missing references) //IL_091f: Unknown result type (might be due to invalid IL or missing references) //IL_0955: Unknown result type (might be due to invalid IL or missing references) //IL_0985: Unknown result type (might be due to invalid IL or missing references) //IL_09c5: Unknown result type (might be due to invalid IL or missing references) //IL_09fb: Unknown result type (might be due to invalid IL or missing references) //IL_0a5f: Unknown result type (might be due to invalid IL or missing references) //IL_0a95: Unknown result type (might be due to invalid IL or missing references) //IL_0ae0: Unknown result type (might be due to invalid IL or missing references) //IL_0eeb: Unknown result type (might be due to invalid IL or missing references) //IL_0ca1: Unknown result type (might be due to invalid IL or missing references) //IL_0d05: Unknown result type (might be due to invalid IL or missing references) //IL_0d60: Unknown result type (might be due to invalid IL or missing references) //IL_0dbb: Unknown result type (might be due to invalid IL or missing references) //IL_0e03: Unknown result type (might be due to invalid IL or missing references) //IL_063d: Unknown result type (might be due to invalid IL or missing references) //IL_0622: Unknown result type (might be due to invalid IL or missing references) //IL_0661: Unknown result type (might be due to invalid IL or missing references) try { bool cachedIsMenuScene = _cachedIsMenuScene; bool flag = !string.IsNullOrEmpty(_uploadNotification); bool flag2 = _showFlowDebugConfig?.Value ?? false; if (!cachedIsMenuScene && !flag && !flag2) { return; } DrawUploadNotification(); DrawLiveUploadDebugOverlay(); if (!cachedIsMenuScene) { return; } if (_centerLabelStyle == null) { _centerLabelStyle = (((Object)(object)GUI.skin != (Object)null) ? new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4 } : new GUIStyle { alignment = (TextAnchor)4 }); } if (_debugLineStyle == null) { _debugLineStyle = (((Object)(object)GUI.skin != (Object)null) ? new GUIStyle(GUI.skin.label) : new GUIStyle()); _debugLineStyle.alignment = (TextAnchor)3; _debugLineStyle.fontSize = 10; _debugLineStyle.wordWrap = false; _debugLineStyle.clipping = (TextClipping)1; _debugLineStyle.richText = false; } float num = 350f; float num2 = (float)Screen.width - num - 30f; ConfigEntry showLogsConfig = _showLogsConfig; float num3 = ((showLogsConfig != null && showLogsConfig.Value) ? 340f : 230f); ConfigEntry showFlowDebugConfig = _showFlowDebugConfig; if (showFlowDebugConfig != null && showFlowDebugConfig.Value) { num3 += 145f; } GUI.DrawTexture(new Rect(num2, 20f, num, num3), (Texture)(object)_solidBgTex); GUI.Box(new Rect(num2, 20f, num, num3), "SBGL MATCH MAKING ASSISTANT"); if (_currentSession != null) { if (_currentSession.status == "pending_accept") { GUI.backgroundColor = (_hasAccepted ? Color.gray : Color.yellow); if (GUI.Button(new Rect(num2 + 10f, 50f, num - 20f, 50f), _hasAccepted ? "WAITING FOR OTHERS..." : "ACCEPT MATCH") && !_hasAccepted) { ((MonoBehaviour)this).StartCoroutine(AcceptMatch()); } } else if (_currentSession.status == "ready") { if (_isHost) { GUI.backgroundColor = Color.cyan; if (GUI.Button(new Rect(num2 + 10f, 50f, num - 20f, 50f), "INITIALIZE HOST")) { InitiateHostSequence(); } } else if (_hasAccepted && !string.IsNullOrEmpty(_currentSession.steam_lobby_link)) { GUI.backgroundColor = Color.green; GUI.enabled = false; GUI.Button(new Rect(num2 + 10f, 50f, num - 20f, 50f), "AUTO-JOINING..."); GUI.enabled = true; } else if (!_hasAccepted) { GUI.backgroundColor = Color.yellow; if (GUI.Button(new Rect(num2 + 10f, 50f, num - 20f, 50f), "ACCEPT & JOIN")) { ((MonoBehaviour)this).StartCoroutine(AcceptMatch()); } } else { GUI.backgroundColor = Color.gray; GUI.enabled = false; GUI.Button(new Rect(num2 + 10f, 50f, num - 20f, 50f), "JOINING..."); GUI.enabled = true; } } } else { bool enabled = !_isInitializing && _userProfile != null; if (_isQueueing) { GUI.enabled = enabled; GUI.backgroundColor = new Color(0.8f, 0.1f, 0.1f, 1f); if (GUI.Button(new Rect(num2 + 10f, 50f, num - 20f, 50f), "LEAVE QUEUE")) { ((MonoBehaviour)this).StartCoroutine(LeaveQueue()); } GUI.enabled = true; } else { float num4 = (num - 22f) / 2f; bool flag3 = _queueTypeSelection != "casual"; GUI.enabled = enabled; GUI.backgroundColor = (flag3 ? new Color(0.15f, 0.22f, 0.65f, 1f) : new Color(0.22f, 0.22f, 0.22f, 1f)); if (GUI.Button(new Rect(num2 + 10f, 50f, num4, 23f), "Ranked")) { _queueTypeSelection = "ranked"; } GUI.backgroundColor = ((!flag3) ? new Color(0.72f, 0.47f, 0.1f, 1f) : new Color(0.22f, 0.22f, 0.22f, 1f)); if (GUI.Button(new Rect(num2 + 12f + num4, 50f, num4, 23f), "Casual")) { _queueTypeSelection = "casual"; } bool flag4 = _queueTypeSelection != "casual" && _activeSeasonFetched && !IsActiveSeason; string text = (_isInitializing ? "SYNCING WITH SERVER..." : ((_userProfile == null) ? "RESOLVING PROFILE..." : ((!flag4) ? "JOIN QUEUE" : "OFFSEASON — RANKED UNAVAILABLE"))); GUI.enabled = !flag4; GUI.backgroundColor = (flag4 ? new Color(0.35f, 0.35f, 0.35f, 1f) : new Color(0.1f, 0.6f, 0.1f, 1f)); if (GUI.Button(new Rect(num2 + 10f, 76f, num - 20f, 32f), text)) { if (_queueTypeSelection == "casual") { PlayerPrefs.SetString("MatchType", "casual"); PlayerPrefs.SetString("HostRuleset", "casual"); } else { PlayerPrefs.SetString("MatchType", "ranked_season_2"); PlayerPrefs.SetString("HostRuleset", "ranked"); } ((MonoBehaviour)this).StartCoroutine(MatchmakingLoop()); } GUI.enabled = true; } } float num5 = 0f; GUI.Box(new Rect(num2 + 10f, 110f + num5, num - 20f, 100f), ""); if (_userProfile != null) { if (Object.op_Implicit((Object)(object)_profileTexture)) { GUI.DrawTexture(new Rect(num2 + 20f, 120f + num5, 40f, 40f), (Texture)(object)_profileTexture); } GUI.Label(new Rect(num2 + 70f, 120f + num5, 240f, 20f), "User: " + _userProfile.display_name + ""); GUI.Label(new Rect(num2 + 70f, 135f + num5, 240f, 20f), "" + _webStatus + ""); if (_activeSeasonFetched) { string text2 = (IsActiveSeason ? ("" + (_activeSeasonName ?? "Active Season") + "") : "OFFSEASON"); GUI.Label(new Rect(num2 + 70f, 148f + num5, 240f, 14f), text2); } float num6 = 160f + num5; float num7 = (num - 40f) / 4f; GUI.Label(new Rect(num2 + 20f, num6, num7, 20f), "TIME", _centerLabelStyle); string text3 = "00:00"; if (_isQueueing && _queueStartTime.HasValue) { TimeSpan timeSpan = DateTime.UtcNow - _queueStartTime.Value.ToUniversalTime(); text3 = ((timeSpan.TotalSeconds < 0.0) ? "00:00" : $"{timeSpan.Minutes:00}:{timeSpan.Seconds:00}"); } GUI.Label(new Rect(num2 + 20f, num6 + 15f, num7, 25f), "" + text3 + "", _centerLabelStyle); GUI.Label(new Rect(num2 + 20f + num7, num6, num7, 20f), "QUEUED", _centerLabelStyle); GUI.Label(new Rect(num2 + 20f + num7, num6 + 15f, num7, 25f), $"{_queuedCount}", _centerLabelStyle); GUI.Label(new Rect(num2 + 20f + num7 * 2f, num6, num7, 20f), "MATCHED", _centerLabelStyle); GUI.Label(new Rect(num2 + 20f + num7 * 2f, num6 + 15f, num7, 25f), $"{_matchedCount}", _centerLabelStyle); string rankName = Season2RuleSet.GetRankName(_userProfile.current_mmr); string rankColor = Season2RuleSet.GetRankColor(_userProfile.current_mmr); GUI.Label(new Rect(num2 + 20f + num7 * 3f, num6, num7, 20f), "YOUR MMR", _centerLabelStyle); GUI.Label(new Rect(num2 + 20f + num7 * 3f, num6 + 15f, num7, 18f), $"{_userProfile.current_mmr}", _centerLabelStyle); GUI.Label(new Rect(num2 + 20f + num7 * 3f, num6 + 31f, num7, 16f), "" + rankName + "", _centerLabelStyle); } float num8 = 215f + num5; ConfigEntry showFlowDebugConfig2 = _showFlowDebugConfig; if (showFlowDebugConfig2 != null && showFlowDebugConfig2.Value) { GUI.Label(new Rect(num2 + 15f, num8, num, 20f), "FLOW DIAGNOSTICS (TEMP)"); num8 += 18f; float num9 = 14f; float num10 = 6f; int num11 = 7; float num12 = num10 + (float)num11 * num9 + 6f; GUI.Box(new Rect(num2 + 10f, num8, num - 20f, num12), ""); GUI.Label(new Rect(num2 + 16f, num8 + num10 + 0f * num9, num - 30f, num9), $"sync_tick={_syncTickCount} host={_isHost} accepted={_hasAccepted}", _debugLineStyle); GUI.Label(new Rect(num2 + 16f, num8 + num10 + 1f * num9, num - 30f, num9), "session=" + ((_currentSession != null) ? _currentSession.id : "none") + " status=" + ((_currentSession != null) ? _currentSession.status : "none"), _debugLineStyle); GUI.Label(new Rect(num2 + 16f, num8 + num10 + 2f * num9, num - 30f, num9), $"lobby_events={_lobbyCreatedEventCount} link_present={!string.IsNullOrEmpty(_currentSession?.steam_lobby_link)}", _debugLineStyle); GUI.Label(new Rect(num2 + 16f, num8 + num10 + 3f * num9, num - 30f, num9), $"upload a/s/f={_steamLinkUploadAttempts}/{_steamLinkUploadSuccesses}/{_steamLinkUploadFailures}", _debugLineStyle); GUI.Label(new Rect(num2 + 16f, num8 + num10 + 4f * num9, num - 30f, num9), $"autojoin a/s/f={_autoJoinAttempts}/{_autoJoinSuccesses}/{_autoJoinFailures}", _debugLineStyle); GUI.Label(new Rect(num2 + 16f, num8 + num10 + 5f * num9, num - 30f, num9), "last_upload_err=" + Truncate(_lastUploadError, 42), _debugLineStyle); GUI.Label(new Rect(num2 + 16f, num8 + num10 + 6f * num9, num - 30f, num9), "last_join_err=" + Truncate(_lastAutoJoinError, 42), _debugLineStyle); num8 += num12 + 5f; } if (_showLogsConfig.Value) { float num13 = num8 + 5f; GUI.Label(new Rect(num2 + 15f, num13, num, 25f), "SYSTEM LOGS:"); _logScroll = GUI.BeginScrollView(new Rect(num2 + 10f, num13 + 25f, num - 20f, 70f), _logScroll, new Rect(0f, 0f, num - 40f, (float)(_debugLogs.Count * 20))); for (int i = 0; i < _debugLogs.Count; i++) { GUI.Label(new Rect(5f, (float)(i * 20), 300f, 20f), "" + _debugLogs[i] + ""); } GUI.EndScrollView(); } } catch (Exception ex) { Log("[CRITICAL] Exception in OnGUI: " + ex.Message + " | StackTrace: " + ex.StackTrace + ""); } } private void DrawUploadNotification() { //IL_00c1: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0140: 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) if (string.IsNullOrEmpty(_uploadNotification)) { return; } float num = (float)(DateTime.UtcNow - _uploadNotificationTime).TotalSeconds; if (num >= 4f) { _uploadNotification = ""; return; } float num2 = 1f; if (num > 3f) { num2 = Mathf.Lerp(1f, 0f, (num - 3f) / 1f); } float num3 = 500f; float num4 = 50f; float num5 = ((float)Screen.width - num3) / 2f; float num6 = (float)Screen.height - 100f; GUI.color = new Color(1f, 1f, 1f, num2); GUI.Box(new Rect(num5, num6, num3, num4), ""); GUIStyle val = (((Object)(object)GUI.skin != (Object)null) ? new GUIStyle(GUI.skin.label) : new GUIStyle()); val.alignment = (TextAnchor)4; val.fontSize = 14; val.richText = true; string text = ColorUtility.ToHtmlStringRGB(_uploadNotificationColor); GUI.Label(new Rect(num5, num6 + 10f, num3, 30f), "" + _uploadNotification + "", val); GUI.color = Color.white; } private void DrawLiveUploadDebugOverlay() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_008e: 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_01f5: Unknown result type (might be due to invalid IL or missing references) ConfigEntry showFlowDebugConfig = _showFlowDebugConfig; if (showFlowDebugConfig != null && showFlowDebugConfig.Value) { float num = 860f; float num2 = 62f; float num3 = ((float)Screen.width - num) / 2f; float num4 = (float)Screen.height - 170f; GUIStyle val = (((Object)(object)GUI.skin != (Object)null) ? new GUIStyle(GUI.skin.label) : new GUIStyle()); val.alignment = (TextAnchor)3; val.fontSize = 11; val.richText = true; GUI.Box(new Rect(num3, num4, num, num2), ""); string text = "LobbySources s='" + Truncate(_debugLobbySessionSource, 24) + "' p='" + Truncate(_debugLobbyPrefsSource, 24) + "' c='" + Truncate(_debugLobbyCapturedSource, 24) + "'"; string text2 = "LobbyResolved '" + Truncate(_debugLobbyResolved, 40) + "' via=" + _debugLobbyResolvedBy + " | SessionType='" + Truncate(_currentSession?.match_type ?? "", 24) + "' MatchType='" + Truncate(PlayerPrefs.GetString("MatchType", ""), 24) + "' HostRuleset='" + Truncate(PlayerPrefs.GetString("HostRuleset", ""), 14) + "'"; GUI.Label(new Rect(num3 + 10f, num4 + 7f, num - 20f, 20f), text, val); GUI.Label(new Rect(num3 + 10f, num4 + 29f, num - 20f, 20f), text2, val); } } public async void JoinBySteamLink(string steamLink, string password) { if (string.IsNullOrEmpty(steamLink)) { return; } PlayerPrefs.SetString("LobbyPassword", password); PlayerPrefs.Save(); Log("Password '" + password + "' cached for join."); try { string[] parts = steamLink.Split('/'); if (parts.Length < 5 || !ulong.TryParse(parts[4], out var lobbyId)) { Log("Invalid Steam Link"); return; } Log($"Joining Steam Lobby: {lobbyId}..."); Lobby lobby = new Lobby(SteamId.op_Implicit(lobbyId)); RoomEnter result = await ((Lobby)(ref lobby)).Join(); if ((int)result == 1) { NetworkManager manager = NetworkManager.singleton; manager.networkAddress = lobbyId.ToString(); await Task.Delay(200); manager.StartClient(); _autoJoinSuccesses++; _lastAutoJoinSuccessAt = DateTime.Now; _lastAutoJoinError = "-"; Log("Client started via Steam Link."); } else { _autoJoinFailures++; _lastAutoJoinError = $"Steam room enter failure: {result}"; Log($"Steam Join Failed: {result}"); } } catch (Exception ex) { _autoJoinFailures++; _lastAutoJoinError = ex.Message; Log("Join Error: " + ex.Message); } } private IEnumerator UpdateSessionStatus(string status) { if (_currentSession != null && !(_currentSession.id == "DEBUG")) { yield return CallAPI(json: "{\"status\":\"" + status + "\"}", endpoint: "/matchmaking_session?id=eq." + _currentSession.id, method: "PATCH", onSuccess: null); } } private IEnumerator CallAPI(string endpoint, string method, string json, Action onSuccess) { string fullUrl = GetBaseApiUrl() + endpoint; Log("[API] " + method + " " + endpoint + ""); Log("[API] Full URL: " + fullUrl + ""); UnityWebRequest req = new UnityWebRequest(fullUrl, method); try { if (!string.IsNullOrEmpty(json)) { req.uploadHandler = (UploadHandler)new UploadHandlerRaw(Encoding.UTF8.GetBytes(json)); } req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { Log("[API] " + method + " " + endpoint + " - Success"); _lastCallApiError = ""; onSuccess?.Invoke(req.downloadHandler.text); yield break; } string errorMsg = $"[API] {method} {endpoint} failed: {req.result}"; if (!string.IsNullOrEmpty(req.error)) { errorMsg = errorMsg + " - " + req.error; } if (req.responseCode > 0) { errorMsg += $" (HTTP {req.responseCode})"; } DownloadHandler downloadHandler = req.downloadHandler; string body = ((downloadHandler != null) ? downloadHandler.text : null) ?? ""; if (!string.IsNullOrEmpty(body)) { errorMsg = errorMsg + " - Response: " + body[..Math.Min(200, body.Length)]; } _lastCallApiError = $"HTTP {req.responseCode}: {body.Substring(0, Math.Min(120, body.Length))}"; Log("" + errorMsg + ""); } finally { ((IDisposable)req)?.Dispose(); } } private void ApplyApiHeaders(UnityWebRequest req) { if (req != null) { req.SetRequestHeader("Content-Type", "application/json"); req.SetRequestHeader("apikey", GetAuthToken()); req.SetRequestHeader("Authorization", "Bearer " + GetAuthToken()); req.SetRequestHeader("Prefer", "return=representation"); } } private IEnumerator ResolveProfile(string steamName) { string fullUrl = GetBaseApiUrl() + "/player?display_name=eq." + UnityWebRequest.EscapeURL(steamName); Log("[Init] GET /player (resolving profile for " + steamName + ")"); Log("[Init] Full URL: " + fullUrl.Substring(0, Math.Min(150, fullUrl.Length)) + "..."); UnityWebRequest req = UnityWebRequest.Get(fullUrl); try { ApplyApiHeaders(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { string raw = req.downloadHandler.text; JObject profile = ParseApiSingleObject(raw); if (profile == null) { Log("Profile sync failed: response was not valid JSON object."); yield break; } _userProfile = new PlayerProfile { id = (string)profile["id"], display_name = (string)profile["display_name"], region = (((string)profile["region"]) ?? "US"), state_province = (string)profile["state_province"] }; float.TryParse(((object)profile["current_mmr"])?.ToString(), out _userProfile.current_mmr); Log("Profile Sync: " + _userProfile.display_name + " from " + _userProfile.state_province); string picUrl = (string)profile["profile_pic_url"]; if (!string.IsNullOrEmpty(picUrl) && !_hasFetchedProfilePic) { ((MonoBehaviour)this).StartCoroutine(DownloadProfilePic(picUrl)); } } else { Log($"[Init] ResolveProfile failed: {req.result} - {req.error}"); } } finally { ((IDisposable)req)?.Dispose(); } } private List ParseApiObjectList(string rawJson) { if (string.IsNullOrEmpty(rawJson)) { return new List(); } try { JToken val = JToken.Parse(rawJson); JArray val2 = (JArray)(object)((val is JArray) ? val : null); if (val2 != null) { return ((IEnumerable)val2).OfType().ToList(); } JObject val3 = (JObject)(object)((val is JObject) ? val : null); if (val3 != null) { return new List { val3 }; } } catch (Exception ex) { Log("JSON parse warning (list): " + ex.Message + ""); } return new List(); } private JObject ParseApiSingleObject(string rawJson) { if (string.IsNullOrEmpty(rawJson)) { return null; } try { JToken val = JToken.Parse(rawJson); JObject val2 = (JObject)(object)((val is JObject) ? val : null); if (val2 != null) { return val2; } JArray val3 = (JArray)(object)((val is JArray) ? val : null); if (val3 != null) { return ((IEnumerable)val3).OfType().FirstOrDefault(); } } catch (Exception ex) { Log("JSON parse warning (single): " + ex.Message + ""); } return null; } private string GetOrDeriveUserUUID() { string text = PlayerPrefs.GetString("SBGLUserAuthUUID", ""); if (!string.IsNullOrEmpty(text)) { return text; } string text2 = _userProfile?.id ?? ""; if (Guid.TryParse(text2, out var _)) { return text2; } if (text2.Length > 0) { string text3 = text2.PadRight(32, '0').Substring(0, 32); return text3.Substring(0, 8) + "-" + text3.Substring(8, 4) + "-" + text3.Substring(12, 4) + "-" + text3.Substring(16, 4) + "-" + text3.Substring(20, 12); } return Guid.NewGuid().ToString(); } private static List ParseTextJsonArray(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return new List(); } try { JArray val = JArray.Parse(raw); return (from v in ((JContainer)val).Values() where v != null select v).ToList(); } catch { return new List(); } } private MatchmakingSession ParseSessionFromJson(JObject s) { if (s == null) { return null; } return new MatchmakingSession { id = (string)s["id"], lobby_name = (string)s["lobby_name"], lobby_password = (string)s["lobby_password"], host_player_id = (string)s["host_player_id"], status = (string)s["status"], match_id = (string)s["match_id"], steam_lobby_link = (string)s["steam_lobby_link"], match_type = (string)s["match_type"], player_ids = ParseTextJsonArray(((object)s["player_ids"])?.ToString()), accepted_player_ids = ParseTextJsonArray(((object)s["accepted_player_ids"])?.ToString()) }; } private IEnumerator DownloadProfilePic(string url) { UnityWebRequest req = UnityWebRequestTexture.GetTexture(url.Replace("http://", "https://")); try { yield return req.SendWebRequest(); if ((int)req.result == 1) { _profileTexture = DownloadHandlerTexture.GetContent(req); } _hasFetchedProfilePic = true; } finally { ((IDisposable)req)?.Dispose(); } } private void PlayMatchFoundAlert() { //IL_0007: 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) //IL_0014: Unknown result type (might be due to invalid IL or missing references) try { RuntimeManager.PlayOneShot(GameManager.AudioSettings.AnnouncerMainMenuTitle, default(Vector3)); Log("[Alert] ♪ Match found alert played via FMOD AnnouncerMainMenuTitle"); } catch (Exception ex) { Log("[Alert] Could not play match alert: " + ex.Message + ""); } } private string Truncate(string value, int maxLength) { if (string.IsNullOrEmpty(value)) { return "-"; } return (value.Length <= maxLength) ? value : (value.Substring(0, maxLength) + "..."); } private IEnumerator ValidateMatchUpload(Action callback) { if (PlayerPrefs.GetString("MatchType", "").Contains("pro_series")) { if (!_proSeriesSkipLogged) { Log("[Match] Pro Series match — automated upload skipped"); _proSeriesSkipLogged = true; } callback?.Invoke(obj: false); yield break; } if (_userProfile == null) { yield return EnsureUserProfileResolved("Match Upload Validation", 6f); } if (_userProfile == null) { Log("[Match Upload Validation] Profile is still null after waiting for resolution"); ShowUploadNotification("Upload validation failed: missing profile", "failure"); callback?.Invoke(obj: false); yield break; } string lobbyName = ResolveCurrentLobbyName(); if (IgnoreSbglLobbyRequirementEnabled) { Log("[Match Upload Validation] Upload All Matches enabled. Bypassing SBGL-* lobby requirement for '" + lobbyName + "'."); ShowUploadNotification("Uploading match results (Upload All Matches enabled)..."); callback?.Invoke(obj: true); } else if (string.IsNullOrEmpty(lobbyName) && (_isInGameplay || IsRankedTriggered || _currentSession != null)) { Log("[Match Upload Validation] Lobby name unresolved (blank). Allowing upload due to active ranked/session context."); ShowUploadNotification("Uploading match results (lobby name unresolved)..."); callback?.Invoke(obj: true); } else if (!IsSbglLobbyName(lobbyName)) { Log("[Match Upload Validation] Lobby name '" + lobbyName + "' does not match 'SBGL-*' pattern"); ShowUploadNotification("Upload blocked: Lobby '" + lobbyName + "' is not SBGL-*", "warning"); callback?.Invoke(obj: false); } else { Log("[Match Upload Validation] ✓ Lobby name matches pattern: " + lobbyName + ""); ShowUploadNotification("Uploading match results for " + lobbyName + "..."); Log("[Match Upload Validation] ✓ Validation passed - proceeding with upload"); callback?.Invoke(obj: true); } } private void ShowUploadNotification(string message, string level = "info") { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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) //IL_00c7: 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) if (_showUploadNoticesConfig == null || _showUploadNoticesConfig.Value) { _uploadNotification = message; _uploadNotificationTime = DateTime.UtcNow; switch (level) { case "success": _uploadNotificationColor = new Color(0.3f, 0.95f, 0.4f); break; case "failure": _uploadNotificationColor = new Color(1f, 0.45f, 0.45f); break; case "warning": _uploadNotificationColor = new Color(1f, 0.75f, 0.35f); break; default: _uploadNotificationColor = new Color(0.2f, 0.85f, 1f); break; } Log("[Upload Notification] " + message + ""); } } private string ResolveCurrentLobbyName() { string text = NormalizeLobbyName(_currentSession?.lobby_name); string text2 = NormalizeLobbyName(PlayerPrefs.GetString("LobbyName", "")); string text3 = NormalizeLobbyName(CompetitivePluginCheck._currentLobbyName); _debugLobbySessionSource = text; _debugLobbyPrefsSource = text2; _debugLobbyCapturedSource = text3; Log("[LobbyName] Sources: session='" + text + "', prefs='" + text2 + "', captured='" + text3 + "'"); if (IsSbglLobbyName(text3)) { _debugLobbyResolved = text3; _debugLobbyResolvedBy = "captured(sbgl)"; return text3; } if (IsSbglLobbyName(text)) { _debugLobbyResolved = text; _debugLobbyResolvedBy = "session(sbgl)"; return text; } if (IsSbglLobbyName(text2)) { _debugLobbyResolved = text2; _debugLobbyResolvedBy = "prefs(sbgl)"; return text2; } if (!string.IsNullOrEmpty(text3)) { _debugLobbyResolved = text3; _debugLobbyResolvedBy = "captured"; return text3; } if (!string.IsNullOrEmpty(text)) { _debugLobbyResolved = text; _debugLobbyResolvedBy = "session"; return text; } if (!string.IsNullOrEmpty(text2)) { _debugLobbyResolved = text2; _debugLobbyResolvedBy = "prefs"; return text2; } _debugLobbyResolved = ""; _debugLobbyResolvedBy = "none"; return ""; } private static string NormalizeLobbyName(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } return Regex.Replace(value, "<.*?>", string.Empty).Replace("\u200b", string.Empty).Trim(); } private static bool IsSbglLobbyName(string lobbyName) { return !string.IsNullOrEmpty(lobbyName) && lobbyName.StartsWith("SBGL-", StringComparison.OrdinalIgnoreCase); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static class LobbyPatch { public static void Prefix(ref string value) { if (SBGLPlugin.IsRankedTriggered) { string text = PlayerPrefs.GetString("LobbyName"); if (!string.IsNullOrEmpty(text)) { value = text; } } } } public class MatchResultSubmissionService { private const int SBGL_NET_CHANNEL = 2622; internal static string ReceivedP2PMatchId; private SBGLPlugin.MatchmakingSession _currentSession; private SBGLPlugin.PlayerProfile _userProfile; private string _currentMatchId; private Dictionary _playerMatchEntryIds = new Dictionary(); private Dictionary _lastSubmittedScores = new Dictionary(); private Dictionary _lastSubmittedScoresVsPar = new Dictionary(); private Dictionary _cachedLeaderboardScores = new Dictionary(); private Dictionary _cachedLeaderboardScoresVsPar = new Dictionary(); private bool _matchEntriesCreated = false; private bool _isInGameplay = false; private DateTime? _matchStartTime = null; private bool _matchStatsSubmitted = false; private Func _getBaseApiUrl; private Action _logger; private Func, IEnumerator> _callApi; private Func _parseApiSingleObject; private Action _startCoroutine; internal static void HandleIncomingMatchIdBroadcast(string matchId) { if (!string.IsNullOrEmpty(matchId)) { ReceivedP2PMatchId = matchId; SBGLPlugin.CurrentMatchId = matchId; } } internal static void BroadcastMatchId(string matchId, IEnumerable peers) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid || string.IsNullOrEmpty(matchId)) { return; } try { byte[] bytes = Encoding.UTF8.GetBytes("SBGL_MATCH_ID:" + matchId); int num = 0; foreach (ulong peer in peers) { try { SteamNetworking.SendP2PPacket(SteamId.op_Implicit(peer), bytes, -1, 2622, (P2PSend)2); num++; } catch { } } Debug.Log((object)$"[SBGL-MatchResult] Broadcast Match ID {matchId} to {num} peers"); } catch (Exception ex) { Debug.LogWarning((object)("[SBGL-MatchResult] BroadcastMatchId error: " + ex.Message)); } } public MatchResultSubmissionService(Func getBaseApiUrl, Action logger, Func, IEnumerator> callApi, Func parseApiSingleObject, Action startCoroutine) { _getBaseApiUrl = getBaseApiUrl; _logger = logger; _callApi = callApi; _parseApiSingleObject = parseApiSingleObject; _startCoroutine = startCoroutine; } private void Log(string message) { _logger?.Invoke(message); } private static bool IsProSeriesMatchType(string matchType) { return !string.IsNullOrWhiteSpace(matchType) && matchType.IndexOf("pro_series", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsCasualMatchType(string matchType) { return !string.IsNullOrWhiteSpace(matchType) && matchType.IndexOf("casual", StringComparison.OrdinalIgnoreCase) >= 0; } private IEnumerator ResolvePlayerIdByName(string playerName, Action onResolved) { if (onResolved == null) { yield break; } if (string.IsNullOrWhiteSpace(playerName)) { onResolved(null); yield break; } string normalizedName = playerName.Trim(); string escapedName = normalizedName.Replace("\\", "\\\\").Replace("\"", "\\\""); string resolvedId = null; yield return _callApi("/player?display_name=eq." + UnityWebRequest.EscapeURL(escapedName) + "&limit=1", "GET", "", delegate(string res) { if (string.IsNullOrWhiteSpace(res)) { return; } try { JToken val = JToken.Parse(res); JArray val2 = (JArray)(object)((val is JArray) ? val : null); JObject val3 = (JObject)((val2 != null) ? ((object)((IEnumerable)val2).OfType().FirstOrDefault()) : ((object)((val is JObject) ? val : null))); if (val3 != null) { resolvedId = (string)val3["id"]; } } catch (Exception ex) { Log("[Casual] Could not resolve player '" + normalizedName + "': " + ex.Message + ""); } }); onResolved(resolvedId); } private IEnumerator IncrementCasualMatchesPlayedForLeaderboard(List leaderboardPlayers) { List leaderboardNames = (from player in leaderboardPlayers?.Where((LiveLeaderboardPlugin.SBGLPlayer player) => player != null && !string.IsNullOrWhiteSpace(player.Name)) select player.Name.Trim()).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List(); if (leaderboardNames.Count == 0) { Log("[Casual] No active leaderboard names found - skipping casual stat increment"); yield break; } int updatedPlayers = 0; foreach (string leaderboardName in leaderboardNames) { string playerId = null; if (!string.IsNullOrWhiteSpace(_userProfile?.id) && string.Equals(leaderboardName, _userProfile.display_name, StringComparison.OrdinalIgnoreCase)) { playerId = _userProfile.id; } if (string.IsNullOrWhiteSpace(playerId)) { yield return ResolvePlayerIdByName(leaderboardName, delegate(string id) { playerId = id; }); } if (string.IsNullOrWhiteSpace(playerId)) { Log("[Casual] Could not resolve player ID for '" + leaderboardName + "' - skipping casual stat update"); continue; } int casualMatchesPlayed = 0; string resolvedDisplayName = leaderboardName; yield return _callApi("/player?id=eq." + playerId + "&select=*", "GET", "", delegate(string res) { JObject val = _parseApiSingleObject(res); if (val != null) { resolvedDisplayName = ((string)val["display_name"]) ?? resolvedDisplayName; int.TryParse(((object)val["casual_matches_played"])?.ToString() ?? "0", out casualMatchesPlayed); } }); int updatedCount = casualMatchesPlayed + 1; string json = "{" + $"\"casual_matches_played\":{updatedCount}" + "}"; bool updated = false; yield return _callApi("/player?id=eq." + playerId, "PATCH", json, delegate(string res) { if (_parseApiSingleObject(res) != null) { updated = true; } }); if (!updated) { Log("[Casual] Failed to update casual_matches_played for " + leaderboardName + ""); continue; } updatedPlayers++; Log($"[Casual] ✓ Updated {resolvedDisplayName} ({playerId}) casual_matches_played: {casualMatchesPlayed} -> {updatedCount}"); } _matchEntriesCreated = true; _matchStatsSubmitted = true; Log($"[Casual] Casual match handling complete - updated {updatedPlayers}/{leaderboardNames.Count} players"); } public void Initialize(SBGLPlugin.MatchmakingSession session, SBGLPlugin.PlayerProfile profile) { _currentSession = session; _userProfile = profile; _playerMatchEntryIds.Clear(); _lastSubmittedScores.Clear(); _lastSubmittedScoresVsPar.Clear(); _matchEntriesCreated = false; _matchStatsSubmitted = false; } public void SetCachedScores(Dictionary scores, Dictionary scoresVsPar) { _cachedLeaderboardScores = new Dictionary(scores ?? new Dictionary()); _cachedLeaderboardScoresVsPar = new Dictionary(scoresVsPar ?? new Dictionary()); } public IEnumerator CreateMatchAndEntries(List startingLeaderboard, List playerIds) { Log("[Match Creation] Starting new match for session " + _currentSession.id + ""); _matchStartTime = DateTime.UtcNow; string currentMatchType = _currentSession?.match_type ?? PlayerPrefs.GetString("MatchType", ""); if (IsProSeriesMatchType(currentMatchType)) { Log("[Match] Pro Series match — automated upload skipped"); _matchEntriesCreated = true; yield break; } if (IsCasualMatchType(currentMatchType)) { Log("[Casual] Casual match detected - skipping Match/MatchEntry upload and incrementing player stats instead"); yield return IncrementCasualMatchesPlayedForLeaderboard(startingLeaderboard); yield break; } string courseName = PlayerPrefs.GetString("SelectedCourse", "Unknown"); string matchType = PlayerPrefs.GetString("MatchType", "unranked"); int season = PlayerPrefs.GetInt("Season", 1); SBGLPlugin.MatchStats matchStats = new SBGLPlugin.MatchStats { matchmaking_session_id = _currentSession.id, match_id = _currentSession.id, player_id = _userProfile.id, player_name = _userProfile.display_name, match_date = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), duration_seconds = 0, is_host = false, status = "completed", course_name = courseName, match_type = matchType, season = season }; Log($"[Match Creation] Match configuration - Course: {courseName}, Type: {matchType}, Season: {season}"); string matchId = null; if (CompetitivePluginCheck.HasHigherVersionPeer()) { Log("[Match Creation] Higher-version peer detected — deferring Match creation to them, waiting for their Match ID..."); ReceivedP2PMatchId = null; float waitEnd = Time.realtimeSinceStartup + 15f; while (string.IsNullOrEmpty(ReceivedP2PMatchId) && Time.realtimeSinceStartup < waitEnd) { yield return null; } matchId = ReceivedP2PMatchId; if (!string.IsNullOrEmpty(matchId)) { Log("[Match Creation] ✓ Adopted Match ID from higher-version peer: " + matchId + ""); } else { Log("[Match Creation] No Match ID received from higher-version peer within 15s — submitting own Match record"); yield return SubmitMatchEntry(matchStats, delegate(string id) { matchId = id; }); } } else { yield return SubmitMatchEntry(matchStats, delegate(string id) { matchId = id; }); } if (string.IsNullOrEmpty(matchId)) { Log("[Match Creation] Failed to create Match record"); yield break; } _currentMatchId = matchId; Log("[Match Creation] ✓ Match created: " + matchId + ""); yield return _callApi("/matchmaking_session?id=eq." + _currentSession.id, "PATCH", "{\"match_id\":\"" + matchId + "\"}", delegate(string res) { try { JObject val = _parseApiSingleObject(res); if (val != null) { Log("[Match Creation] ✓ MatchmakingSession " + _currentSession.id + " linked to match: " + matchId + ""); } else { Log("[Match Creation] Could not confirm MatchmakingSession update"); } } catch (Exception ex) { Log("[Match Creation] Error updating MatchmakingSession: " + ex.Message + ""); } }); _playerMatchEntryIds.Clear(); _lastSubmittedScores.Clear(); _lastSubmittedScoresVsPar.Clear(); foreach (string playerId in playerIds) { string playerName = null; string preMatchMmr = null; int gamePoints = 0; int scoreVsPar = 0; if (playerId == _userProfile.id) { playerName = _userProfile.display_name; preMatchMmr = _userProfile.current_mmr.ToString(); Log("[Match Creation] Current player: " + playerName + " (MMR: " + preMatchMmr + ")"); } else { yield return _callApi("/player?id=eq." + playerId + "&select=*", "GET", "", delegate(string res) { try { JObject val = _parseApiSingleObject(res); if (val != null) { playerName = (string)val["display_name"]; if (string.IsNullOrEmpty(playerName)) { Log("[Match Creation] Player " + playerId + " has no display_name, using ID"); playerName = playerId; } object obj = val["current_mmr"]; if (obj != null) { preMatchMmr = obj.ToString(); } Log("[Match Creation] Fetched player: " + playerName + " (MMR: " + preMatchMmr + ")"); } else { Log("[Match Creation] Failed to fetch Player " + playerId + " - response null"); playerName = playerId; } } catch (Exception ex) { Log("[Match Creation] Error fetching Player " + playerId + ": " + ex.Message + ""); playerName = playerId; } }); } if (!string.IsNullOrEmpty(playerName)) { if (_cachedLeaderboardScores.TryGetValue(playerName, out var score)) { gamePoints = score; _cachedLeaderboardScoresVsPar.TryGetValue(playerName, out scoreVsPar); } _lastSubmittedScores[playerName] = gamePoints; _lastSubmittedScoresVsPar[playerName] = scoreVsPar; } int startingPosition = GetPlayerFinishPosition(playerName, startingLeaderboard); int adjustedScore = gamePoints + scoreVsPar * -10; string mmrField = ((!string.IsNullOrEmpty(preMatchMmr)) ? (",\"pre_match_mmr\":" + preMatchMmr) : ""); string posField = ((startingPosition > 0) ? $",\"finish_position\":{startingPosition}" : ""); UnifiedPlugin instance = UnifiedPlugin.Instance; string modVer = ((instance == null) ? null : ((BaseUnityPlugin)instance).Info.Metadata.Version?.ToString()) ?? "unknown"; string json = "{\"match_id\":\"" + matchId + "\",\"player_id\":\"" + playerId + "\",\"player_name\":\"" + (playerName ?? "Unknown") + "\"," + $"\"game_points\":{gamePoints}," + $"\"over_under\":{scoreVsPar}," + $"\"score_vs_par\":{scoreVsPar}," + $"\"adjusted_match_score\":{adjustedScore}" + mmrField + posField + ",\"notes\":\"Progressive match tracking - created at round start (Mod v" + modVer + ")\"}"; Log("[Match Creation] JSON: " + json + ""); string entryId = null; yield return _callApi("/match_entry", "POST", json, delegate(string res) { try { JObject val = _parseApiSingleObject(res); if (val != null) { entryId = (string)val["id"]; _playerMatchEntryIds[playerId] = entryId; Log("[Match Creation] ✓ MatchEntry created for " + playerName + ": " + entryId + ""); } } catch (Exception ex) { Log("[Match Creation] Could not parse MatchEntry: " + ex.Message + ""); } }); } _matchEntriesCreated = true; Log("[Match Creation] ✓ Match and entries initialized. Starting score monitoring..."); _isInGameplay = true; _startCoroutine?.Invoke(MonitorAndUpdateScores()); } public IEnumerator FinalizeMatchStats(List finalLeaderboard) { string currentMatchType = _currentSession?.match_type ?? PlayerPrefs.GetString("MatchType", ""); if (IsProSeriesMatchType(currentMatchType) || IsCasualMatchType(currentMatchType)) { _matchStatsSubmitted = true; yield break; } yield return (object)new WaitForSeconds(1.5f); Log("[Match Finalize] Performing final score update..."); if (finalLeaderboard == null || finalLeaderboard.Count == 0) { Log("[Match Finalize] No leaderboard data available"); _matchStatsSubmitted = true; yield break; } Log($"[Match Finalize] Using final snapshot with {finalLeaderboard.Count} players"); foreach (LiveLeaderboardPlugin.SBGLPlayer player in finalLeaderboard) { if (player != null) { int finalGamePoints = player.BaseScore; int finalScoreVsPar = 0; int finishPosition = GetPlayerFinishPosition(player.Name, finalLeaderboard); if (!string.IsNullOrEmpty(player.RawStrokes)) { string strokeStr = player.RawStrokes.Replace("±", "").Trim(); int.TryParse(strokeStr, out finalScoreVsPar); } string playerId = null; string entryId = null; if (player.Name == _userProfile.display_name) { playerId = _userProfile.id; } if (!string.IsNullOrEmpty(playerId) && _playerMatchEntryIds.TryGetValue(playerId, out entryId)) { Log($"[Match Finalize] Final update for {player.Name}: {finalGamePoints} pts, {finalScoreVsPar} vs par, Position: {finishPosition}"); yield return UpdateMatchEntry(entryId, playerId, player.Name, finalGamePoints, finalScoreVsPar, finishPosition); } } } _matchStatsSubmitted = true; Log("[Match Finalize] ✓ Match stats finalized"); } private IEnumerator MonitorAndUpdateScores() { Log("[Match Monitor] Starting score monitoring for gameplay"); while (_isInGameplay && _currentMatchId != null) { yield return (object)new WaitForSeconds(2f); } Log("[Match Monitor] Score monitoring ended"); } private IEnumerator UpdateMatchEntry(string entryId, string playerId, string playerName, int gamePoints, int scoreVsPar, int finishPosition = 0) { Log($"[Score Update] Hole completed for {playerName}: {gamePoints} pts, {scoreVsPar} vs par"); int adjustedScore = gamePoints + scoreVsPar * -10; UnifiedPlugin instance = UnifiedPlugin.Instance; string modVer = ((instance == null) ? null : ((BaseUnityPlugin)instance).Info.Metadata.Version?.ToString()) ?? "unknown"; string json = "{" + $"\"game_points\":{gamePoints}," + $"\"over_under\":{scoreVsPar}," + $"\"score_vs_par\":{scoreVsPar}," + $"\"adjusted_match_score\":{adjustedScore}," + $"\"finish_position\":{finishPosition}," + "\"notes\":\"Updated after hole completion (Mod v" + modVer + ")\"}"; yield return _callApi("/match_entry?id=eq." + entryId, "PATCH", json, delegate(string res) { try { JObject val = _parseApiSingleObject(res); if (val != null) { Log("[Score Update] ✓ MatchEntry updated for " + playerName + ""); } } catch (Exception ex) { Log("[Score Update] Could not update MatchEntry: " + ex.Message + ""); } }); } private IEnumerator SubmitMatchEntry(SBGLPlugin.MatchStats stats, Action onMatchIdReceived) { if (stats == null || _currentSession == null) { yield break; } string seasonId = "69de6bf4fb103cb0d5eb00c5"; bool isProSeries = IsProSeriesMatchType(stats.match_type); string apiMatchType = (isProSeries ? "pro_series" : "mmr"); string mode = (isProSeries ? "Pro Series" : "Ranked"); JObject val = new JObject { ["matchmaking_session_id"] = JToken.op_Implicit(_currentSession.id), ["season_id"] = JToken.op_Implicit(seasonId), ["match_date"] = JToken.op_Implicit(stats.match_date), ["match_type"] = JToken.op_Implicit(apiMatchType), ["course_name"] = JToken.op_Implicit(stats.course_name), ["player_count"] = JToken.op_Implicit(2), ["status"] = JToken.op_Implicit("Pending"), ["submitted_by_name"] = JToken.op_Implicit(stats.player_name), ["mode"] = JToken.op_Implicit(mode) }; UnifiedPlugin instance = UnifiedPlugin.Instance; val["notes"] = JToken.op_Implicit("Auto-submitted via SBGL Unified Mod v" + (((instance == null) ? null : ((BaseUnityPlugin)instance).Info.Metadata.Version?.ToString()) ?? "unknown")); JObject payload = val; if (isProSeries) { payload["pro_series_season_id"] = JToken.op_Implicit("69b59af135403a1cb7b113c8"); int proSeriesWeek = PlayerPrefs.GetInt("ProSeriesWeek", 0); if (proSeriesWeek > 0) { payload["pro_series_week"] = JToken.op_Implicit(proSeriesWeek); } string proSeriesEventName = PlayerPrefs.GetString("ProSeriesEventName", ""); if (!string.IsNullOrWhiteSpace(proSeriesEventName)) { payload["pro_series_event_name"] = JToken.op_Implicit(proSeriesEventName); } } string json = ((JToken)payload).ToString((Formatting)0, Array.Empty()); Log("[Match Stats] Submitting Match entry to API"); Log("[Match Stats] Full URL: " + _getBaseApiUrl() + "/match"); Log("[Match Stats] Payload: " + json + ""); yield return _callApi("/match", "POST", json, delegate(string res) { JObject val2 = _parseApiSingleObject(res); if (val2 != null) { string text = ((string)val2["id"]) ?? "unknown"; Log("[Match Stats] ✓ Match entry created (ID: " + text + ")"); onMatchIdReceived?.Invoke(text); } else { Log("[Match Stats] Response received but could not parse ID"); } }); } private int GetPlayerFinishPosition(string playerName, List leaderboard) { if (string.IsNullOrEmpty(playerName) || leaderboard == null || leaderboard.Count == 0) { return 0; } Dictionary dictionary = BuildFinishPositionMap(leaderboard); if (dictionary.TryGetValue(playerName, out var value)) { return value; } return 0; } private int ParseScoreVsPar(string rawStrokes) { if (string.IsNullOrWhiteSpace(rawStrokes)) { return 0; } string s = rawStrokes.Replace("±", "").Trim(); int.TryParse(s, out var result); return result; } private Dictionary BuildFinishPositionMap(List leaderboard) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (leaderboard == null || leaderboard.Count == 0) { return dictionary; } var list = (from p in leaderboard where p != null && !string.IsNullOrWhiteSpace(p.Name) select new { Player = p, Stroke = ParseScoreVsPar(p.RawStrokes) } into x orderby x.Player.BaseScore descending, x.Stroke select x).ThenBy(x => x.Player.Name, StringComparer.OrdinalIgnoreCase).ToList(); int num = 1; foreach (var item in list) { if (!dictionary.ContainsKey(item.Player.Name)) { dictionary[item.Player.Name] = num++; } } return dictionary; } } } namespace SBGLLiveLeaderboard { public class LiveLeaderboardPlugin : MonoBehaviour { public class SBGLPlayer { public string Name; public int BaseScore; public string RawStrokes; public string MMR = "..."; public string ProjectedDisplay = "..."; public bool IsSpectator; } private ConfigEntry _configMaxPlayers; private ConfigEntry _configViewToggleKey; private const float K_FACTOR = 24f; private float _updateInterval = 30f; private float _nextUpdateTime = 0f; private List _persistentLeaderboard = new List(); private List _finalLeaderboardSnapshot = new List(); private bool _showFinalSnapshot = false; private readonly Dictionary _lastKnownRoundPlayers = new Dictionary(StringComparer.OrdinalIgnoreCase); private bool _roundCacheActive = false; private bool _isInSbglGameplay = false; private bool _cachedInMenu = false; private bool _cachedInDrivingRange = false; private string _cachedSceneName = string.Empty; private int _cachedTotalHoles = 9; private string _cachedCourseName = ""; private int _cachedHoleGlobalIndex = -1; public static bool SbglOverlayOpen = false; private static bool _useNativeScoreboard = PlayerPrefs.GetInt("LL_UseNative", 1) == 1; private Dictionary _mmrCache = new Dictionary(); private HashSet _pendingRequests = new HashSet(); private FieldInfo _entriesField; private Scoreboard _cachedScoreboard; private GUIStyle _centeredStyle; private GUIStyle _headerStyle; private GUIStyle _nameStyle; private GUIStyle _posStyle; private GUIStyle _negStyle; private GUIStyle _solidStyle; private GUIStyle _toggleStyle; private Texture2D _whiteTexture; private Texture2D _toggleTex; private Texture2D _toggleHoverTex; public static LiveLeaderboardPlugin Instance { get; private set; } private int ConfigMaxPlayers => _configMaxPlayers?.Value ?? PlayerPrefs.GetInt("LL_MaxPlayers", 16); private Key ViewToggleKey { get { //IL_000e: Unknown result type (might be due to invalid IL or missing references) ConfigEntry configViewToggleKey = _configViewToggleKey; return (Key)((configViewToggleKey == null) ? 101 : ((int)configViewToggleKey.Value)); } } public static bool UseNativeScoreboard { get { return _useNativeScoreboard; } set { _useNativeScoreboard = value; PlayerPrefs.SetInt("LL_UseNative", value ? 1 : 0); } } public void SetConfig(ConfigEntry maxPlayers, ConfigEntry viewToggleKey = null) { _configMaxPlayers = maxPlayers; _configViewToggleKey = viewToggleKey; } private void Awake() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) Instance = this; if (!PlayerPrefs.HasKey("LL_MaxPlayers")) { PlayerPrefs.SetInt("LL_MaxPlayers", 16); } _entriesField = typeof(Scoreboard).GetField("entries", BindingFlags.Instance | BindingFlags.NonPublic); _whiteTexture = new Texture2D(1, 1); _whiteTexture.SetPixel(0, 0, Color.white); _whiteTexture.Apply(); UnifiedPlugin.ApiConfigChanged += OnApiConfigChanged; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void OnApiConfigChanged() { _mmrCache.Clear(); _pendingRequests.Clear(); _nextUpdateTime = Time.time; } private void Update() { //IL_000c: 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) Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current[ViewToggleKey]).wasPressedThisFrame) { UseNativeScoreboard = !UseNativeScoreboard; } Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? string.Empty; if (text != _cachedSceneName) { _cachedSceneName = text; string text2 = text.ToLowerInvariant(); _cachedInMenu = text2.Contains("menu"); _cachedInDrivingRange = text2.Contains("driving") || text2.Contains("range"); } bool cachedInDrivingRange = _cachedInDrivingRange; bool cachedInMenu = _cachedInMenu; if (cachedInDrivingRange) { _showFinalSnapshot = true; } else { _showFinalSnapshot = false; } if (cachedInMenu) { ResetForMainMenu(); } if (SbglOverlayOpen && !CourseManager.ForceDisplayScoreboard && !Scoreboard.IsVisible) { SbglOverlayOpen = false; } _isInSbglGameplay = !cachedInMenu && SbglOverlayOpen; if (_isInSbglGameplay) { MatchSetupMenu val = (SingletonNetworkBehaviour.HasInstance ? SingletonNetworkBehaviour.Instance : null); if ((Object)(object)val != (Object)null) { _cachedTotalHoles = val.randomCupNumHoles; } int currentHoleGlobalIndex = CourseManager.CurrentHoleGlobalIndex; if (currentHoleGlobalIndex != _cachedHoleGlobalIndex) { _cachedHoleGlobalIndex = currentHoleGlobalIndex; try { LocalizedString currentHoleLocalizedName = CourseManager.GetCurrentHoleLocalizedName(); _cachedCourseName = ((currentHoleLocalizedName != null) ? currentHoleLocalizedName.GetLocalizedString() : ""); } catch { _cachedCourseName = ""; } } } if (Time.time >= _nextUpdateTime) { ScrapeData(); _nextUpdateTime = Time.time + _updateInterval; } } private void OnGUI() { //IL_002c: 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_0044: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00c2: Expected O, but got Unknown //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_0116: 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_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_0142: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: 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_01e0: 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_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Expected O, but got Unknown //IL_025e: Expected O, but got Unknown //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) float num = (float)Screen.height / 1080f; if (_solidStyle == null) { _centeredStyle = new GUIStyle(GUI.skin.label) { fontSize = Mathf.RoundToInt(11f * num), alignment = (TextAnchor)4, richText = true, wordWrap = false }; _headerStyle = new GUIStyle(_centeredStyle) { fontStyle = (FontStyle)1, wordWrap = false }; _nameStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)3, richText = true, wordWrap = false, fontSize = Mathf.RoundToInt(12f * num) }; GUIStyle val = new GUIStyle(_centeredStyle); val.normal.textColor = Color.green; _posStyle = val; GUIStyle val2 = new GUIStyle(_centeredStyle); val2.normal.textColor = new Color(1f, 0.45f, 0.45f); _negStyle = val2; GUIStyle val3 = new GUIStyle(); val3.normal.background = _whiteTexture; val3.border = new RectOffset(0, 0, 0, 0); _solidStyle = val3; _toggleTex = MakeTex(new Color(0.1f, 0.36f, 0.58f, 0.88f)); _toggleHoverTex = MakeTex(new Color(0.14f, 0.46f, 0.7f, 0.95f)); GUIStyle val4 = new GUIStyle(GUI.skin.button); val4.normal.background = _toggleTex; val4.normal.textColor = Color.white; val4.hover.background = _toggleHoverTex; val4.hover.textColor = Color.white; val4.active.background = _toggleHoverTex; val4.active.textColor = Color.white; val4.focused.background = _toggleTex; val4.focused.textColor = Color.white; val4.fontSize = Mathf.RoundToInt(10f * num); val4.fontStyle = (FontStyle)1; val4.alignment = (TextAnchor)4; val4.border = new RectOffset(2, 2, 2, 2); _toggleStyle = val4; } if (UseNativeScoreboard && Scoreboard.IsVisible) { float num2 = 160f * num; float num3 = 20f * num; GUI.Label(new Rect(((float)Screen.width - num2) / 2f, (float)Screen.height * 0.055f, num2, num3), $"[{ViewToggleKey}] Switch to SBGL View", _toggleStyle); } else if (_isInSbglGameplay) { List players = ((_showFinalSnapshot && _finalLeaderboardSnapshot.Count > 0) ? _finalLeaderboardSnapshot : _persistentLeaderboard); float edgePad = 4f * num; float headerH = 26f * num; float rowH = 22f * num; DrawReplacementLeaderboard(players, num, edgePad, headerH, rowH); } } private static Texture2D MakeTex(Color col) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, col); val.Apply(); return val; } private void DrawReplacementLeaderboard(List players, float scale, float edgePad, float headerH, float rowH) { //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_00ff: 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_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected O, but got Unknown //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0237: 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_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03fd: Expected O, but got Unknown //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_0413: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Expected O, but got Unknown //IL_042f: Unknown result type (might be due to invalid IL or missing references) //IL_0450: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_04b3: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_0518: 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_052b: Unknown result type (might be due to invalid IL or missing references) //IL_0540: Expected O, but got Unknown //IL_0547: Unknown result type (might be due to invalid IL or missing references) //IL_0555: Unknown result type (might be due to invalid IL or missing references) //IL_055a: Unknown result type (might be due to invalid IL or missing references) //IL_056f: Expected O, but got Unknown //IL_0576: Unknown result type (might be due to invalid IL or missing references) //IL_0590: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Expected O, but got Unknown //IL_059e: Unknown result type (might be due to invalid IL or missing references) //IL_05ba: Unknown result type (might be due to invalid IL or missing references) //IL_0b89: Unknown result type (might be due to invalid IL or missing references) //IL_0b8e: Unknown result type (might be due to invalid IL or missing references) //IL_0ba1: Unknown result type (might be due to invalid IL or missing references) //IL_0ba9: Unknown result type (might be due to invalid IL or missing references) //IL_0bb3: Expected O, but got Unknown //IL_0bc9: Unknown result type (might be due to invalid IL or missing references) //IL_0be5: Unknown result type (might be due to invalid IL or missing references) //IL_0bfd: Unknown result type (might be due to invalid IL or missing references) //IL_0c02: Unknown result type (might be due to invalid IL or missing references) //IL_0c15: Unknown result type (might be due to invalid IL or missing references) //IL_0c1f: Expected O, but got Unknown //IL_0c35: Unknown result type (might be due to invalid IL or missing references) //IL_0c81: Unknown result type (might be due to invalid IL or missing references) //IL_0c4e: Unknown result type (might be due to invalid IL or missing references) //IL_061f: Unknown result type (might be due to invalid IL or missing references) //IL_0632: Unknown result type (might be due to invalid IL or missing references) //IL_0ca3: Unknown result type (might be due to invalid IL or missing references) //IL_066e: Unknown result type (might be due to invalid IL or missing references) //IL_0681: Unknown result type (might be due to invalid IL or missing references) //IL_06e6: Unknown result type (might be due to invalid IL or missing references) //IL_06bc: Unknown result type (might be due to invalid IL or missing references) //IL_06cf: Unknown result type (might be due to invalid IL or missing references) //IL_079f: Unknown result type (might be due to invalid IL or missing references) //IL_07a6: Expected O, but got Unknown //IL_07ad: Unknown result type (might be due to invalid IL or missing references) //IL_0764: Unknown result type (might be due to invalid IL or missing references) //IL_0774: Unknown result type (might be due to invalid IL or missing references) //IL_078a: Unknown result type (might be due to invalid IL or missing references) //IL_086a: Unknown result type (might be due to invalid IL or missing references) //IL_086f: Unknown result type (might be due to invalid IL or missing references) //IL_07c8: Unknown result type (might be due to invalid IL or missing references) //IL_07cd: Unknown result type (might be due to invalid IL or missing references) //IL_07d7: Expected O, but got Unknown //IL_07ed: Unknown result type (might be due to invalid IL or missing references) //IL_0800: Unknown result type (might be due to invalid IL or missing references) //IL_0821: Unknown result type (might be due to invalid IL or missing references) //IL_084a: Unknown result type (might be due to invalid IL or missing references) //IL_0881: Expected O, but got Unknown //IL_08b5: Unknown result type (might be due to invalid IL or missing references) //IL_08e2: Unknown result type (might be due to invalid IL or missing references) //IL_0922: Unknown result type (might be due to invalid IL or missing references) //IL_0929: Expected O, but got Unknown //IL_08a2: Unknown result type (might be due to invalid IL or missing references) //IL_094b: Unknown result type (might be due to invalid IL or missing references) //IL_0955: Expected O, but got Unknown //IL_0950: Unknown result type (might be due to invalid IL or missing references) //IL_0955: Unknown result type (might be due to invalid IL or missing references) //IL_095f: Unknown result type (might be due to invalid IL or missing references) //IL_0969: Expected O, but got Unknown //IL_0964: Unknown result type (might be due to invalid IL or missing references) //IL_0969: Unknown result type (might be due to invalid IL or missing references) //IL_0974: Unknown result type (might be due to invalid IL or missing references) //IL_093f: Unknown result type (might be due to invalid IL or missing references) //IL_0984: Unknown result type (might be due to invalid IL or missing references) //IL_09b2: Unknown result type (might be due to invalid IL or missing references) //IL_09bb: Unknown result type (might be due to invalid IL or missing references) //IL_0a24: Unknown result type (might be due to invalid IL or missing references) //IL_0a29: Unknown result type (might be due to invalid IL or missing references) //IL_0a33: Expected O, but got Unknown //IL_0a49: Unknown result type (might be due to invalid IL or missing references) //IL_0a5c: Unknown result type (might be due to invalid IL or missing references) //IL_09dc: Unknown result type (might be due to invalid IL or missing references) //IL_09e9: Unknown result type (might be due to invalid IL or missing references) //IL_09f2: Unknown result type (might be due to invalid IL or missing references) //IL_0ad2: Unknown result type (might be due to invalid IL or missing references) //IL_0ad9: Expected O, but got Unknown //IL_0b13: Unknown result type (might be due to invalid IL or missing references) //IL_0afd: Unknown result type (might be due to invalid IL or missing references) //IL_0aea: Unknown result type (might be due to invalid IL or missing references) //IL_0b53: Unknown result type (might be due to invalid IL or missing references) if (_solidStyle == null) { return; } string text = CompetitivePluginCheck._currentLobbyName ?? ""; string matchTypeDisplayName = GetMatchTypeDisplayName(PlayerPrefs.GetString("MatchType", "")); string text2 = SBGLPlugin.CurrentMatchId ?? ""; bool showFinalSnapshot = _showFinalSnapshot; float num = (float)Screen.width * 0.52f; float num2 = 5f * scale; float num3 = 30f * scale; float num4 = 22f * scale; float num5 = 26f * scale; float num6 = 4f * scale; float num7 = 10f * scale; int num8 = Mathf.Max(players.Count, 1); float num9 = Mathf.Max(num7 * 1.8f, 18f * scale); float num10 = num2 + num3 + num4 + (float)num8 * num5 + num9; float num11 = ((float)Screen.width - num) / 2f; float num12 = (float)Screen.height * 0.055f; Color backgroundColor = GUI.backgroundColor; Color backgroundColor2 = default(Color); ColorUtility.TryParseHtmlString("#FEFAEE", ref backgroundColor2); GUI.backgroundColor = backgroundColor2; GUI.Box(new Rect(num11, num12, num, num10), GUIContent.none, _solidStyle); Color val = default(Color); ColorUtility.TryParseHtmlString("#1ABD60", ref val); GUI.backgroundColor = val; GUI.Box(new Rect(num11, num12, num, num2), GUIContent.none, _solidStyle); GUI.backgroundColor = backgroundColor; float num13 = num12 + num2; Color textColor = default(Color); ((Color)(ref textColor))..ctor(0.12f, 0.12f, 0.14f); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.35f, 0.35f, 0.38f); Color textColor2 = val; GUIStyle val3 = new GUIStyle(_headerStyle) { fontSize = Mathf.RoundToInt(13f * scale), alignment = (TextAnchor)3 }; val3.normal.textColor = textColor2; GUIStyle val4 = new GUIStyle(_headerStyle) { fontSize = Mathf.RoundToInt(14f * scale), alignment = (TextAnchor)4 }; val4.normal.textColor = textColor; GUIStyle val5 = new GUIStyle(_headerStyle) { fontSize = Mathf.RoundToInt(11f * scale), alignment = (TextAnchor)5 }; val5.normal.textColor = (Color)(showFinalSnapshot ? new Color(0.8f, 0.45f, 0.05f) : val2); GUI.Label(new Rect(num11 + num7, num13, num * 0.32f, num3), "SBGL SEASON 2", val3); GUI.Label(new Rect(num11, num13, num, num3), text, val4); int num14 = CourseManager.CurrentHoleCourseIndex + 1; string text3 = ((num14 > 0) ? $"{num14}/{_cachedTotalHoles}" : ""); string text4 = matchTypeDisplayName + (string.IsNullOrEmpty(_cachedCourseName) ? "" : (" · " + _cachedCourseName)) + (string.IsNullOrEmpty(text3) ? "" : (" · " + text3)) + (showFinalSnapshot ? " · FINAL" : " · LIVE"); GUI.Label(new Rect(num11, num13, num - num7, num3), text4, val5); num13 += num3; GUI.backgroundColor = new Color(0.78f, 0.74f, 0.68f, 1f); GUI.Box(new Rect(num11 + num7, num13 - 1f, num - num7 * 2f, 1f), GUIContent.none, _solidStyle); GUI.backgroundColor = backgroundColor; float num15 = num - num6 - num7 * 2f; float num16 = num15 * 0.07f; float num17 = num15 * 0.37f; float num18 = num15 * 0.28f; float num19 = num15 * 0.12f; float num20 = num15 * 0.16f; GUIStyle val6 = new GUIStyle(_headerStyle) { fontSize = Mathf.RoundToInt(9f * scale), alignment = (TextAnchor)4 }; val6.normal.textColor = val2; GUIStyle val7 = new GUIStyle(val6) { alignment = (TextAnchor)3 }; float num21 = num11 + num6 + num7; GUI.Label(new Rect(num21, num13, num16, num4), "POS", val6); num21 += num16; GUI.Label(new Rect(num21, num13, num17, num4), "PLAYER", val7); num21 += num17; GUI.Label(new Rect(num21, num13, num18, num4), "MMR", val6); num21 += num18; GUI.Label(new Rect(num21, num13, num19, num4), "PTS", val6); num21 += num19; GUI.Label(new Rect(num21, num13, num20, num4), "+/-", val6); num13 += num4; GUI.backgroundColor = new Color(0.78f, 0.74f, 0.68f, 1f); GUI.Box(new Rect(num11 + num7, num13, num - num7 * 2f, 1f), GUIContent.none, _solidStyle); GUI.backgroundColor = backgroundColor; GUIStyle val8 = new GUIStyle(_nameStyle) { fontSize = Mathf.RoundToInt(13f * scale) }; val8.normal.textColor = textColor; GUIStyle val9 = new GUIStyle(_centeredStyle) { fontSize = Mathf.RoundToInt(12f * scale) }; val9.normal.textColor = textColor; if (players.Count == 0) { GUIStyle val10 = new GUIStyle(val9); val10.normal.textColor = val2; GUI.Label(new Rect(num11 + num7, num13, num - num7 * 2f, num5), "Waiting for player data...", val10); return; } int num22 = 0; Color backgroundColor3 = default(Color); Color textColor3 = default(Color); for (int i = 0; i < players.Count; i++) { SBGLPlayer sBGLPlayer = players[i]; if (!sBGLPlayer.IsSpectator) { num22++; } if (sBGLPlayer.IsSpectator) { GUI.backgroundColor = new Color(0f, 0f, 0f, 0.06f); GUI.Box(new Rect(num11, num13, num, num5), GUIContent.none, _solidStyle); } else if (num22 == 1) { GUI.backgroundColor = new Color(1f, 0.92f, 0.55f, 0.35f); GUI.Box(new Rect(num11, num13, num, num5), GUIContent.none, _solidStyle); } else if (num22 % 2 == 0) { GUI.backgroundColor = new Color(0f, 0f, 0f, 0.04f); GUI.Box(new Rect(num11, num13, num, num5), GUIContent.none, _solidStyle); } GUI.backgroundColor = backgroundColor; float result = 0f; float.TryParse(sBGLPlayer.MMR, NumberStyles.Float, CultureInfo.InvariantCulture, out result); string text5 = (sBGLPlayer.IsSpectator ? "#555555" : ((sBGLPlayer.MMR == "..." || sBGLPlayer.MMR == "--") ? "#888888" : Season2RuleSet.GetRankColor(result))); if (ColorUtility.TryParseHtmlString(text5, ref backgroundColor3)) { GUI.backgroundColor = backgroundColor3; GUI.Box(new Rect(num11, num13, num6, num5), GUIContent.none, _solidStyle); GUI.backgroundColor = backgroundColor; } num21 = num11 + num6 + num7; GUIStyle val11 = new GUIStyle(val9); val11.normal.textColor = val2; if (sBGLPlayer.IsSpectator) { GUIStyle val12 = new GUIStyle(val9) { fontStyle = (FontStyle)1 }; val12.normal.textColor = new Color(0.55f, 0.55f, 0.6f); GUI.Label(new Rect(num21, num13, num16, num5), "\ud83d\udc41", val12); num21 += num16; GUI.Label(new Rect(num21, num13, num17, num5), sBGLPlayer.Name, val11); num21 += num17; GUI.Label(new Rect(num21, num13, num18 + num19 + num20, num5), "SPECTATING", val11); num13 += num5; continue; } GUIStyle val13 = new GUIStyle(val9) { fontStyle = (FontStyle)(num22 == 1) }; if (num22 == 1) { val13.normal.textColor = new Color(0.6f, 0.45f, 0.02f); } GUI.Label(new Rect(num21, num13, num16, num5), $"{num22}", val13); num21 += num16; GUI.Label(new Rect(num21, num13, num17, num5), sBGLPlayer.Name, val8); num21 += num17; string mMR = sBGLPlayer.MMR; string text6 = sBGLPlayer.ProjectedDisplay.Replace(mMR, "").Trim(); GUIStyle val14 = new GUIStyle(val9); if (ColorUtility.TryParseHtmlString(text5, ref textColor3)) { val14.normal.textColor = textColor3; } Vector2 val15 = val14.CalcSize(new GUIContent(mMR)); Vector2 val16 = _centeredStyle.CalcSize(new GUIContent(text6)); float num23 = 3f * scale; float num24 = val15.x + (string.IsNullOrEmpty(text6) ? 0f : (val16.x + num23)); float num25 = num21 + num18 / 2f - num24 / 2f; GUI.Label(new Rect(num25, num13, val15.x, num5), mMR, val14); if (!string.IsNullOrEmpty(text6)) { GUI.Label(new Rect(num25 + val15.x + num23, num13, val16.x, num5), text6, text6.Contains("+") ? _posStyle : _negStyle); } num21 += num18; GUIStyle val17 = new GUIStyle(val9) { fontStyle = (FontStyle)1 }; val17.normal.textColor = new Color(0.72f, 0.52f, 0.02f); GUI.Label(new Rect(num21, num13, num19, num5), $"{sBGLPlayer.BaseScore}", val17); num21 += num19; int result2 = 0; if (!string.IsNullOrEmpty(sBGLPlayer.RawStrokes)) { int.TryParse(sBGLPlayer.RawStrokes.Replace("±", "").Replace("+", "").Trim(), out result2); } GUIStyle val18 = new GUIStyle(val9); val18.normal.textColor = (Color)((result2 > 0) ? new Color(0.8f, 0.15f, 0.15f) : ((result2 < 0) ? new Color(0.1f, 0.6f, 0.2f) : val2)); string text7 = ((result2 > 0) ? $"+{result2}" : ((result2 < 0) ? result2.ToString() : "E")); GUI.Label(new Rect(num21, num13, num20, num5), text7, val18); num13 += num5; } GUIStyle val19 = new GUIStyle(_centeredStyle) { fontSize = Mathf.RoundToInt(11f * scale), fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; val19.normal.textColor = new Color(0.18f, 0.85f, 0.32f); GUI.Label(new Rect(num11 + num7, num13, num * 0.45f, num9), "SBGLeague.com", val19); GUIStyle val20 = new GUIStyle(_centeredStyle) { fontSize = Mathf.RoundToInt(10f * scale), alignment = (TextAnchor)5 }; val20.normal.textColor = new Color(0.45f, 0.5f, 0.6f); string text8 = (string.IsNullOrWhiteSpace(text2) ? $"[{ViewToggleKey}] Native View" : $"[{ViewToggleKey}] Native View · Match: {((text2.Length > 14) ? text2.Substring(text2.Length - 14) : text2)}"); GUI.Label(new Rect(num11, num13, num - num7 * 0.5f, num9), text8, val20); } private string GetMatchTypeDisplayName(string matchType) { if (Season2RuleSet.IsRankedMatchType(matchType)) { return "Ranked"; } if (Season2RuleSet.IsProSeriesMatchType(matchType)) { return "Pro Series"; } if (Season2RuleSet.IsCasualMatchType(matchType)) { return "Casual"; } return "Unknown"; } public void OnScoreboardRefreshed(Scoreboard scoreboard) { if (!((Object)(object)scoreboard == (Object)null)) { _cachedScoreboard = scoreboard; ScrapeData(scoreboard); } } private void ScrapeData(Scoreboard inScoreboard = null) { //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) Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name.Contains("Driving") || name.Contains("Range")) { _roundCacheActive = false; return; } if (!_roundCacheActive) { _lastKnownRoundPlayers.Clear(); _roundCacheActive = true; } Scoreboard val = inScoreboard ?? _cachedScoreboard ?? Object.FindAnyObjectByType((FindObjectsInactive)1); if ((Object)(object)val != (Object)null) { _cachedScoreboard = val; } if ((Object)(object)_cachedScoreboard == (Object)null || _entriesField == null || !(_entriesField.GetValue(_cachedScoreboard) is List { Count: not 0 } list)) { return; } List list2 = new List(); foreach (ScoreboardEntry item in list) { if ((Object)(object)item == (Object)null) { continue; } TextMeshProUGUI name2 = item.name; string text = CleanTMP((name2 != null) ? ((TMP_Text)name2).text : null); if (string.IsNullOrEmpty(text) || text == "Name") { continue; } TextMeshProUGUI strokes = item.strokes; string text2 = CleanTMP((strokes != null) ? ((TMP_Text)strokes).text : null); TextMeshProUGUI courseScore = item.courseScore; string text3 = CleanTMP((courseScore != null) ? ((TMP_Text)courseScore).text : null); bool flag = !string.IsNullOrEmpty(text2) && text2.ToUpper().Contains("SPEC"); if (!flag) { bool flag2 = Regex.IsMatch(text2 ?? string.Empty, "\\d"); bool flag3 = Regex.IsMatch(text3 ?? string.Empty, "\\d"); bool flag4 = !string.IsNullOrEmpty(text2) && Regex.IsMatch(text2, "^[\\-\\u2013\\u2014\\s]+$"); if (!flag2 && !flag3 && flag4) { continue; } } int.TryParse(text3, out var result); string text4 = "..."; if (_mmrCache.TryGetValue(text, out (string, float) value)) { (text4, _) = value; if (Time.time - value.Item2 > 300f && !_pendingRequests.Contains(text)) { ((MonoBehaviour)this).StartCoroutine(GetMMRForPlayer(text)); } } else if (!_pendingRequests.Contains(text)) { ((MonoBehaviour)this).StartCoroutine(GetMMRForPlayer(text)); } list2.Add(new SBGLPlayer { Name = text, BaseScore = result, RawStrokes = (flag ? "SPEC" : text2), MMR = text4, ProjectedDisplay = text4, IsSpectator = flag }); } HashSet hashSet = new HashSet(list2.Select((SBGLPlayer p) => p.Name), StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair lastKnownRoundPlayer in _lastKnownRoundPlayers) { if (!hashSet.Contains(lastKnownRoundPlayer.Key) && lastKnownRoundPlayer.Value != null) { list2.Add(ClonePlayer(lastKnownRoundPlayer.Value)); } } List list3 = list2.Where((SBGLPlayer p) => !p.IsSpectator).ToList(); List second = list2.Where((SBGLPlayer p) => p.IsSpectator).ToList(); if (list3.Count > 1) { CalculateProjectedMMR(list3); } _persistentLeaderboard = list3.OrderByDescending((SBGLPlayer p) => p.BaseScore).Take(ConfigMaxPlayers).Concat(second) .ToList(); _lastKnownRoundPlayers.Clear(); foreach (SBGLPlayer item2 in _persistentLeaderboard) { if (item2 != null && !string.IsNullOrEmpty(item2.Name)) { _lastKnownRoundPlayers[item2.Name] = ClonePlayer(item2); } } } private SBGLPlayer ClonePlayer(SBGLPlayer source) { if (source == null) { return null; } return new SBGLPlayer { Name = source.Name, BaseScore = source.BaseScore, RawStrokes = source.RawStrokes, MMR = source.MMR, ProjectedDisplay = source.ProjectedDisplay, IsSpectator = source.IsSpectator }; } private void CalculateProjectedMMR(List players) { Dictionary dictionary = new Dictionary(); foreach (SBGLPlayer player in players) { dictionary[player.Name] = 0f; } for (int i = 0; i < players.Count; i++) { for (int j = i + 1; j < players.Count; j++) { SBGLPlayer sBGLPlayer = players[i]; SBGLPlayer sBGLPlayer2 = players[j]; if (float.TryParse(sBGLPlayer.MMR, out var result) && float.TryParse(sBGLPlayer2.MMR, out var result2)) { float num = 1f / (1f + Mathf.Pow(10f, (result2 - result) / 400f)); float num2 = ((sBGLPlayer.BaseScore > sBGLPlayer2.BaseScore) ? 1f : ((sBGLPlayer.BaseScore < sBGLPlayer2.BaseScore) ? 0f : 0.5f)); float num3 = 24f * (num2 - num); dictionary[sBGLPlayer.Name] += num3; dictionary[sBGLPlayer2.Name] -= num3; } } } foreach (SBGLPlayer player2 in players) { if (float.TryParse(player2.MMR, out var _)) { int num4 = Mathf.RoundToInt(dictionary[player2.Name]); player2.ProjectedDisplay = string.Format("{0} ({1}{2})", player2.MMR, (num4 >= 0) ? "+" : "", num4); } } } private IEnumerator GetMMRForPlayer(string originalName) { if (_pendingRequests.Contains(originalName)) { yield break; } _pendingRequests.Add(originalName); string url = UnifiedPlugin.GetCurrentPlayerApi() + "?display_name=ilike." + UnityWebRequest.EscapeURL(originalName); bool needsFallback = false; UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", UnifiedPlugin.GetCurrentAuthToken()); r.SetRequestHeader("Authorization", "Bearer " + UnifiedPlugin.GetCurrentAuthToken()); yield return r.SendWebRequest(); if ((int)r.result == 1) { JArray res = JArray.Parse(r.downloadHandler.text); if (((JContainer)res).Count > 0) { _mmrCache[originalName] = (((object)res[0][(object)"current_mmr"])?.ToString() ?? "--", Time.time); } else { needsFallback = true; } } else { needsFallback = true; } } finally { ((IDisposable)r)?.Dispose(); } if (needsFallback) { yield return GetMMRFuzzyFallback(originalName); } _pendingRequests.Remove(originalName); } private IEnumerator GetMMRFuzzyFallback(string originalName) { string url = UnifiedPlugin.GetCurrentPlayerApi() + "?display_name=ilike.*" + UnityWebRequest.EscapeURL(originalName) + "*"; UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", UnifiedPlugin.GetCurrentAuthToken()); r.SetRequestHeader("Authorization", "Bearer " + UnifiedPlugin.GetCurrentAuthToken()); yield return r.SendWebRequest(); string val = "--"; if ((int)r.result == 1) { JArray res = JArray.Parse(r.downloadHandler.text); if (((JContainer)res).Count > 0) { val = ((object)res[0][(object)"current_mmr"])?.ToString() ?? "--"; } } _mmrCache[originalName] = (val, Time.time); } finally { ((IDisposable)r)?.Dispose(); } } private string CleanTMP(string input) { return string.IsNullOrEmpty(input) ? "" : Regex.Replace(input, "<.*?>", string.Empty).Trim(); } public SBGLPlayer GetPlayerLeaderboardData(string playerName) { return _persistentLeaderboard.FirstOrDefault((SBGLPlayer p) => p.Name.Equals(playerName, StringComparison.OrdinalIgnoreCase)); } public List GetCurrentLeaderboard() { return new List(_persistentLeaderboard); } private void ResetForMainMenu() { if (_persistentLeaderboard.Count != 0 || _finalLeaderboardSnapshot.Count != 0 || _mmrCache.Count != 0) { _persistentLeaderboard.Clear(); _finalLeaderboardSnapshot.Clear(); _showFinalSnapshot = false; _lastKnownRoundPlayers.Clear(); _roundCacheActive = false; _mmrCache.Clear(); _pendingRequests.Clear(); _cachedScoreboard = null; SbglOverlayOpen = false; _nextUpdateTime = Time.time + _updateInterval; Debug.Log((object)"[LiveLeaderboard] Reset leaderboard state on main menu"); } } public void CaptureLeaderboardSnapshot() { _finalLeaderboardSnapshot = (from p in _persistentLeaderboard.Select(ClonePlayer) where p != null select p).ToList(); Debug.Log((object)$"[LiveLeaderboard] Captured final snapshot: {_finalLeaderboardSnapshot.Count} players"); } public List GetFinalLeaderboardSnapshot() { if (_finalLeaderboardSnapshot.Count > 0) { return (from p in _finalLeaderboardSnapshot.Select(ClonePlayer) where p != null select p).ToList(); } return (from p in _persistentLeaderboard.Select(ClonePlayer) where p != null select p).ToList(); } } } namespace SBGL.UnifiedMod { public static class MyPluginInfo { public const string PLUGIN_GUID = "SBGL.UnifiedMod"; public const string PLUGIN_NAME = "Super Battle Golf League Unified Mod"; public const string PLUGIN_VERSION = "0.1.20"; } } namespace SBGL.UnifiedMod.Utils { public class BypassCertificate : CertificateHandler { protected override bool ValidateCertificate(byte[] certificateData) { return true; } } public static class ApiUtilities { public static IEnumerator MakeApiRequest(string url, Dictionary headers, Action onComplete, ManualLogSource logger = null) { UnityWebRequest req = UnityWebRequest.Get(url); try { req.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); if (headers != null) { foreach (KeyValuePair kvp in headers) { req.SetRequestHeader(kvp.Key, kvp.Value); } } yield return req.SendWebRequest(); if ((int)req.result == 1) { onComplete?.Invoke(arg1: true, req.downloadHandler.text); yield break; } string errorMsg = $"HTTP {req.responseCode}: {req.error}"; DownloadHandler downloadHandler = req.downloadHandler; if (!string.IsNullOrEmpty((downloadHandler != null) ? downloadHandler.text : null)) { errorMsg = errorMsg + " | Response: " + req.downloadHandler.text; } if (logger != null) { logger.LogWarning((object)("[API] Request failed: " + errorMsg)); } onComplete?.Invoke(arg1: false, errorMsg); } finally { ((IDisposable)req)?.Dispose(); } } public static JObject ParseJsonResponse(string json) { try { return JObject.Parse(json); } catch (Exception ex) { throw new InvalidOperationException("Failed to parse JSON response: " + ex.Message); } } public static JArray ParseJsonArrayResponse(string json) { try { return JArray.Parse(json); } catch (Exception ex) { throw new InvalidOperationException("Failed to parse JSON array response: " + ex.Message); } } } public static class PlayerProfileFetcher { public class PlayerProfile { public string ID; public string DisplayName; public float CurrentMMR; public string Region; public string ProfilePicUrl; } public static IEnumerator FetchPlayerProfile(string playerIdOrName, string playerApiUrl, string appId, string authToken, Action onComplete, ManualLogSource logger = null) { if (string.IsNullOrEmpty(playerIdOrName)) { onComplete?.Invoke(arg1: false, null); yield break; } string url = (playerIdOrName.All(char.IsDigit) ? (playerApiUrl + "?id=eq." + UnityWebRequest.EscapeURL(playerIdOrName)) : (playerApiUrl + "?display_name=ilike." + UnityWebRequest.EscapeURL(playerIdOrName))); Dictionary headers = new Dictionary { { "apikey", authToken }, { "Authorization", "Bearer " + authToken } }; bool found = false; PlayerProfile profile = null; yield return ApiUtilities.MakeApiRequest(url, headers, delegate(bool success, string response) { if (success) { try { JArray val = ApiUtilities.ParseJsonArrayResponse(response); if (((JContainer)val).Count > 0) { profile = new PlayerProfile { ID = (((object)val[0][(object)"id"])?.ToString() ?? ""), DisplayName = (((object)val[0][(object)"display_name"])?.ToString() ?? ""), Region = (((object)val[0][(object)"region"])?.ToString() ?? "US"), ProfilePicUrl = ((object)val[0][(object)"profile_pic_url"])?.ToString(), CurrentMMR = (float.TryParse(((object)val[0][(object)"current_mmr"])?.ToString() ?? "0", out var result) ? result : 0f) }; found = true; ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("[PlayerProfileFetcher] ✓ Found profile: " + profile.DisplayName + " (ID: " + profile.ID + ")")); } } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogWarning((object)("[PlayerProfileFetcher] No results for: " + playerIdOrName)); } } return; } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogError((object)("[PlayerProfileFetcher] Parse error: " + ex.Message)); } return; } } ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogError((object)("[PlayerProfileFetcher] Request failed: " + response)); } }, logger); onComplete?.Invoke(found, profile); } public static IEnumerator FuzzySearchPlayer(string playerName, string playerApiUrl, string appId, string authToken, Action onComplete, ManualLogSource logger = null) { string url = playerApiUrl + "?display_name=ilike.*" + UnityWebRequest.EscapeURL(playerName) + "*"; Dictionary headers = new Dictionary { { "apikey", authToken }, { "Authorization", "Bearer " + authToken } }; bool found = false; PlayerProfile profile = null; yield return ApiUtilities.MakeApiRequest(url, headers, delegate(bool success, string response) { if (success) { try { JArray val = ApiUtilities.ParseJsonArrayResponse(response); if (((JContainer)val).Count > 0) { profile = new PlayerProfile { ID = (((object)val[0][(object)"id"])?.ToString() ?? ""), DisplayName = (((object)val[0][(object)"display_name"])?.ToString() ?? ""), Region = (((object)val[0][(object)"region"])?.ToString() ?? "US"), ProfilePicUrl = ((object)val[0][(object)"profile_pic_url"])?.ToString(), CurrentMMR = (float.TryParse(((object)val[0][(object)"current_mmr"])?.ToString() ?? "0", out var result) ? result : 0f) }; found = true; ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("[PlayerProfileFetcher] ✓ Fuzzy match: " + profile.DisplayName)); } } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogWarning((object)("[PlayerProfileFetcher] No fuzzy matches for: " + playerName)); } } return; } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogError((object)("[PlayerProfileFetcher] Fuzzy parse error: " + ex.Message)); } return; } } ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogError((object)("[PlayerProfileFetcher] Fuzzy request failed: " + response)); } }, logger); onComplete?.Invoke(found, profile); } } } namespace SBGL.UnifiedMod.Patches { [HarmonyPatch] public static class LeaderboardPatches { [HarmonyPatch(typeof(Scoreboard), "UpdateVisibility")] [HarmonyPrefix] public static bool PatchUpdateVisibility(Scoreboard __instance) { if (LiveLeaderboardPlugin.UseNativeScoreboard) { return true; } bool flag = (bool)AccessTools.Field(typeof(Scoreboard), "wantsToShowExternal").GetValue(__instance) || CourseManager.ForceDisplayScoreboard; bool flag2 = (bool)AccessTools.Field(typeof(Scoreboard), "isVisible").GetValue(__instance); LiveLeaderboardPlugin.SbglOverlayOpen = flag; if (flag != flag2) { AccessTools.Field(typeof(Scoreboard), "isVisible").SetValue(__instance, flag); if (flag) { try { AccessTools.Method(typeof(Scoreboard), "Refresh", (Type[])null, (Type[])null).Invoke(__instance, null); } catch (Exception ex) { Debug.LogWarning((object)("[LeaderboardPatches] Refresh failed: " + ex.Message)); } } } return false; } [HarmonyPatch(typeof(Scoreboard), "Refresh")] [HarmonyPostfix] public static void PostfixRefresh(Scoreboard __instance) { try { LiveLeaderboardPlugin instance = LiveLeaderboardPlugin.Instance; if ((Object)(object)instance != (Object)null) { instance.OnScoreboardRefreshed(__instance); } } catch (Exception ex) { Debug.LogWarning((object)("[LeaderboardPatches] PostfixRefresh error: " + ex.Message)); } } } [HarmonyPatch] public static class RulePatches { private static ManualLogSource _logger; private static ConfigEntry _applyRulesets; public static void SetLogger(ManualLogSource logger) { _logger = logger; } public static void SetApplyRulesetsConfig(ConfigEntry applyRulesets) { _applyRulesets = applyRulesets; } private static void Log(string message) { if (_logger != null) { _logger.LogInfo((object)("[RulePatches] " + message)); } } private static void LogError(string message) { if (_logger != null) { _logger.LogError((object)("[RulePatches] " + message)); } } [HarmonyPostfix] [HarmonyPatch(typeof(MatchSetupMenu), "OnStartClient")] public static void PatchMatchSetupMenuOnStartClient(MatchSetupMenu __instance) { try { if (!((NetworkBehaviour)__instance).isServer) { return; } ConfigEntry applyRulesets = _applyRulesets; if (applyRulesets == null || !applyRulesets.Value) { Log("ApplyRulesets is disabled in config — skipping rule enforcement"); return; } string text = PlayerPrefs.GetString("MatchType", ""); if (!Season2RuleSet.IsManagedMatchType(text) && (string.IsNullOrEmpty(text) || !text.Contains("season_1")) && !string.Equals(text, "casual", StringComparison.OrdinalIgnoreCase)) { Log("Not a managed ruleset match (MatchType='" + text + "'), skipping"); return; } MatchSetupRules rules = __instance.rules; if ((Object)(object)rules == (Object)null) { LogError("__instance.rules is null"); return; } Log("=== APPLYING SEASON 2 RULES (OnStartClient) ==="); Log(" Match Type: " + text); Log(" Host Ruleset: " + PlayerPrefs.GetString("HostRuleset", "ranked")); ApplyRulesToMatchSetup(rules); ApplyCourseSelection(__instance); Log("============================"); } catch (Exception ex) { LogError("Exception in PatchMatchSetupMenuOnStartClient: " + ex.Message); } } public static void ApplyRulesToMatchSetup(MatchSetupRules matchSetup) { //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) string text = PlayerPrefs.GetString("HostRuleset", "ranked"); bool flag = text == "pro_series"; bool flag2 = text == "casual"; matchSetup.SetPreset((Preset)0); Log("✓ Reset to Classic preset"); Dictionary dictionary = (flag2 ? Season2RuleSet.GetCasualRulesSettings() : ((!flag) ? Season2RuleSet.GetRankedRulesSettings() : Season2RuleSet.GetProSeriesRulesSettings())); int num = 0; foreach (KeyValuePair item in dictionary) { try { matchSetup.SetValue(item.Key, item.Value); SliderOption value2; DropdownOption value3; if (matchSetup.onOffDropdownLookup.TryGetValue(item.Key, out var value)) { value.SetValue((!matchSetup.GetValueAsBoolInternal(item.Key)) ? 1 : 0); } else if (matchSetup.sliderLookup.TryGetValue(item.Key, out value2)) { value2.SetValue(matchSetup.GetValueInternal(item.Key)); } else if (matchSetup.dropdownLookup.TryGetValue(item.Key, out value3)) { value3.SetValue((int)matchSetup.GetValueInternal(item.Key)); } matchSetup.UpdateRule(item.Key); Log($" ✓ Set {item.Key} = {item.Value}"); num++; } catch (Exception ex) { LogError($" ✗ Failed to set {item.Key}: {ex.Message}"); } } Log($"✓ Applied {num}/{dictionary.Count} Season 2 rules (item weights at game defaults)"); } public static void ApplyCourseSelection(MatchSetupMenu menu) { string text = PlayerPrefs.GetString("HostRuleset", "ranked"); bool flag = text == "pro_series"; bool flag2 = text == "casual"; if (flag) { Log(" Pro Series: skipping course selection (maps set manually)"); return; } if (flag2) { Log(" Casual: skipping course selection (maps set manually)"); return; } List allHoles = GameManager.AllCourses.allHoles; List list = new List(allHoles); if (list.Count == 0) { LogError(" No eligible holes found — aborting course selection"); return; } MatchSetupMenu.CustomCourseData.OverrideHoles(list.ToArray()); menu.SetCourse(-1, false); menu.NetworkrandomEnabled = true; menu.courseRandomToggle.isOn = true; menu.NetworkrandomCupNumHoles = 9; menu.numberOfHolesSlider.value = 9f; Log($" ✓ Set {list.Count} eligible holes, random order ON, 9 holes"); } } } namespace SBGL.UnifiedMod.Features { public class PseudoDedicatedServer : MonoBehaviour { private ConfigEntry _enabled; private ConfigEntry _requeueDelay; private ConfigEntry _defaultRuleset; private ManualLogSource _logger; private bool _inMainMenu = false; private bool _inDrivingRange = false; private bool _hasQueuedThisCycle = false; private bool _hasClaimedHostRole = false; private bool _hasAcceptedThisCycle = false; private bool _hasHostedThisCycle = false; private bool _waitingToReturnToMenu = false; private float _returnToMenuAt = 0f; private SBGLPlugin _matchmakingPlugin; public void SetConfig(ConfigEntry enabled, ConfigEntry requeueDelay, ConfigEntry defaultRuleset, ManualLogSource logger) { _enabled = enabled; _requeueDelay = requeueDelay; _defaultRuleset = defaultRuleset; _logger = logger; } private void Awake() { SceneManager.sceneLoaded += OnSceneLoaded; Log("[PDS] Pseudo Dedicated Server component loaded."); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { string text = ((Scene)(ref scene)).name.ToLower(); _inMainMenu = text.Contains("menu"); _inDrivingRange = text.Contains("drivingrange") || text.Contains("driving range"); if (!IsEnabled()) { return; } if (_inMainMenu) { Log("[PDS] Entered main menu — resetting cycle state."); _hasQueuedThisCycle = false; _hasClaimedHostRole = false; _hasAcceptedThisCycle = false; _hasHostedThisCycle = false; _waitingToReturnToMenu = false; ((MonoBehaviour)this).StartCoroutine(TryAutoQueueAfterDelay(2.5f)); } else if (_inDrivingRange) { Log("[PDS] Entered driving range."); if (!_waitingToReturnToMenu) { _waitingToReturnToMenu = true; float num = _requeueDelay?.Value ?? 10f; _returnToMenuAt = Time.time + num; Log($"[PDS] Will return to main menu in {num}s to requeue."); } } else { _waitingToReturnToMenu = false; } } private void Update() { if (IsEnabled()) { if (_inDrivingRange && _waitingToReturnToMenu && Time.time >= _returnToMenuAt) { _waitingToReturnToMenu = false; Log("[PDS] Requeue delay elapsed — returning to main menu."); ReturnToMainMenu(); } else if (_inMainMenu) { PollSessionActions(); } } } private void PollSessionActions() { SBGLPlugin matchmakingPlugin = GetMatchmakingPlugin(); if ((Object)(object)matchmakingPlugin == (Object)null) { return; } SBGLPlugin.MatchmakingSession currentSession = matchmakingPlugin.CurrentSession; if (currentSession == null) { return; } if (!_hasClaimedHostRole && !matchmakingPlugin.IsHost && (currentSession.status == "pending_accept" || currentSession.status == "ready")) { _hasClaimedHostRole = true; Log("[PDS] Claiming host role (PATCHing host_player_id)..."); ((MonoBehaviour)this).StartCoroutine(ClaimThenAccept(matchmakingPlugin)); return; } if (_hasClaimedHostRole && !_hasAcceptedThisCycle && !matchmakingPlugin.HasAccepted && currentSession.status == "pending_accept") { _hasAcceptedThisCycle = true; Log("[PDS] Auto-accepting match..."); ((MonoBehaviour)this).StartCoroutine(matchmakingPlugin.AcceptMatchCoroutine()); } if (currentSession.status == "ready" && matchmakingPlugin.IsHost && matchmakingPlugin.HasAccepted && !_hasHostedThisCycle) { _hasHostedThisCycle = true; Log("[PDS] Auto-initializing host lobby (ruleset: " + GetRuleset() + ")..."); PlayerPrefs.SetString("HostRuleset", GetRuleset()); PlayerPrefs.Save(); matchmakingPlugin.InitiateHostSequencePublic(); } } private IEnumerator ClaimThenAccept(SBGLPlugin plugin) { yield return plugin.ClaimHostRoleCoroutine(); if (plugin.CurrentSession != null && plugin.CurrentSession.status == "pending_accept" && !_hasAcceptedThisCycle && !plugin.HasAccepted) { _hasAcceptedThisCycle = true; Log("[PDS] Claimed host — auto-accepting match..."); yield return plugin.AcceptMatchCoroutine(); } } private IEnumerator TryAutoQueueAfterDelay(float delay) { yield return (object)new WaitForSeconds(delay); if (!IsEnabled() || !_inMainMenu || _hasQueuedThisCycle) { yield break; } SBGLPlugin plugin = GetMatchmakingPlugin(); if ((Object)(object)plugin == (Object)null) { Log("[PDS] SBGLPlugin not found — cannot auto-queue."); yield break; } if (plugin.IsQueueing || plugin.CurrentSession != null) { Log("[PDS] Already queuing or in session — skipping auto-queue."); _hasQueuedThisCycle = true; yield break; } if (plugin.UserProfile == null) { Log("[PDS] Profile not yet resolved — waiting up to 15s..."); float waited = 0f; while (plugin.UserProfile == null && waited < 15f) { yield return (object)new WaitForSeconds(1f); waited += 1f; } if (plugin.UserProfile == null) { Log("[PDS] Profile still unresolved — aborting auto-queue."); yield break; } } _hasQueuedThisCycle = true; Log("[PDS] Auto-joining queue..."); ((MonoBehaviour)this).StartCoroutine(plugin.MatchmakingLoopCoroutine()); } private void ReturnToMainMenu() { Log("[PDS] Loading MainMenu scene..."); try { SceneManager.LoadScene("MainMenu"); } catch (Exception ex) { Log("[PDS] LoadScene('MainMenu') failed (" + ex.Message + ") — trying scene index 0."); try { SceneManager.LoadScene(0); } catch (Exception ex2) { Log("[PDS] Fallback LoadScene(0) also failed: " + ex2.Message); } } } private bool IsEnabled() { return _enabled?.Value ?? false; } private string GetRuleset() { return _defaultRuleset?.Value ?? "ranked"; } private SBGLPlugin GetMatchmakingPlugin() { if ((Object)(object)_matchmakingPlugin != (Object)null) { return _matchmakingPlugin; } _matchmakingPlugin = Object.FindAnyObjectByType(); return _matchmakingPlugin; } private void Log(string msg) { if (_logger != null) { _logger.LogInfo((object)msg); } else { Debug.Log((object)msg); } } } public class RuleSetDisplayManager : MonoBehaviour { private ConfigEntry _posX; private ConfigEntry _posY; private ConfigEntry _showDetails; private ConfigEntry _applyRulesets; private GUIStyle _labelStyle; private GUIStyle _smallBtnStyle; private GUIStyle _tooltipStyle; private Texture2D _bgTexture; private Texture2D _tooltipBgTexture; private string _tooltipRanked = null; private string _tooltipPro = null; private string _tooltipCasual = null; private string _noRulesetTooltip = "Applies Classic preset only.\nNo Season 2 rules enforced."; private string _lastSceneName = string.Empty; private bool _defaultAppliedForScene = false; public void SetConfig(ConfigEntry posX, ConfigEntry posY, ConfigEntry showDetails, ConfigEntry applyRulesets = null) { _posX = posX; _posY = posY; _showDetails = showDetails; _applyRulesets = applyRulesets; } public void Awake() { Debug.Log((object)"[RuleSetDisplayManager] Awake - Feature loaded"); SceneManager.sceneLoaded += OnSceneLoaded; } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { string text = ((Scene)(ref scene)).name.ToLower(); if (text.Contains("menu")) { PlayerPrefs.SetString("HostRuleset", "ranked"); PlayerPrefs.SetString("MatchType", "ranked_season_2"); PlayerPrefs.SetInt("Season", 2); PlayerPrefs.Save(); Debug.Log((object)"[RuleSetDisplayManager] Returned to main menu — ruleset reset to ranked"); } } public void OnGUI() { //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) try { Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name.ToLower(); if (!string.Equals(text, _lastSceneName, StringComparison.Ordinal)) { _lastSceneName = text; _defaultAppliedForScene = false; } if ((text.Contains("drivingrange") || text.Contains("driving range")) && NetworkServer.active) { EnsureDefaultRankedForScene(); RenderPanel(); } } catch (Exception ex) { Debug.LogError((object)("[RuleSetDisplayManager] OnGUI error: " + ex.Message + "\n" + ex.StackTrace)); } } private void EnsureDefaultRankedForScene() { if (!_defaultAppliedForScene) { _defaultAppliedForScene = true; PlayerPrefs.SetString("HostRuleset", "ranked"); PlayerPrefs.SetString("MatchType", "ranked_season_2"); PlayerPrefs.SetInt("Season", 2); string value = PlayerPrefs.GetString("SelectedCourse", ""); if (string.IsNullOrWhiteSpace(value)) { MapPoolConfig.Course randomApprovedCourse = MapPoolConfig.GetRandomApprovedCourse(); PlayerPrefs.SetString("SelectedCourse", randomApprovedCourse.Name); } PlayerPrefs.Save(); Debug.Log((object)"[RuleSetDisplayManager] Defaulted ruleset to ranked for this driving-range session"); } } private void RenderPanel() { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) //IL_0042: 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_0057: Expected O, but got Unknown //IL_0027: 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_0088: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_017d: 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_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_01b8: Expected O, but got Unknown //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Expected O, but got Unknown //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Expected O, but got Unknown //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d5: Expected O, but got Unknown //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Expected O, but got Unknown //IL_0431: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_043c: 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_0482: Unknown result type (might be due to invalid IL or missing references) //IL_0494: Unknown result type (might be due to invalid IL or missing references) //IL_04f0: Unknown result type (might be due to invalid IL or missing references) //IL_04da: Unknown result type (might be due to invalid IL or missing references) //IL_04fb: Unknown result type (might be due to invalid IL or missing references) //IL_0548: Unknown result type (might be due to invalid IL or missing references) //IL_0541: Unknown result type (might be due to invalid IL or missing references) //IL_0553: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_05a8: Unknown result type (might be due to invalid IL or missing references) //IL_05ae: Invalid comparison between Unknown and I4 //IL_05bf: Unknown result type (might be due to invalid IL or missing references) //IL_05c4: Unknown result type (might be due to invalid IL or missing references) //IL_05cb: Unknown result type (might be due to invalid IL or missing references) //IL_0762: Unknown result type (might be due to invalid IL or missing references) //IL_0794: Unknown result type (might be due to invalid IL or missing references) //IL_07cc: Unknown result type (might be due to invalid IL or missing references) //IL_0804: Unknown result type (might be due to invalid IL or missing references) //IL_05e4: Unknown result type (might be due to invalid IL or missing references) //IL_05fd: Unknown result type (might be due to invalid IL or missing references) //IL_0616: Unknown result type (might be due to invalid IL or missing references) //IL_0648: Unknown result type (might be due to invalid IL or missing references) //IL_064f: Expected O, but got Unknown //IL_0660: Unknown result type (might be due to invalid IL or missing references) //IL_066f: Unknown result type (might be due to invalid IL or missing references) //IL_0691: Unknown result type (might be due to invalid IL or missing references) //IL_06d0: Unknown result type (might be due to invalid IL or missing references) //IL_06b6: Unknown result type (might be due to invalid IL or missing references) if (_labelStyle == null) { GUISkin skin = GUI.skin; _labelStyle = new GUIStyle((GUIStyle)(((object)((skin != null) ? skin.label : null)) ?? ((object)new GUIStyle()))) { alignment = (TextAnchor)3, fontSize = 14, fontStyle = (FontStyle)1, richText = true }; } if (_smallBtnStyle == null) { GUISkin skin2 = GUI.skin; _smallBtnStyle = new GUIStyle((GUIStyle)(((object)((skin2 != null) ? skin2.button : null)) ?? ((object)new GUIStyle()))) { fontSize = 10, fontStyle = (FontStyle)1, richText = true }; } if ((Object)(object)_bgTexture == (Object)null) { _bgTexture = new Texture2D(1, 1); _bgTexture.SetPixel(0, 0, new Color(0.1f, 0.1f, 0.1f, 0.8f)); _bgTexture.Apply(); } if ((Object)(object)_tooltipBgTexture == (Object)null) { _tooltipBgTexture = new Texture2D(1, 1); _tooltipBgTexture.SetPixel(0, 0, new Color(0.05f, 0.05f, 0.05f, 0.95f)); _tooltipBgTexture.Apply(); } if (_tooltipStyle == null) { GUISkin skin3 = GUI.skin; _tooltipStyle = new GUIStyle((GUIStyle)(((object)((skin3 != null) ? skin3.box : null)) ?? ((object)new GUIStyle()))) { alignment = (TextAnchor)0, fontSize = 14, wordWrap = true, richText = true, padding = new RectOffset(8, 8, 6, 6) }; _tooltipStyle.normal.background = _tooltipBgTexture; _tooltipStyle.normal.textColor = Color.white; } bool flag = _showDetails?.Value ?? false; ConfigEntry applyRulesets = _applyRulesets; string text = ((applyRulesets != null && applyRulesets.Value) ? PlayerPrefs.GetString("HostRuleset", "ranked") : "none"); float num = 450f; float num2 = 40f; float num3 = (flag ? 110f : 0f); float num4 = 30f + num2 + num3 + 10f; float num5 = _posX?.Value ?? (-1f); float num6 = ((num5 < 0f) ? ((float)Screen.width - num - 20f) : num5); float num7 = _posY?.Value ?? 20f; GUI.DrawTexture(new Rect(num6, num7, num, num4), (Texture)(object)_bgTexture); GUI.Box(new Rect(num6, num7, num, num4), "APPLY RULESET"); float num8 = num7 + 28f; float num9 = (num - 50f) / 4f; float num10 = num6 + 10f; float num11 = num10 + num9 + 10f; float num12 = num11 + num9 + 10f; float num13 = num12 + num9 + 10f; if (_tooltipRanked == null) { int count = MapPoolConfig.GetApprovedCourses().Count; _tooltipRanked = Season2RuleSet.BuildRulesDescription(Season2RuleSet.GetRankedRulesSettings()) + "\nItems: Game defaults" + $"\nCourses: Random ({count} maps)" + "\nHoles: 9"; } if (_tooltipPro == null) { _tooltipPro = Season2RuleSet.BuildRulesDescription(Season2RuleSet.GetProSeriesRulesSettings()) + "\nItems: Game defaults\nCourses: Manual selection\nHoles: 9"; } if (_tooltipCasual == null) { _tooltipCasual = "Applies Classic preset only.\nItems: Game defaults (all enabled)\nCourses: Manual selection\nNo MMR impact. Only casual matches played is tracked."; } GUIContent val = new GUIContent("RANKED"); GUIContent val2 = new GUIContent("PRO SERIES"); GUIContent val3 = new GUIContent("CASUAL"); GUIContent val4 = new GUIContent("NO RULESET"); Rect val5 = default(Rect); ((Rect)(ref val5))..ctor(num10, num8, num9, num2); Rect val6 = default(Rect); ((Rect)(ref val6))..ctor(num11, num8, num9, num2); Rect val7 = default(Rect); ((Rect)(ref val7))..ctor(num12, num8, num9, num2); Rect val8 = default(Rect); ((Rect)(ref val8))..ctor(num13, num8, num9, num2); GUI.backgroundColor = ((text == "ranked") ? Color.green : Color.grey); if (GUI.Button(val5, val)) { if (_applyRulesets != null) { _applyRulesets.Value = true; } ApplyRuleset("ranked"); } GUI.backgroundColor = ((text == "pro_series") ? Color.magenta : Color.grey); if (GUI.Button(val6, val2)) { if (_applyRulesets != null) { _applyRulesets.Value = true; } ApplyRuleset("pro_series"); } GUI.backgroundColor = (Color)((text == "casual") ? new Color(0.2f, 0.75f, 1f) : Color.grey); if (GUI.Button(val7, val3)) { if (_applyRulesets != null) { _applyRulesets.Value = true; } ApplyRuleset("casual"); } GUI.backgroundColor = ((text == "none") ? Color.yellow : Color.grey); if (GUI.Button(val8, val4, _smallBtnStyle)) { if (_applyRulesets != null) { _applyRulesets.Value = false; } ResetToClassicPreset(); Debug.Log((object)"[RuleSetDisplayManager] Ruleset enforcement disabled via No Ruleset button"); } GUI.backgroundColor = Color.white; if ((int)Event.current.type == 7) { Vector2 mousePosition = Event.current.mousePosition; string text2 = null; if (((Rect)(ref val5)).Contains(mousePosition)) { text2 = _tooltipRanked; } else if (((Rect)(ref val6)).Contains(mousePosition)) { text2 = _tooltipPro; } else if (((Rect)(ref val7)).Contains(mousePosition)) { text2 = _tooltipCasual; } else if (((Rect)(ref val8)).Contains(mousePosition)) { text2 = _noRulesetTooltip; } if (!string.IsNullOrEmpty(text2)) { float num14 = 280f; GUIContent val9 = new GUIContent(text2); float num15 = _tooltipStyle.CalcHeight(val9, num14); float num16 = mousePosition.x + 14f; float num17 = mousePosition.y + 14f; if (num16 + num14 > (float)Screen.width) { num16 = mousePosition.x - num14 - 6f; } if (num17 + num15 > (float)Screen.height) { num17 = mousePosition.y - num15 - 6f; } GUI.Box(new Rect(num16, num17, num14, num15), val9, _tooltipStyle); } } if (flag) { string text3 = PlayerPrefs.GetString("MatchType", "—"); string text4 = PlayerPrefs.GetString("SelectedCourse", "—"); int num18 = PlayerPrefs.GetInt("Season", 0); string text5 = PlayerPrefs.GetString("HostRuleset", "—"); float num19 = num6 + 10f; float num20 = num8 + num2 + 8f; float num21 = 20f; float num22 = 22f; GUI.Label(new Rect(num19, num20, num - 20f, num21), "Type: " + text3 + "", _labelStyle); GUI.Label(new Rect(num19, num20 + num22, num - 20f, num21), "Course: " + text4 + "", _labelStyle); GUI.Label(new Rect(num19, num20 + num22 * 2f, num - 20f, num21), $"Season: {num18}", _labelStyle); GUI.Label(new Rect(num19, num20 + num22 * 3f, num - 20f, num21), "Ruleset: " + text5 + "", _labelStyle); } } private void ApplyRuleset(string rulesetName) { Debug.Log((object)("[RuleSetDisplayManager] Applying ruleset: " + rulesetName)); PlayerPrefs.SetString("HostRuleset", rulesetName); int num = 2; switch (rulesetName) { case "ranked": PlayerPrefs.SetString("MatchType", "ranked_season_2"); break; case "pro_series": PlayerPrefs.SetString("MatchType", "pro_series_season_2"); break; case "casual": PlayerPrefs.SetString("MatchType", "casual"); num = 0; break; } PlayerPrefs.SetInt("Season", num); if (rulesetName == "ranked") { MapPoolConfig.Course randomApprovedCourse = MapPoolConfig.GetRandomApprovedCourse(); PlayerPrefs.SetString("SelectedCourse", randomApprovedCourse.Name); PlayerPrefs.Save(); Debug.Log((object)$"[RuleSetDisplayManager] ✓ Ruleset {rulesetName} applied. Season={num}, Course={randomApprovedCourse.Name}. Stored in PlayerPrefs"); } else { PlayerPrefs.Save(); Debug.Log((object)$"[RuleSetDisplayManager] ✓ Ruleset {rulesetName} applied. Season={num}. Course preserved (manual selection mode). Stored in PlayerPrefs"); } ApplyRulesToExistingMatchSetupRules(); } private void ApplyRulesToExistingMatchSetupRules() { try { MatchSetupMenu val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null || !((NetworkBehaviour)val).isServer) { Debug.Log((object)"[RuleSetDisplayManager] Match setup menu not open - rules will be applied automatically when it opens"); return; } MatchSetupRules rules = val.rules; if ((Object)(object)rules == (Object)null) { Debug.Log((object)"[RuleSetDisplayManager] MatchSetupMenu.rules is null"); return; } Debug.Log((object)"[RuleSetDisplayManager] ✓ Match setup menu is open - applying rules NOW"); RulePatches.ApplyRulesToMatchSetup(rules); RulePatches.ApplyCourseSelection(val); } catch (Exception ex) { Debug.LogError((object)("[RuleSetDisplayManager] Error applying rules: " + ex.Message + "\n" + ex.StackTrace)); } } private void ResetToClassicPreset() { try { MatchSetupMenu val = Object.FindAnyObjectByType(); if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).isServer) { MatchSetupRules rules = val.rules; if (!((Object)(object)rules == (Object)null)) { rules.SetPreset((Preset)0); Debug.Log((object)"[RuleSetDisplayManager] ✓ Reset to Classic preset (No Ruleset)"); } } } catch (Exception ex) { Debug.LogError((object)("[RuleSetDisplayManager] Error resetting to Classic: " + ex.Message)); } } } } namespace SBGL.UnifiedMod.Features.Poll { public class PollManager : MonoBehaviour { private string _activePollId; private string _activePollQuestion; private string[] _activePollOptions; private Dictionary _votes = new Dictionary(); private float _pollEndTime; private ulong _pollHostSteamId; private bool _hasVoted; private int[] _voteCounts = new int[0]; private Rect _pollWindowRect = new Rect(0f, 0f, 450f, 0f); private Rect _createWindowRect = new Rect(20f, 200f, 330f, 380f); private bool _pollWindowPositioned; private bool _showCreatePanel; private int _presetIndex = -1; private string _customQuestion = ""; private string[] _customOptions = new string[4] { "", "", "", "" }; private int _customOptionCount = 2; private const float POLL_DURATION = 30f; private const int POLL_WINDOW_ID = 1533476865; private const int CREATE_WINDOW_ID = 1533476866; private const string PREFIX_START = "SBGL_POLL_START:"; private const string PREFIX_VOTE = "SBGL_POLL_VOTE:"; private const string PREFIX_CANCEL = "SBGL_POLL_CANCEL:"; private static readonly Key[] VoteKeys; private static readonly (string Question, string[] Options)[] Presets; private Texture2D _texDark; private Texture2D _texBtn; private Texture2D _texBtnHover; private Texture2D _texVoted; private Texture2D _texVotedHover; private Texture2D _texCancel; private Texture2D _texCancelHover; private Texture2D _texAccent; private Texture2D _texAccentHover; private Texture2D _texField; private Texture2D _texDivider; private GUIStyle _windowStyle; private GUIStyle _questionStyle; private GUIStyle _optionStyle; private GUIStyle _votedStyle; private GUIStyle _hintStyle; private GUIStyle _cancelStyle; private GUIStyle _launchStyle; private GUIStyle _presetStyle; private GUIStyle _presetActiveStyle; private GUIStyle _labelStyle; private GUIStyle _fieldStyle; private GUIStyle _centeredLabel; private GUIStyle _voteButton; private GUIStyle _votedButton; private GUIStyle _hintLabel; public static PollManager Instance { get; private set; } private void Awake() { Instance = this; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { Keyboard current = Keyboard.current; if (current == null) { return; } if (((ButtonControl)current[(Key)100]).wasPressedThisFrame) { _showCreatePanel = !_showCreatePanel; } if (_activePollId != null && !_hasVoted) { for (int i = 0; i < VoteKeys.Length; i++) { int num = i; string[] activePollOptions = _activePollOptions; if (num < ((activePollOptions != null) ? activePollOptions.Length : 0) && ((ButtonControl)current[VoteKeys[i]]).wasPressedThisFrame) { CastVote(i); break; } } } if (_activePollId != null && Time.time > _pollEndTime + 20f) { ClearActivePoll(); } } public void HandlePacket(string msg, ulong senderId) { if (msg.StartsWith("SBGL_POLL_START:")) { HandleStart(msg.Substring("SBGL_POLL_START:".Length), senderId); } else if (msg.StartsWith("SBGL_POLL_VOTE:")) { HandleVote(msg.Substring("SBGL_POLL_VOTE:".Length)); } else if (msg.StartsWith("SBGL_POLL_CANCEL:")) { HandleCancel(msg.Substring("SBGL_POLL_CANCEL:".Length)); } } private void HandleStart(string payload, ulong hostId) { string[] array = payload.Split('|'); if (array.Length >= 3) { _activePollId = array[0]; _activePollQuestion = array[1]; _activePollOptions = (from s in array.Skip(2) where !string.IsNullOrEmpty(s) select s).ToArray(); _votes.Clear(); _voteCounts = new int[_activePollOptions.Length]; _pollEndTime = Time.time + 30f; _pollHostSteamId = hostId; _hasVoted = false; _pollWindowPositioned = false; Debug.Log((object)$"[SBGL-Poll] Received poll from {hostId}: {_activePollQuestion}"); } } private void HandleVote(string payload) { string[] array = payload.Split('|'); if (array.Length >= 3 && !(array[0] != _activePollId) && int.TryParse(array[1], out var result) && ulong.TryParse(array[2], out var result2) && result >= 0 && result < _voteCounts.Length) { if (_votes.TryGetValue(result2, out var value) && value >= 0 && value < _voteCounts.Length) { _voteCounts[value] = Mathf.Max(0, _voteCounts[value] - 1); } _votes[result2] = result; _voteCounts[result]++; } } private void HandleCancel(string pollId) { if (pollId == _activePollId) { ClearActivePoll(); } } private void ClearActivePoll() { _activePollId = null; _activePollQuestion = null; _activePollOptions = null; _votes.Clear(); _voteCounts = new int[0]; _hasVoted = false; } public void StartPoll(string question, string[] options) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (SteamClient.IsValid) { string text = (_activePollId = Guid.NewGuid().ToString("N").Substring(0, 8)); _activePollQuestion = question; _activePollOptions = options; _votes.Clear(); _voteCounts = new int[options.Length]; _pollEndTime = Time.time + 30f; _pollHostSteamId = SteamId.op_Implicit(SteamClient.SteamId); _hasVoted = false; _pollWindowPositioned = false; string text2 = string.Join("|", options); BroadcastToAll("SBGL_POLL_START:" + text + "|" + question + "|" + text2); Debug.Log((object)("[SBGL-Poll] Started poll: " + question)); } } public void CastVote(int optionIndex) { //IL_0049: 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_00da: Unknown result type (might be due to invalid IL or missing references) if (!_hasVoted && _activePollId != null && optionIndex >= 0 && optionIndex < _activePollOptions.Length) { _hasVoted = true; if (_votes.TryGetValue(SteamId.op_Implicit(SteamClient.SteamId), out var value) && value >= 0 && value < _voteCounts.Length) { _voteCounts[value] = Mathf.Max(0, _voteCounts[value] - 1); } _votes[SteamId.op_Implicit(SteamClient.SteamId)] = optionIndex; _voteCounts[optionIndex]++; BroadcastToAll(string.Format("{0}{1}|{2}|{3}", "SBGL_POLL_VOTE:", _activePollId, optionIndex, SteamClient.SteamId)); } } private void BroadcastToAll(string msg) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) SBGL.UnifiedMod.Features.CompetitivePluginCheck.CompetitivePluginCheck instance = SBGL.UnifiedMod.Features.CompetitivePluginCheck.CompetitivePluginCheck.Instance; if ((Object)(object)instance == (Object)null) { return; } byte[] bytes = Encoding.UTF8.GetBytes(msg); foreach (ulong knownPeerId in instance.GetKnownPeerIds()) { try { SteamNetworking.SendP2PPacket(SteamId.op_Implicit(knownPeerId), bytes, -1, SBGL.UnifiedMod.Features.CompetitivePluginCheck.CompetitivePluginCheck.NetChannel, (P2PSend)2); } catch (Exception ex) { Debug.LogWarning((object)$"[SBGL-Poll] Send failed to {knownPeerId}: {ex.Message}"); } } } private void OnGUI() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Expected O, but got Unknown //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) EnsureStyles(); if (_activePollId != null && (int)Event.current.type == 4 && (int)Event.current.keyCode == 27) { Event.current.Use(); } if (_activePollId != null) { string[] activePollOptions = _activePollOptions; int num = ((activePollOptions != null) ? activePollOptions.Length : 0); ((Rect)(ref _pollWindowRect)).height = 110f + (float)num * 46f; if (!_pollWindowPositioned) { ((Rect)(ref _pollWindowRect)).x = (float)Screen.width - ((Rect)(ref _pollWindowRect)).width - 20f; ((Rect)(ref _pollWindowRect)).y = ((float)Screen.height - ((Rect)(ref _pollWindowRect)).height) / 2f; _pollWindowPositioned = true; } bool flag = Time.time <= _pollEndTime; float num2 = Mathf.Max(0f, _pollEndTime - Time.time); string text = (flag ? $"VOTE — {num2:F0}s remaining" : "RESULTS"); _pollWindowRect = GUI.Window(1533476865, _pollWindowRect, new WindowFunction(DrawPollWindow), text, _windowStyle); } if (_showCreatePanel) { _createWindowRect = GUI.Window(1533476866, _createWindowRect, new WindowFunction(DrawCreateWindow), "Create Poll [F7]", _windowStyle); } } private static Texture2D MakeTex(Color col) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, col); val.Apply(); return val; } private void EnsureStyles() { //IL_0028: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0190: 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_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: 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_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Expected O, but got Unknown //IL_023d: 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_024c: Expected O, but got Unknown //IL_0252: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0273: 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_0284: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Expected O, but got Unknown //IL_02a4: 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_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Expected O, but got Unknown //IL_035b: Expected O, but got Unknown //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_039c: 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) //IL_03ad: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Expected O, but got Unknown //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_044b: Expected O, but got Unknown //IL_0452: Unknown result type (might be due to invalid IL or missing references) //IL_0457: Unknown result type (might be due to invalid IL or missing references) //IL_0469: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Unknown result type (might be due to invalid IL or missing references) //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_048c: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_049d: Unknown result type (might be due to invalid IL or missing references) //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04b5: Unknown result type (might be due to invalid IL or missing references) //IL_04c0: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_04d8: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Unknown result type (might be due to invalid IL or missing references) //IL_04f1: Expected O, but got Unknown //IL_04f8: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) //IL_050f: Unknown result type (might be due to invalid IL or missing references) //IL_0515: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_0532: Unknown result type (might be due to invalid IL or missing references) //IL_0538: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_0555: Unknown result type (might be due to invalid IL or missing references) //IL_055b: Unknown result type (might be due to invalid IL or missing references) //IL_0566: Unknown result type (might be due to invalid IL or missing references) //IL_0578: Unknown result type (might be due to invalid IL or missing references) //IL_057e: Unknown result type (might be due to invalid IL or missing references) //IL_0589: Unknown result type (might be due to invalid IL or missing references) //IL_0592: Unknown result type (might be due to invalid IL or missing references) //IL_059f: Expected O, but got Unknown //IL_05a6: Unknown result type (might be due to invalid IL or missing references) //IL_05ab: Unknown result type (might be due to invalid IL or missing references) //IL_05b9: Expected O, but got Unknown //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05c5: Unknown result type (might be due to invalid IL or missing references) //IL_05d3: Expected O, but got Unknown //IL_05de: Unknown result type (might be due to invalid IL or missing references) //IL_05e3: Unknown result type (might be due to invalid IL or missing references) //IL_05fd: Unknown result type (might be due to invalid IL or missing references) //IL_0608: Unknown result type (might be due to invalid IL or missing references) //IL_0616: Expected O, but got Unknown //IL_0621: Unknown result type (might be due to invalid IL or missing references) //IL_0626: Unknown result type (might be due to invalid IL or missing references) //IL_0638: Unknown result type (might be due to invalid IL or missing references) //IL_063e: Unknown result type (might be due to invalid IL or missing references) //IL_0649: Unknown result type (might be due to invalid IL or missing references) //IL_065b: Unknown result type (might be due to invalid IL or missing references) //IL_0661: Unknown result type (might be due to invalid IL or missing references) //IL_066c: Unknown result type (might be due to invalid IL or missing references) //IL_067a: Expected O, but got Unknown if (_windowStyle == null) { _texDark = MakeTex(new Color(0.07f, 0.08f, 0.1f, 0.94f)); _texBtn = MakeTex(new Color(0.17f, 0.19f, 0.23f, 1f)); _texBtnHover = MakeTex(new Color(0.25f, 0.28f, 0.34f, 1f)); _texVoted = MakeTex(new Color(0.08f, 0.44f, 0.13f, 1f)); _texVotedHover = MakeTex(new Color(0.1f, 0.54f, 0.16f, 1f)); _texCancel = MakeTex(new Color(0.5f, 0.09f, 0.09f, 1f)); _texCancelHover = MakeTex(new Color(0.63f, 0.11f, 0.11f, 1f)); _texAccent = MakeTex(new Color(0.1f, 0.36f, 0.58f, 1f)); _texAccentHover = MakeTex(new Color(0.14f, 0.46f, 0.7f, 1f)); _texField = MakeTex(new Color(0.12f, 0.14f, 0.17f, 1f)); _texDivider = MakeTex(new Color(0.28f, 0.3f, 0.36f, 0.9f)); GUIStyle val = new GUIStyle(GUI.skin.window); val.normal.background = _texDark; val.normal.textColor = new Color(1f, 0.83f, 0.2f); val.onNormal.background = _texDark; val.onNormal.textColor = new Color(1f, 0.83f, 0.2f); val.fontSize = 13; val.fontStyle = (FontStyle)1; val.alignment = (TextAnchor)1; val.padding = new RectOffset(0, 0, 26, 6); val.border = new RectOffset(2, 2, 2, 2); _windowStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label); val2.normal.textColor = Color.white; val2.fontSize = 15; val2.fontStyle = (FontStyle)1; val2.alignment = (TextAnchor)4; val2.wordWrap = true; _questionStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.button); val3.normal.background = _texBtn; val3.normal.textColor = Color.white; val3.hover.background = _texBtnHover; val3.hover.textColor = Color.white; val3.active.background = _texBtnHover; val3.active.textColor = Color.white; val3.focused.background = _texBtn; val3.focused.textColor = Color.white; val3.fontSize = 13; val3.alignment = (TextAnchor)4; val3.border = new RectOffset(2, 2, 2, 2); _optionStyle = val3; GUIStyle val4 = new GUIStyle(_optionStyle); val4.normal.background = _texVoted; val4.normal.textColor = Color.white; val4.hover.background = _texVotedHover; val4.hover.textColor = Color.white; val4.active.background = _texVotedHover; val4.active.textColor = Color.white; val4.focused.background = _texVoted; val4.focused.textColor = Color.white; val4.fontStyle = (FontStyle)1; _votedStyle = val4; GUIStyle val5 = new GUIStyle(GUI.skin.label); val5.normal.textColor = new Color(0.55f, 0.6f, 0.68f, 1f); val5.fontSize = 11; val5.alignment = (TextAnchor)5; _hintStyle = val5; GUIStyle val6 = new GUIStyle(_optionStyle); val6.normal.background = _texCancel; val6.normal.textColor = Color.white; val6.hover.background = _texCancelHover; val6.hover.textColor = Color.white; val6.active.background = _texCancelHover; val6.active.textColor = Color.white; val6.focused.background = _texCancel; val6.focused.textColor = Color.white; val6.fontSize = 11; _cancelStyle = val6; GUIStyle val7 = new GUIStyle(_optionStyle); val7.normal.background = _texAccent; val7.normal.textColor = Color.white; val7.hover.background = _texAccentHover; val7.hover.textColor = Color.white; val7.active.background = _texAccentHover; val7.active.textColor = Color.white; val7.focused.background = _texAccent; val7.focused.textColor = Color.white; val7.fontSize = 13; val7.fontStyle = (FontStyle)1; _launchStyle = val7; _presetStyle = new GUIStyle(_optionStyle) { fontSize = 12 }; _presetActiveStyle = new GUIStyle(_votedStyle) { fontSize = 12 }; GUIStyle val8 = new GUIStyle(GUI.skin.label); val8.normal.textColor = new Color(0.72f, 0.76f, 0.84f, 1f); val8.fontSize = 12; _labelStyle = val8; GUIStyle val9 = new GUIStyle(GUI.skin.textField); val9.normal.background = _texField; val9.normal.textColor = Color.white; val9.focused.background = _texBtnHover; val9.focused.textColor = Color.white; val9.fontSize = 12; _fieldStyle = val9; _centeredLabel = _questionStyle; _voteButton = _optionStyle; _votedButton = _votedStyle; _hintLabel = _hintStyle; } } private void DrawPollWindow(int windowId) { //IL_0046: 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_0215: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_0251: 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_0168: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) string[] activePollOptions = _activePollOptions; int num = ((activePollOptions != null) ? activePollOptions.Length : 0); float num2 = ((Rect)(ref _pollWindowRect)).width - 20f; bool flag = Time.time <= _pollEndTime; float num3 = 30f; GUI.Label(new Rect(10f, num3, num2, 44f), _activePollQuestion ?? "", _questionStyle); num3 += 48f; GUI.DrawTexture(new Rect(10f, num3, num2, 1f), (Texture)(object)_texDivider); num3 += 10f; for (int i = 0; i < num; i++) { int num4 = ((_voteCounts != null && i < _voteCounts.Length) ? _voteCounts[i] : 0); int count = _votes.Count; string arg = ((count > 0) ? $" {Mathf.RoundToInt(100f * (float)num4 / (float)count)}%" : ""); string text = $"{_activePollOptions[i]} ({num4}{arg})"; int value; bool flag2 = _votes.TryGetValue(SteamClient.IsValid ? SteamId.op_Implicit(SteamClient.SteamId) : 0, out value) && value == i; GUI.enabled = flag && !_hasVoted; if (GUI.Button(new Rect(10f, num3, num2, 38f), text, flag2 ? _votedStyle : _optionStyle)) { CastVote(i); } GUI.enabled = true; if (flag && !_hasVoted && i < VoteKeys.Length) { GUI.Label(new Rect(10f, num3, num2 - 6f, 38f), $"[F{i + 1}]", _hintStyle); } num3 += 46f; } if (SteamClient.IsValid && SteamId.op_Implicit(SteamClient.SteamId) == _pollHostSteamId && GUI.Button(new Rect(((Rect)(ref _pollWindowRect)).width - 86f, 4f, 76f, 22f), "✕ Cancel", _cancelStyle)) { BroadcastToAll("SBGL_POLL_CANCEL:" + _activePollId); ClearActivePoll(); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _pollWindowRect)).width, 28f)); } private void DrawCreateWindow(int windowId) { //IL_0027: 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_00ec: 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_0141: 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_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) float num = 10f; float num2 = 28f; float num3 = ((Rect)(ref _createWindowRect)).width - 20f; GUI.Label(new Rect(num, num2, num3, 18f), "Quick polls:", _labelStyle); num2 += 20f; for (int i = 0; i < Presets.Length; i++) { GUIStyle val = ((_presetIndex == i) ? _presetActiveStyle : _presetStyle); if (GUI.Button(new Rect(num, num2, num3, 28f), Presets[i].Question, val)) { _presetIndex = ((_presetIndex == i) ? (-1) : i); } num2 += 32f; } GUI.DrawTexture(new Rect(num, num2 + 4f, num3, 1f), (Texture)(object)_texDivider); num2 += 14f; GUI.Label(new Rect(num, num2, num3, 18f), "Custom poll:", _labelStyle); num2 += 20f; GUI.Label(new Rect(num, num2, 62f, 20f), "Question:", _labelStyle); _customQuestion = GUI.TextField(new Rect(num + 66f, num2, num3 - 66f, 22f), _customQuestion, 80, _fieldStyle); num2 += 28f; for (int j = 0; j < _customOptionCount; j++) { GUI.Label(new Rect(num, num2, 52f, 20f), $"Option {j + 1}:", _labelStyle); _customOptions[j] = GUI.TextField(new Rect(num + 56f, num2, num3 - 56f, 22f), _customOptions[j] ?? "", 40, _fieldStyle); num2 += 26f; } if (_customOptionCount < 4 && GUI.Button(new Rect(num, num2, 32f, 24f), "+", _presetStyle)) { _customOptionCount++; } if (_customOptionCount > 2 && GUI.Button(new Rect(num + 38f, num2, 32f, 24f), "−", _presetStyle)) { _customOptions[_customOptionCount - 1] = ""; _customOptionCount--; } num2 += 32f; if (GUI.Button(new Rect(num, num2, num3, 30f), "Launch Poll", _launchStyle)) { TryLaunchPoll(); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _createWindowRect)).width, 28f)); } private void TryLaunchPoll() { string question; string[] options; if (_presetIndex >= 0 && _presetIndex < Presets.Length) { question = Presets[_presetIndex].Question; options = Presets[_presetIndex].Options; } else { if (string.IsNullOrWhiteSpace(_customQuestion)) { return; } string[] array = (from o in _customOptions.Take(_customOptionCount) where !string.IsNullOrWhiteSpace(o) select o).ToArray(); if (array.Length < 2) { return; } question = _customQuestion.Trim(); options = array.Select((string o) => o.Trim()).ToArray(); } StartPoll(question, options); _showCreatePanel = false; _presetIndex = -1; _customQuestion = ""; for (int num = 0; num < _customOptions.Length; num++) { _customOptions[num] = ""; } } static PollManager() { Key[] array = new Key[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); VoteKeys = (Key[])(object)array; Presets = new(string, string[])[3] { ("Ready to start?", new string[2] { "Ready!", "Not yet" }), ("How many holes?", new string[3] { "9", "15", "18" }), ("Skip this hole?", new string[2] { "Yes", "No" }) }; } } } namespace SBGL.UnifiedMod.Features.HitTracker { public class HitTrackerPlugin : MonoBehaviour { private readonly Dictionary>> _hitsPerHole = new Dictionary>>(); private readonly Dictionary> _hitsCurrentHole = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _hitsTotal = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly HashSet _allPlayers = new HashSet(StringComparer.OrdinalIgnoreCase); private int _lastHoleIndex = -1; private bool _showWindow = false; private Rect _windowRect = new Rect(10f, 10f, 520f, 380f); private const int WindowId = 9876543; private Vector2 _scrollMain = Vector2.zero; private int _selectedHole = -2; private GUIStyle _headerStyle; private GUIStyle _cellStyle; private GUIStyle _attackerStyle; private GUIStyle _hitStyle; private GUIStyle _windowStyle; private Texture2D _bgTexture; private bool _stylesInit = false; public static HitTrackerPlugin Instance { get; private set; } private Key ToggleKey => (Key)99; private void Awake() { Instance = this; } private void Update() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (Keyboard.current != null && ((ButtonControl)Keyboard.current[ToggleKey]).wasPressedThisFrame && string.Equals(SteamClient.Name, "KingCox22", StringComparison.OrdinalIgnoreCase)) { _showWindow = !_showWindow; } try { int currentHoleGlobalIndex = CourseManager.CurrentHoleGlobalIndex; if (_lastHoleIndex >= 0 && currentHoleGlobalIndex != _lastHoleIndex) { ArchiveCurrentHole(_lastHoleIndex); } _lastHoleIndex = currentHoleGlobalIndex; } catch { } } public void RecordHit(string attackerName, string victimName) { if (!string.IsNullOrWhiteSpace(attackerName) && !string.IsNullOrWhiteSpace(victimName)) { _allPlayers.Add(attackerName); _allPlayers.Add(victimName); Increment(_hitsCurrentHole, attackerName, victimName); Increment(_hitsTotal, attackerName, victimName); } } public void ResetAll() { _hitsPerHole.Clear(); _hitsCurrentHole.Clear(); _hitsTotal.Clear(); _allPlayers.Clear(); _lastHoleIndex = -1; _selectedHole = -1; } private void ArchiveCurrentHole(int holeIndex) { if (_hitsCurrentHole.Count > 0) { _hitsPerHole[holeIndex] = DeepCopy(_hitsCurrentHole); } _hitsCurrentHole.Clear(); } private static void Increment(Dictionary> table, string attacker, string victim) { if (!table.TryGetValue(attacker, out var value)) { value = (table[attacker] = new Dictionary(StringComparer.OrdinalIgnoreCase)); } value.TryGetValue(victim, out var value2); value[victim] = value2 + 1; } private static Dictionary> DeepCopy(Dictionary> src) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> item in src) { dictionary[item.Key] = new Dictionary(item.Value, StringComparer.OrdinalIgnoreCase); } return dictionary; } private void OnGUI() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00c6: 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) if (!_showWindow) { return; } if (!_stylesInit) { InitStyles(); } if (_allPlayers.Count > 0) { float num = 90f + 38f * (float)_allPlayers.Count + 50f + 28f; if (((Rect)(ref _windowRect)).width < num) { ((Rect)(ref _windowRect)).width = num; } } _windowRect = GUILayout.Window(9876543, _windowRect, new WindowFunction(DrawWindow), $"Hit Tracker · H{_lastHoleIndex + 1} · {ToggleKey}", _windowStyle, Array.Empty()); } private void InitStyles() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) //IL_0021: 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_0032: 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_0048: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_0070: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_009a: 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) //IL_00a7: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00d5: 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) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Expected O, but got Unknown //IL_01b3: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, alignment = (TextAnchor)4, wordWrap = false, fontSize = 11 }; val.normal.textColor = Color.white; _headerStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, alignment = (TextAnchor)3, wordWrap = false, fontSize = 11 }; val2.normal.textColor = Color.white; _attackerStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, wordWrap = false, fontSize = 11 }; val3.normal.textColor = Color.white; _cellStyle = val3; GUIStyle val4 = new GUIStyle(_cellStyle); val4.normal.textColor = new Color(1f, 0.45f, 0.45f); val4.fontStyle = (FontStyle)1; _hitStyle = val4; _bgTexture = new Texture2D(1, 1); _bgTexture.SetPixel(0, 0, new Color(0.08f, 0.08f, 0.08f, 0.92f)); _bgTexture.Apply(); GUIStyle val5 = new GUIStyle(GUI.skin.window); val5.normal.background = _bgTexture; val5.normal.textColor = Color.white; val5.onNormal.background = _bgTexture; val5.onNormal.textColor = Color.white; val5.padding = new RectOffset(6, 6, 22, 6); _windowStyle = val5; _stylesInit = true; } private void DrawWindow(int id) { //IL_004d: 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_0091: 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) List list = _allPlayers.OrderBy((string p) => p).ToList(); GUILayout.BeginHorizontal(Array.Empty()); GUI.color = ((_selectedHole == -2) ? Color.yellow : Color.white); if (GUILayout.Button("TOTALS", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _selectedHole = -2; } GUI.color = ((_selectedHole == -1) ? Color.yellow : Color.white); if (GUILayout.Button($"H{_lastHoleIndex + 1}▶", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(46f) })) { _selectedHole = -1; } GUI.color = Color.white; foreach (int item in _hitsPerHole.Keys.OrderBy((int x) => x)) { GUI.color = ((_selectedHole == item) ? Color.yellow : Color.white); if (GUILayout.Button($"H{item + 1}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { _selectedHole = item; } GUI.color = Color.white; } GUILayout.FlexibleSpace(); GUI.color = new Color(1f, 0.6f, 0.6f); if (GUILayout.Button("Reset", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) })) { ResetAll(); } GUI.color = Color.white; GUILayout.EndHorizontal(); Dictionary> data; string text; if (_selectedHole == -2) { data = _hitsTotal; text = "MATCH TOTALS"; } else if (_selectedHole == -1) { data = _hitsCurrentHole; text = $"HOLE {_lastHoleIndex + 1} (LIVE)"; } else { _hitsPerHole.TryGetValue(_selectedHole, out var value); data = value ?? new Dictionary>(); text = $"HOLE {_selectedHole + 1}"; } GUILayout.Label(text, _headerStyle, Array.Empty()); if (list.Count == 0) { GUILayout.Label("No hits recorded yet.", _headerStyle, Array.Empty()); } else { DrawMatrix(data, list, ref _scrollMain, ((Rect)(ref _windowRect)).width - 22f); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 20f)); } private void DrawMatrix(Dictionary> data, List players, ref Vector2 scroll, float panelWidth) { //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) int count = players.Count; float num = 90f; float num2 = Mathf.Max(38f, (panelWidth - num - 70f) / (float)count); float num3 = 50f; Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); int num4 = 0; foreach (string player in players) { dictionary[player] = 0; dictionary2[player] = 0; } foreach (KeyValuePair> datum in data) { foreach (KeyValuePair item in datum.Value) { int value = item.Value; if (dictionary.ContainsKey(datum.Key)) { dictionary[datum.Key] += value; } if (dictionary2.ContainsKey(item.Key)) { dictionary2[item.Key] += value; } num4 += value; } } float num5 = 20f; float num6 = num5 * (float)(count + 3) + 20f; scroll = GUILayout.BeginScrollView(scroll, false, false, GUIStyle.none, GUIStyle.none, GUIStyle.none, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Min(num6, 300f)) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("↓Atk / Vic→", _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); foreach (string player2 in players) { GUILayout.Label(Shorten(player2, 5), _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); } GUILayout.Label("SENT", _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num3) }); GUILayout.EndHorizontal(); foreach (string player3 in players) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(Shorten(player3, 10), _attackerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); foreach (string player4 in players) { if (string.Equals(player3, player4, StringComparison.OrdinalIgnoreCase)) { GUILayout.Label("—", _cellStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); continue; } int value2 = 0; if (data.TryGetValue(player3, out var value3)) { value3.TryGetValue(player4, out value2); } GUILayout.Label(value2.ToString(), (value2 > 0) ? _hitStyle : _cellStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); } GUILayout.Label(dictionary[player3].ToString(), _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num3) }); GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("RECV", _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); foreach (string player5 in players) { GUILayout.Label(dictionary2[player5].ToString(), _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); } GUILayout.Label(num4.ToString(), _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num3) }); GUILayout.EndHorizontal(); GUILayout.EndScrollView(); } private static string Shorten(string name, int maxLen) { return (name.Length > maxLen) ? name.Substring(0, maxLen) : name; } public string GetMatchTotalsJson() { if (_hitsTotal.Count == 0) { return null; } StringBuilder stringBuilder = new StringBuilder("{"); bool flag = true; foreach (KeyValuePair> item in _hitsTotal.OrderBy((KeyValuePair> k) => k.Key)) { List> list = (from k in item.Value where k.Value > 0 orderby k.Key select k).ToList(); if (list.Count == 0) { continue; } if (!flag) { stringBuilder.Append(','); } flag = false; string value = item.Key.Replace("\\", "\\\\").Replace("\"", "\\\""); stringBuilder.Append('"').Append(value).Append("\":"); stringBuilder.Append('{'); bool flag2 = true; foreach (KeyValuePair item2 in list) { if (!flag2) { stringBuilder.Append(','); } flag2 = false; string value2 = item2.Key.Replace("\\", "\\\\").Replace("\"", "\\\""); stringBuilder.Append('"').Append(value2).Append('"') .Append(':') .Append(item2.Value); } stringBuilder.Append('}'); } stringBuilder.Append('}'); return flag ? null : stringBuilder.ToString(); } } [HarmonyPatch] public static class HitWithGolfSwingPatch { [HarmonyTargetMethod] public static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("Hittable"); return AccessTools.Method(type, "HitWithGolfSwingInternal", new Type[9] { typeof(Vector3), typeof(Vector3), typeof(Vector3), typeof(bool), typeof(float), typeof(float), typeof(bool), AccessTools.TypeByName("PlayerGolfer"), AccessTools.TypeByName("Hittable") }, (Type[])null); } [HarmonyPostfix] public static void Postfix(object __instance, object hitter) { try { if (__instance == null || hitter == null) { return; } Component val = (Component)((__instance is Component) ? __instance : null); if ((Object)(object)val == (Object)null) { return; } Component component = val.GetComponent("Entity"); if ((Object)(object)component == (Object)null) { return; } PropertyInfo property = ((object)component).GetType().GetProperty("IsPlayer"); if (!(property == null) && (bool)property.GetValue(component)) { string playerName = GetPlayerName(component, "PlayerInfo"); string playerNameFromGolfer = GetPlayerNameFromGolfer(hitter); if (!string.IsNullOrWhiteSpace(playerName) && !string.IsNullOrWhiteSpace(playerNameFromGolfer) && !string.Equals(playerName, playerNameFromGolfer, StringComparison.OrdinalIgnoreCase)) { HitTrackerPlugin.Instance?.RecordHit(playerNameFromGolfer, playerName); } } } catch { } } private static string GetPlayerName(Component entity, string playerInfoPropName) { PropertyInfo property = ((object)entity).GetType().GetProperty(playerInfoPropName); if (property == null) { return null; } object? value = property.GetValue(entity); Component val = (Component)((value is Component) ? value : null); if ((Object)(object)val == (Object)null) { return null; } PropertyInfo property2 = ((object)val).GetType().GetProperty("PlayerId"); if (property2 == null) { return null; } object value2 = property2.GetValue(val); if (value2 == null) { return null; } return value2.GetType().GetProperty("PlayerName")?.GetValue(value2) as string; } private static string GetPlayerNameFromGolfer(object golfer) { Component val = (Component)((golfer is Component) ? golfer : null); if ((Object)(object)val == (Object)null) { return null; } PropertyInfo property = ((object)val).GetType().GetProperty("PlayerInfo"); if (property == null) { return null; } object? value = property.GetValue(val); Component val2 = (Component)((value is Component) ? value : null); if ((Object)(object)val2 == (Object)null) { return null; } PropertyInfo property2 = ((object)val2).GetType().GetProperty("PlayerId"); if (property2 == null) { return null; } object value2 = property2.GetValue(val2); if (value2 == null) { return null; } return value2.GetType().GetProperty("PlayerName")?.GetValue(value2) as string; } } } namespace SBGL.UnifiedMod.Features.CompetitivePluginCheck { public class ProSeriesEvent { public string name; public string event_date; } public class MatchEntry { public string match_summary; public string match_date; } public class CompetitivePluginCheck : MonoBehaviour { private class PlayerComplianceStatus { public ulong SteamId { get; set; } public bool HasReportedMods { get; set; } public bool IsCompliant { get; set; } public bool HasMelonLoader { get; set; } public string ModList { get; set; } public float FirstSeenTime { get; set; } } private class LocalPluginScanResult { public List MissingModNames { get; } = new List(); public List TamperedModNames { get; } = new List(); public List SuspiciousRuntimeAssemblies { get; } = new List(); public bool HasIllegalMods { get; set; } } private sealed class AllowedModsSnapshot { public static readonly AllowedModsSnapshot Empty = new AllowedModsSnapshot(new HashSet(StringComparer.OrdinalIgnoreCase), new Dictionary(StringComparer.OrdinalIgnoreCase), null); private readonly HashSet _allowedGuids; private readonly Dictionary _displayNamesByGuid; private readonly Dictionary> _allowedHashesByGuid; public int Count => _displayNamesByGuid.Count; public bool HasHashConstraints => _allowedHashesByGuid != null; public IEnumerable DisplayNames => _displayNamesByGuid.Values; private AllowedModsSnapshot(HashSet allowedGuids, Dictionary displayNamesByGuid, Dictionary> allowedHashesByGuid) { _allowedGuids = allowedGuids; _displayNamesByGuid = displayNamesByGuid; _allowedHashesByGuid = allowedHashesByGuid; } public bool ContainsGuid(string guid) { return !string.IsNullOrWhiteSpace(guid) && _allowedGuids.Contains(guid); } public HashSet GetAllowedHashes(string guid) { if (_allowedHashesByGuid == null || string.IsNullOrWhiteSpace(guid)) { return null; } _allowedHashesByGuid.TryGetValue(guid, out var value); return value; } public IEnumerable GetMissingModNames(HashSet installedGuids) { foreach (KeyValuePair entry in _displayNamesByGuid) { if (!ShouldIgnorePluginGuid(entry.Key) && !installedGuids.Contains(entry.Key)) { yield return entry.Value; } } } public static AllowedModsSnapshot FromJson(string json) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary> dictionary2 = new Dictionary>(StringComparer.OrdinalIgnoreCase); try { JObject val = JObject.Parse(json); JToken obj = val["mods"]; JArray val2 = (JArray)(object)((obj is JArray) ? obj : null); if (val2 == null) { return Empty; } foreach (JToken item in val2) { JToken obj2 = item[(object)"guid"]; string text = ((obj2 != null) ? Extensions.Value((IEnumerable)obj2) : null); JToken obj3 = item[(object)"name"]; string text2 = ((obj3 != null) ? Extensions.Value((IEnumerable)obj3) : null); if (string.IsNullOrWhiteSpace(text)) { continue; } hashSet.Add(text); dictionary[text] = (string.IsNullOrWhiteSpace(text2) ? text : text2); JToken obj4 = item[(object)"assemblies"]; JArray val3 = (JArray)(object)((obj4 is JArray) ? obj4 : null); if (val3 == null || ((JContainer)val3).Count <= 0) { continue; } HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (JToken item2 in val3) { JToken obj5 = item2[(object)"sha256"]; string text3 = ((obj5 != null) ? Extensions.Value((IEnumerable)obj5) : null); if (!string.IsNullOrWhiteSpace(text3)) { hashSet2.Add(text3); } } if (hashSet2.Count > 0) { dictionary2[text] = hashSet2; } } } catch (Exception ex) { Debug.LogWarning((object)("[CompCheck] Failed to parse approved mods JSON manifest: " + ex.Message)); return Empty; } return new AllowedModsSnapshot(hashSet, dictionary, dictionary2); } public static AllowedModsSnapshot FromText(string rawText) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] array = rawText.Split('\n'); foreach (string text in array) { if (string.IsNullOrWhiteSpace(text)) { continue; } string text2; string text3; if (text.Contains("|")) { string[] array2 = text.Split('|'); if (array2.Length < 2) { continue; } text2 = array2[0].Trim(); text3 = array2[1].Trim(); } else { text3 = text.Trim(); text2 = text3; } if (!string.IsNullOrWhiteSpace(text3)) { hashSet.Add(text3); dictionary[text3] = (string.IsNullOrWhiteSpace(text2) ? text3 : text2); } } return new AllowedModsSnapshot(hashSet, dictionary, null); } public static AllowedModsSnapshot Parse(string rawText) { if (string.IsNullOrWhiteSpace(rawText)) { return Empty; } string text = rawText.TrimStart(); return text.StartsWith("{") ? FromJson(text) : FromText(rawText); } } private sealed class KnownVisibleRuntimeAssemblies { public HashSet ExactAssemblyPaths { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet AllowedCompanionAssemblyNames { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); } private ConfigEntry _configX; private ConfigEntry _configY; private ConfigEntry _configWidth; private ConfigEntry _configAlpha; private ConfigEntry _configUpdateInterval; private ConfigEntry _configCompliancePanelX; private ConfigEntry _configCompliancePanelY; private ConfigEntry _configHideUIWindow; private ConfigEntry _configShowModList; private ConfigEntry _configShowDebugWindow; private ConfigEntry _configMelonLoaderChatEnabled; private ConfigEntry _configPlayerId; private const string ALLOWED_MODS_URL = "https://gist.githubusercontent.com/Kingcox22/59765f02af8dd87179ca920409ff3b27/raw/Approved_Mods.json"; internal const int SBGL_NET_CHANNEL = 2622; private Dictionary _remotePlayerMods = new Dictionary(); private Dictionary _playerDisplayNames = new Dictionary(); private Dictionary _playerComplianceStatus = new Dictionary(); private HashSet _playersNotifiedAbout = new HashSet(); private HashSet _knownPeers = new HashSet(); private Dictionary _playerFirstSeenTime = new Dictionary(); private const float COMPLIANCE_TIMEOUT = 15f; private bool _hasLoggedDiscoveryInfo = false; private string _lastDiscoveryScene = ""; private float _lastMelonLoaderAnnouncementTime = -100f; private float _lastLobbyNameCheck = -100f; internal static string _currentLobbyName = ""; private int _lastPlayerCosmeticsCount = 0; private Rect _compliancePanelRect; private bool _compliancePanelRectInit = false; private GUIStyle _compWindowStyle; private GameObject _canvasObj; private GameObject _profilePicContainer; private GameObject _bgObj; private GameObject _warnContainer; private GameObject _debugWindowObj; private GameObject _flagContainer; private TextMeshProUGUI _statsText; private TextMeshProUGUI _illegalWarningText; private TextMeshProUGUI _missingWarningText; private TextMeshProUGUI _debugWindowText; private TextMeshProUGUI _cardNameText; private TextMeshProUGUI _cardMMRText; private TextMeshProUGUI _cardRankText; private TextMeshProUGUI _cardSecondaryText; private TextMeshProUGUI _cardSyncText; private Image _bgImage; private Image _debugWindowBg; private Image _rankColorStripImage; private RawImage _profileIcon; private RawImage _flagIcon; private RectTransform _bgRect; private RectTransform _debugWindowRect; private static readonly HashSet IgnoredPluginGuids = new HashSet(StringComparer.OrdinalIgnoreCase) { "BepInEx", "com.sbgl.unified" }; private AllowedModsSnapshot _allowedModsSnapshot = AllowedModsSnapshot.Empty; private readonly HashSet _observedRuntimeAssemblyPaths = new HashSet(StringComparer.OrdinalIgnoreCase); private Lobby _currentLobby; private bool _inLobby = false; public List _upcomingEvents = new List(); public List _recentMatches = new List(); private bool _isSyncing = false; private float _timeUntilNextUpdate = 0f; private string _lastSyncTime = "Never"; public string _activeUsername = "Searching..."; public string _playerRank = "N/A"; public string _totalPlayers = "0"; public string _playerMMR = "0"; public string _matches = "0"; public string _playerPeak = "0"; public string _winRate = "0%"; public string _lastChange = "0"; public string _top3s = "0"; public string _avgScore = "0.0"; public string _syncStatus = "Idle"; private string _playerRegion = ""; private string _playerState = ""; private string _spectatedPlayerName = ""; public string ResolvedName = "Not Found"; public string ResolvedID = "None"; private static Texture2D _sbglLogoCache; private const string SBGL_LOGO_URL = "https://liquipedia.net/commons/images/1/19/Super_Battle_Golf_League_allmode.png"; public static CompetitivePluginCheck Instance { get; private set; } public static int NetChannel => 2622; private float ConfigX => _configX?.Value ?? PlayerPrefs.GetFloat("CompCheck_X", 0f); private float ConfigY => _configY?.Value ?? PlayerPrefs.GetFloat("CompCheck_Y", 12f); private float ConfigWidth => _configWidth?.Value ?? PlayerPrefs.GetFloat("CompCheck_Width", 200f); private float ConfigAlpha => _configAlpha?.Value ?? PlayerPrefs.GetFloat("CompCheck_Alpha", 0.85f); private float ConfigCompliancePanelX { get { return _configCompliancePanelX?.Value ?? PlayerPrefs.GetFloat("CompCheck_ComplianceX", (float)Screen.width - 420f); } set { if (_configCompliancePanelX != null) { _configCompliancePanelX.Value = value; } } } private float ConfigCompliancePanelY { get { return _configCompliancePanelY?.Value ?? PlayerPrefs.GetFloat("CompCheck_ComplianceY", (float)Screen.height - 350f); } set { if (_configCompliancePanelY != null) { _configCompliancePanelY.Value = value; } } } private bool ConfigHideUIWindow { get { return _configHideUIWindow?.Value ?? (PlayerPrefs.GetInt("CompCheck_HideUIWindow", 0) == 1); } set { if (_configHideUIWindow != null) { _configHideUIWindow.Value = value; } } } private bool ConfigShowModList { get { return _configShowModList?.Value ?? (PlayerPrefs.GetInt("CompCheck_ShowModList", 0) == 1); } set { if (_configShowModList != null) { _configShowModList.Value = value; } } } private bool ConfigShowDebugWindow { get { return _configShowDebugWindow?.Value ?? (PlayerPrefs.GetInt("CompCheck_ShowDebugWindow", 0) == 1); } set { if (_configShowDebugWindow != null) { _configShowDebugWindow.Value = value; } } } internal string ConfigPlayerId { get { return _configPlayerId?.Value ?? PlayerPrefs.GetString("CompCheck_PlayerId", "PASTE_ID_HERE"); } set { if (_configPlayerId != null) { _configPlayerId.Value = value; } } } private bool ConfigMelonLoaderChatEnabled => _configMelonLoaderChatEnabled?.Value ?? false; private static string NormalizePath(string path) { if (string.IsNullOrWhiteSpace(path)) { return string.Empty; } try { return Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); } catch { return path; } } private static string TryGetAssemblyLocation(Assembly assembly) { if (assembly == null) { return string.Empty; } try { return NormalizePath(assembly.Location); } catch { return string.Empty; } } private bool IsTrackedRuntimeAssemblyPath(string assemblyPath) { if (string.IsNullOrWhiteSpace(assemblyPath)) { return false; } string text = NormalizePath(assemblyPath); string text2 = NormalizePath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "BepInEx", "plugins")); if (text.StartsWith(text2 + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) { return true; } return text.IndexOf(Path.DirectorySeparatorChar + "Cheats" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) >= 0; } private void TrackRuntimeAssembly(Assembly assembly) { string text = TryGetAssemblyLocation(assembly); if (IsTrackedRuntimeAssemblyPath(text)) { _observedRuntimeAssemblyPaths.Add(text); } } private void CaptureLoadedRuntimeAssemblies() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { TrackRuntimeAssembly(assembly); } } private void OnRuntimeAssemblyLoaded(object sender, AssemblyLoadEventArgs args) { TrackRuntimeAssembly(args.LoadedAssembly); } private static string TryGetAssemblySimpleName(string assemblyPath) { if (string.IsNullOrWhiteSpace(assemblyPath) || !File.Exists(assemblyPath)) { return string.Empty; } try { return AssemblyName.GetAssemblyName(assemblyPath).Name ?? string.Empty; } catch { return string.Empty; } } private static string ComputeSha256Hex(string filePath) { try { using SHA256 sHA = SHA256.Create(); using FileStream inputStream = File.OpenRead(filePath); byte[] array = sHA.ComputeHash(inputStream); return BitConverter.ToString(array).Replace("-", "").ToLowerInvariant(); } catch { return string.Empty; } } private KnownVisibleRuntimeAssemblies BuildKnownVisibleRuntimeAssemblies() { KnownVisibleRuntimeAssemblies knownVisibleRuntimeAssemblies = new KnownVisibleRuntimeAssemblies(); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { try { Assembly assembly = (((object)value).GetType().GetProperty("Instance", BindingFlags.Instance | BindingFlags.Public)?.GetValue(value))?.GetType().Assembly; string text = TryGetAssemblyLocation(assembly); if (string.IsNullOrWhiteSpace(text)) { continue; } knownVisibleRuntimeAssemblies.ExactAssemblyPaths.Add(text); AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies(); foreach (AssemblyName assemblyName in referencedAssemblies) { if (!string.IsNullOrWhiteSpace(assemblyName.Name)) { knownVisibleRuntimeAssemblies.AllowedCompanionAssemblyNames.Add(assemblyName.Name); } } } catch { } } return knownVisibleRuntimeAssemblies; } private static bool ShouldIgnorePluginGuid(string guid) { return !string.IsNullOrWhiteSpace(guid) && IgnoredPluginGuids.Contains(guid); } private bool IsAllowedPluginGuid(string guid) { return _allowedModsSnapshot.ContainsGuid(guid); } private LocalPluginScanResult BuildLocalPluginScanResult() { LocalPluginScanResult localPluginScanResult = new LocalPluginScanResult(); HashSet installedGuids = new HashSet(from plugin in Chainloader.PluginInfos.Values select plugin.Metadata.GUID into guid where !string.IsNullOrWhiteSpace(guid) select guid, StringComparer.OrdinalIgnoreCase); KnownVisibleRuntimeAssemblies knownVisibleRuntimeAssemblies = BuildKnownVisibleRuntimeAssemblies(); localPluginScanResult.MissingModNames.AddRange(_allowedModsSnapshot.GetMissingModNames(installedGuids)); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (ShouldIgnorePluginGuid(value.Metadata.GUID)) { continue; } if (!IsAllowedPluginGuid(value.Metadata.GUID)) { localPluginScanResult.HasIllegalMods = true; break; } if (!_allowedModsSnapshot.HasHashConstraints) { continue; } HashSet allowedHashes = _allowedModsSnapshot.GetAllowedHashes(value.Metadata.GUID); if (allowedHashes == null || allowedHashes.Count <= 0) { continue; } try { string text = TryGetAssemblyLocation((((object)value).GetType().GetProperty("Instance", BindingFlags.Instance | BindingFlags.Public)?.GetValue(value))?.GetType().Assembly); if (!string.IsNullOrWhiteSpace(text) && File.Exists(text)) { string text2 = ComputeSha256Hex(text); if (!string.IsNullOrWhiteSpace(text2) && !allowedHashes.Contains(text2)) { localPluginScanResult.TamperedModNames.Add(value.Metadata.Name); localPluginScanResult.HasIllegalMods = true; } } } catch { } } foreach (string item in _observedRuntimeAssemblyPaths.OrderBy((string path) => path, StringComparer.OrdinalIgnoreCase)) { if (!knownVisibleRuntimeAssemblies.ExactAssemblyPaths.Contains(item)) { string text3 = TryGetAssemblySimpleName(item); if (item.Contains(Path.DirectorySeparatorChar + "Cheats" + Path.DirectorySeparatorChar) || string.IsNullOrWhiteSpace(text3) || !knownVisibleRuntimeAssemblies.AllowedCompanionAssemblyNames.Contains(text3)) { localPluginScanResult.SuspiciousRuntimeAssemblies.Add(item.Replace(NormalizePath(AppDomain.CurrentDomain.BaseDirectory) + Path.DirectorySeparatorChar, string.Empty)); localPluginScanResult.HasIllegalMods = true; } } } return localPluginScanResult; } public void SetConfig(ConfigEntry x, ConfigEntry y, ConfigEntry width, ConfigEntry alpha, ConfigEntry hideUIWindow, ConfigEntry showModList, ConfigEntry showDebugWindow, ConfigEntry updateInterval, ConfigEntry playerId, ConfigEntry compliancePanelX = null, ConfigEntry compliancePanelY = null, ConfigEntry melonLoaderChatEnabled = null) { _configX = x; _configY = y; _configWidth = width; _configAlpha = alpha; _configHideUIWindow = hideUIWindow; _configShowModList = showModList; _configShowDebugWindow = showDebugWindow; _configUpdateInterval = updateInterval; _configPlayerId = playerId; _configCompliancePanelX = compliancePanelX; _configCompliancePanelY = compliancePanelY; _configMelonLoaderChatEnabled = melonLoaderChatEnabled; } private void OnEnable() { try { SteamMatchmaking.OnLobbyEntered += OnLobbyEntered; } catch { } try { SteamMatchmaking.OnLobbyMemberJoined += OnLobbyMemberJoined; } catch { } try { SteamMatchmaking.OnLobbyMemberLeave += OnLobbyMemberLeave; } catch { } try { SteamNetworking.OnP2PSessionRequest = (Action)Delegate.Combine(SteamNetworking.OnP2PSessionRequest, new Action(OnP2PSessionRequest)); } catch { } } private void OnDisable() { try { SteamMatchmaking.OnLobbyEntered -= OnLobbyEntered; } catch { } try { SteamMatchmaking.OnLobbyMemberJoined -= OnLobbyMemberJoined; } catch { } try { SteamMatchmaking.OnLobbyMemberLeave -= OnLobbyMemberLeave; } catch { } try { SteamNetworking.OnP2PSessionRequest = (Action)Delegate.Remove(SteamNetworking.OnP2PSessionRequest, new Action(OnP2PSessionRequest)); } catch { } } private void OnP2PSessionRequest(SteamId remoteId) { //IL_0007: 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_003e: 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_0081: 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_0091: Unknown result type (might be due to invalid IL or missing references) if (!_inLobby || !((Lobby)(ref _currentLobby)).Members.Any((Friend m) => SteamId.op_Implicit(m.Id) == SteamId.op_Implicit(remoteId))) { return; } SteamNetworking.AcceptP2PSessionWithUser(remoteId); if (!_playerDisplayNames.ContainsKey(remoteId.Value)) { foreach (Friend member in ((Lobby)(ref _currentLobby)).Members) { Friend current = member; if (SteamId.op_Implicit(current.Id) == SteamId.op_Implicit(remoteId)) { _playerDisplayNames[remoteId.Value] = ((Friend)(ref current)).Name; break; } } } Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Accepted P2P session request from {remoteId.Value}"); } private void OnLobbyEntered(Lobby lobby) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0071: 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) //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_0085: 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) //IL_00a7: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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_011b: Unknown result type (might be due to invalid IL or missing references) _currentLobby = lobby; _inLobby = true; float time = Time.time; string text = TryReadLobbyName(lobby); if (!string.IsNullOrWhiteSpace(text)) { _currentLobbyName = text; } Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ OnLobbyEntered: {((Lobby)(ref lobby)).Id.Value}, member count: {((Lobby)(ref lobby)).MemberCount}, lobby name: '{text}'"); foreach (Friend member in ((Lobby)(ref lobby)).Members) { Friend current = member; if (current.Id.Value != SteamId.op_Implicit(SteamClient.SteamId)) { Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Lobby member: {((Friend)(ref current)).Name} ({current.Id.Value})"); _playerDisplayNames[current.Id.Value] = ((Friend)(ref current)).Name; AddPlayerToTracking(current.Id.Value, time); } } if (string.IsNullOrWhiteSpace(text)) { ((MonoBehaviour)this).StartCoroutine(RetryResolveLobbyName(lobby)); } } private static string TryReadLobbyName(Lobby lobby) { string[] array = new string[5] { "name", "Name", "lobby_name", "LobbyName", "server_name" }; string[] array2 = array; foreach (string text in array2) { try { string data = ((Lobby)(ref lobby)).GetData(text); if (!string.IsNullOrWhiteSpace(data)) { return data; } } catch { } } return string.Empty; } private IEnumerator RetryResolveLobbyName(Lobby lobby) { //IL_000e: 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) float[] delays = new float[4] { 0.5f, 1.5f, 3f, 6f }; float[] array = delays; foreach (float delay in array) { yield return (object)new WaitForSeconds(delay); string name = TryReadLobbyName(lobby); if (!string.IsNullOrWhiteSpace(name)) { _currentLobbyName = name; Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Lobby name resolved (retry +{delay}s): '{name}'"); yield break; } } Debug.LogWarning((object)"[SBGL-CompPluginCheck] Lobby name still blank after all retries (non-host path)."); } private void OnLobbyMemberJoined(Lobby lobby, Friend friend) { //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_000c: 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_0031: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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) if (friend.Id.Value != SteamId.op_Implicit(SteamClient.SteamId)) { float time = Time.time; Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Lobby member joined: {((Friend)(ref friend)).Name} ({friend.Id.Value})"); _playerDisplayNames[friend.Id.Value] = ((Friend)(ref friend)).Name; AddPlayerToTracking(friend.Id.Value, time); } } private void OnLobbyMemberLeave(Lobby lobby, Friend friend) { //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_000e: Unknown result type (might be due to invalid IL or missing references) ulong value = friend.Id.Value; if (value == SteamId.op_Implicit(SteamClient.SteamId)) { _inLobby = false; _playerComplianceStatus.Clear(); _playerFirstSeenTime.Clear(); _knownPeers.Clear(); _playersNotifiedAbout.Clear(); _playerDisplayNames.Clear(); _remotePlayerMods.Clear(); _hasLoggedDiscoveryInfo = false; PlayerPrefs.SetString("LobbyName", ""); Debug.Log((object)"[SBGL-CompPluginCheck] Left lobby — cleared all player tracking"); } else { _playerComplianceStatus.Remove(value); _playerFirstSeenTime.Remove(value); _knownPeers.Remove(value); _playersNotifiedAbout.Remove(value); _playerDisplayNames.Remove(value); _remotePlayerMods.Remove(value); Debug.Log((object)$"[SBGL-CompPluginCheck] Player left lobby: {((Friend)(ref friend)).Name} ({value}) — removed from tracking"); } } private void Awake() { Instance = this; if (!PlayerPrefs.HasKey("CompCheck_X")) { PlayerPrefs.SetFloat("CompCheck_X", 0f); } if (!PlayerPrefs.HasKey("CompCheck_Y")) { PlayerPrefs.SetFloat("CompCheck_Y", 12f); } if (!PlayerPrefs.HasKey("CompCheck_Width")) { PlayerPrefs.SetFloat("CompCheck_Width", 200f); } if (!PlayerPrefs.HasKey("CompCheck_Alpha")) { PlayerPrefs.SetFloat("CompCheck_Alpha", 0.85f); } if (!PlayerPrefs.HasKey("CompCheck_UpdateInterval")) { PlayerPrefs.SetFloat("CompCheck_UpdateInterval", 5f); } if (!PlayerPrefs.HasKey("CompCheck_HideUIWindow")) { PlayerPrefs.SetInt("CompCheck_HideUIWindow", 0); } if (!PlayerPrefs.HasKey("CompCheck_ShowModList")) { PlayerPrefs.SetInt("CompCheck_ShowModList", 0); } if (!PlayerPrefs.HasKey("CompCheck_ShowDebugWindow")) { PlayerPrefs.SetInt("CompCheck_ShowDebugWindow", 0); } if (!PlayerPrefs.HasKey("CompCheck_PlayerId")) { PlayerPrefs.SetString("CompCheck_PlayerId", "PASTE_ID_HERE"); } if (HasMelonLoaderLoaded()) { Debug.LogError((object)"[SBGL-CompPluginCheck] ⚠\ufe0f⚠\ufe0f⚠\ufe0f STARTUP DETECTION: MELONLOADER IS LOADED ⚠\ufe0f⚠\ufe0f⚠\ufe0f"); } UnifiedPlugin.ApiConfigChanged += OnApiConfigChanged; AppDomain.CurrentDomain.AssemblyLoad += OnRuntimeAssemblyLoaded; CaptureLoadedRuntimeAssemblies(); ((MonoBehaviour)this).StartCoroutine(GetNetworkTime()); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } UnifiedPlugin.ApiConfigChanged -= OnApiConfigChanged; AppDomain.CurrentDomain.AssemblyLoad -= OnRuntimeAssemblyLoaded; } private void OnApiConfigChanged() { ((MonoBehaviour)this).StopCoroutine(AutoUpdateLoop()); ((MonoBehaviour)this).StartCoroutine(AutoUpdateLoop()); } private void Start() { if (!string.IsNullOrEmpty(ConfigPlayerId) && ConfigPlayerId != "PASTE_ID_HERE") { ResolvedID = ConfigPlayerId; } CreateUI(); ((MonoBehaviour)this).StartCoroutine(AutoUpdateLoop()); } private void Update() { //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: 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_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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_0432: Unknown result type (might be due to invalid IL or missing references) Keyboard current = Keyboard.current; if (current == null) { return; } if (((ButtonControl)current[(Key)102]).wasPressedThisFrame) { ConfigHideUIWindow = !ConfigHideUIWindow; } if (((ButtonControl)current[(Key)103]).wasPressedThisFrame) { TriggerManualSync(); } if (((ButtonControl)current[(Key)104]).wasPressedThisFrame) { ConfigShowDebugWindow = !ConfigShowDebugWindow; UpdateUIReport(); } if (Time.frameCount % 30 == 0) { string spectatedPlayerName = GetSpectatedPlayerName(); if (spectatedPlayerName != _spectatedPlayerName) { _spectatedPlayerName = spectatedPlayerName; if (string.IsNullOrEmpty(spectatedPlayerName)) { TriggerManualSync(); } else { if ((Object)(object)_profileIcon != (Object)null) { ((Graphic)_profileIcon).color = new Color(1f, 1f, 1f, 0f); } if ((Object)(object)_flagIcon != (Object)null) { ((Graphic)_flagIcon).color = new Color(1f, 1f, 1f, 0f); } ((MonoBehaviour)this).StartCoroutine(FetchPlayerDataByName(spectatedPlayerName)); } } } if ((Object)(object)_bgRect != (Object)null) { float num = _bgRect.sizeDelta.x / 2f; float num2 = Mathf.Clamp(ConfigX, 0f - ((float)Screen.width / 2f - num), (float)Screen.width / 2f - num); float num3 = Mathf.Clamp(ConfigY, 0f, (float)Screen.height - _bgRect.sizeDelta.y); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(num2, num3); if (_bgRect.anchoredPosition != val) { _bgRect.anchoredPosition = val; } float configAlpha = ConfigAlpha; if (((Graphic)_bgImage).color.a != configAlpha) { ((Graphic)_bgImage).color = new Color(0.04f, 0.06f, 0.08f, configAlpha); } if (num2 != ConfigX && _configX != null) { _configX.Value = num2; } if (num3 != ConfigY && _configY != null) { _configY.Value = num3; } } bool flag = !ConfigHideUIWindow; if ((Object)(object)_bgObj != (Object)null && _bgObj.activeSelf != flag) { _bgObj.SetActive(flag); } bool configShowDebugWindow = ConfigShowDebugWindow; if ((Object)(object)_debugWindowObj != (Object)null && _debugWindowObj.activeSelf != configShowDebugWindow) { _debugWindowObj.SetActive(configShowDebugWindow); } if (Time.frameCount % 60 == 0) { int num4 = 0; try { num4 = Object.FindObjectsByType((FindObjectsSortMode)0).Length; } catch { } if (num4 != _lastPlayerCosmeticsCount) { Debug.Log((object)$"[SBGL-CompPluginCheck] PlayerCosmetics count changed: {_lastPlayerCosmeticsCount} → {num4}, triggering immediate discovery"); _lastPlayerCosmeticsCount = num4; _hasLoggedDiscoveryInfo = false; DiscoverLobbyPlayers(); } } if (Time.frameCount % 300 == 0) { DiscoverLobbyPlayers(); } if (HasMelonLoaderLoaded() && _inLobby) { float num5 = Time.time - _lastMelonLoaderAnnouncementTime; if (num5 >= 10f) { AnnounceOwnMelonLoaderToChat(); _lastMelonLoaderAnnouncementTime = Time.time; } } if (_inLobby && Time.time - _lastLobbyNameCheck > 45f) { _lastLobbyNameCheck = Time.time; string text = TryReadLobbyName(_currentLobby); if (!string.IsNullOrWhiteSpace(text) && text != _currentLobbyName) { _currentLobbyName = text; Debug.Log((object)("[SBGL-CompPluginCheck] Lobby name refreshed: '" + text + "'")); } } if (Time.frameCount % 120 == 0) { AcceptIncomingP2PSessions(); } ListenForModReports(); if (Time.frameCount % 600 == 0) { Debug.Log((object)$"[SBGL-CompPluginCheck] Frame {Time.frameCount}: Triggering BroadcastMyMods()"); BroadcastMyMods(); } } private void OnGUI() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (ConfigShowDebugWindow) { Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name.ToLower().Contains("driving range")) { DrawPlayerComplianceDebugUI(); } } } private void DrawPlayerComplianceDebugUI() { //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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0038: Expected O, but got Unknown //IL_00be: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //IL_0120: 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_012a: 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_009d: Unknown result type (might be due to invalid IL or missing references) if (_compWindowStyle == null) { _compWindowStyle = new GUIStyle(GUI.skin.box) { padding = new RectOffset(12, 12, 12, 12) }; } if (!_compliancePanelRectInit) { float num = ((ConfigCompliancePanelX > 0f) ? ConfigCompliancePanelX : ((float)Screen.width - 480f)); float num2 = ((ConfigCompliancePanelY > 0f) ? ConfigCompliancePanelY : ((float)Screen.height - 350f)); _compliancePanelRect = new Rect(num, num2, 10f, 10f); _compliancePanelRectInit = true; } GUI.backgroundColor = new Color(0.07f, 0.07f, 0.07f, 0.95f); _compliancePanelRect = GUILayout.Window(42424, _compliancePanelRect, new WindowFunction(DrawComplianceWindowContent), "", _compWindowStyle, (GUILayoutOption[])(object)new GUILayoutOption[3] { GUILayout.MinWidth(300f), GUILayout.MaxWidth(500f), GUILayout.MaxHeight((float)Screen.height - 40f) }); GUI.backgroundColor = Color.white; } private void DrawComplianceWindowContent(int windowId) { //IL_000b: 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_0019: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003d: 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_004a: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_006f: 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_007c: 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_008d: 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_009f: Expected O, but got Unknown //IL_00a9: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: 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_00d1: Expected O, but got Unknown //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 15, fontStyle = (FontStyle)1 }; val.normal.textColor = Color.white; GUIStyle val2 = val; GUIStyle val3 = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 12 }; val3.normal.textColor = Color.white; GUIStyle val4 = val3; GUIStyle val5 = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 13, fontStyle = (FontStyle)1 }; val5.normal.textColor = Color.white; GUIStyle val6 = val5; GUIStyle val7 = new GUIStyle(GUI.skin.label) { richText = true, fontSize = 11 }; val7.normal.textColor = Color.white; GUIStyle val8 = val7; GUILayout.Label("PLAYER COMPLIANCE", val2, Array.Empty()); GUILayout.Space(4f); int count = _playerComplianceStatus.Count; int num = _playerComplianceStatus.Values.Count((PlayerComplianceStatus s) => s.IsCompliant && s.HasReportedMods && !s.HasMelonLoader); int num2 = _playerComplianceStatus.Values.Count((PlayerComplianceStatus s) => s.HasReportedMods && (!s.IsCompliant || s.HasMelonLoader)); int num3 = _playerComplianceStatus.Values.Count((PlayerComplianceStatus s) => !s.HasReportedMods); GUILayout.Label($"Players: {count} ✓ {num} ✗ {num2} ? {num3}", val4, Array.Empty()); GUILayout.Space(6f); if (_playerComplianceStatus.Count == 0) { GUIStyle val9 = new GUIStyle(GUI.skin.label) { fontSize = 12 }; val9.normal.textColor = new Color(0.6f, 0.6f, 0.6f); GUILayout.Label("Waiting for players...", val9, Array.Empty()); } else { HashSet hashSet = new HashSet(_allowedModsSnapshot.DisplayNames.Select((string v) => v.ToLowerInvariant())); foreach (KeyValuePair item in _playerComplianceStatus) { PlayerComplianceStatus value = item.Value; string text; string text2; if (!value.HasReportedMods) { text = "?"; text2 = "yellow"; } else if (value.HasMelonLoader) { text = "⚠"; text2 = "orange"; } else if (value.IsCompliant) { text = "✓"; text2 = "lime"; } else { text = "✗"; text2 = "red"; } string value2; string text3 = (_playerDisplayNames.TryGetValue(value.SteamId, out value2) ? value2 : value.SteamId.ToString()); GUILayout.Label("[" + text + "] " + text3, val6, Array.Empty()); if (value.HasMelonLoader) { GUILayout.Label(" ⚠ MelonLoader detected", val8, Array.Empty()); } if (_remotePlayerMods.TryGetValue(value.SteamId, out var value3)) { string[] array = value3.Split(';'); foreach (string text4 in array) { if (!string.IsNullOrEmpty(text4) && !text4.Contains("USER_HAS_MELONLOADER")) { string text5 = text4.Split('|')[0]; bool flag = text5.StartsWith("⚡") || hashSet.Contains(text5.ToLowerInvariant()); string text6 = (flag ? "#AAFFAA" : "#FF4444"); string text7 = (flag ? "✓" : "✗"); GUILayout.Label(" " + text7 + " " + text5 + "", val8, Array.Empty()); } } } GUILayout.Space(5f); } } GUI.DragWindow(); } private bool HasMelonLoaderLoaded() { return MelonLoaderBridge.IsMelonLoaderLoaded; } private void DiscoverLobbyPlayers() { //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_0371: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0552: Unknown result type (might be due to invalid IL or missing references) //IL_0557: Unknown result type (might be due to invalid IL or missing references) //IL_055a: Unknown result type (might be due to invalid IL or missing references) //IL_055c: Unknown result type (might be due to invalid IL or missing references) //IL_0575: Unknown result type (might be due to invalid IL or missing references) //IL_0577: Unknown result type (might be due to invalid IL or missing references) //IL_0581: Unknown result type (might be due to invalid IL or missing references) //IL_059e: Unknown result type (might be due to invalid IL or missing references) //IL_05a0: Unknown result type (might be due to invalid IL or missing references) //IL_05c5: Unknown result type (might be due to invalid IL or missing references) //IL_05c7: Unknown result type (might be due to invalid IL or missing references) //IL_05e2: Unknown result type (might be due to invalid IL or missing references) //IL_05e4: Unknown result type (might be due to invalid IL or missing references) //IL_0611: Unknown result type (might be due to invalid IL or missing references) //IL_0613: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid) { return; } try { float time = Time.time; Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name != _lastDiscoveryScene) { _hasLoggedDiscoveryInfo = false; _lastDiscoveryScene = name; _lastPlayerCosmeticsCount = 0; _playerComplianceStatus.Clear(); _playerFirstSeenTime.Clear(); _knownPeers.Clear(); _playersNotifiedAbout.Clear(); _playerDisplayNames.Clear(); _remotePlayerMods.Clear(); Debug.Log((object)("[SBGL-CompPluginCheck] Scene changed to: " + name + " - cleared player tracking and resetting discovery")); } bool flag = !_hasLoggedDiscoveryInfo; if (flag && !_playerComplianceStatus.ContainsKey(SteamId.op_Implicit(SteamClient.SteamId))) { bool flag2 = HasMelonLoaderLoaded(); _playerComplianceStatus[SteamId.op_Implicit(SteamClient.SteamId)] = new PlayerComplianceStatus { SteamId = SteamId.op_Implicit(SteamClient.SteamId), FirstSeenTime = time, IsCompliant = true, HasReportedMods = true, HasMelonLoader = flag2, ModList = "⚡SBGL.UnifiedMod|1.0.0" }; if (!_playerFirstSeenTime.ContainsKey(SteamId.op_Implicit(SteamClient.SteamId))) { _playerFirstSeenTime[SteamId.op_Implicit(SteamClient.SteamId)] = time; } Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Added self ({SteamClient.SteamId}) to tracking as COMPLIANT (MelonLoader={flag2})"); _playerDisplayNames[SteamId.op_Implicit(SteamClient.SteamId)] = SteamClient.Name ?? "Me"; } PlayerCosmetics[] array = (PlayerCosmetics[])(object)new PlayerCosmetics[0]; try { array = Object.FindObjectsByType((FindObjectsSortMode)0); if (flag) { Debug.Log((object)$"[SBGL-CompPluginCheck] \ud83d\udd0d Scene discovery: Found {array.Length} PlayerCosmetics components"); } PlayerCosmetics[] array2 = array; foreach (PlayerCosmetics val in array2) { try { GameObject gameObject = ((Component)val).gameObject; string name2 = ((Object)gameObject).name; if (flag) { Debug.Log((object)("[SBGL-CompPluginCheck] → PlayerCosmetics object: '" + name2 + "'")); } NetworkIdentity component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null) { if (flag) { Debug.Log((object)"[SBGL-CompPluginCheck] → Has NetworkIdentity, checking for owner info"); } ulong num = 0uL; bool flag3 = false; try { PropertyInfo property = typeof(NetworkIdentity).GetProperty("connectionToClient", BindingFlags.Instance | BindingFlags.Public); if (property != null) { object value = property.GetValue(component); if (value != null && flag) { Debug.Log((object)("[SBGL-CompPluginCheck] → Has connectionToClient: " + value.GetType().Name)); Type type = value.GetType(); PropertyInfo property2 = type.GetProperty("authenticationData", BindingFlags.Instance | BindingFlags.Public); if (property2 != null) { object value2 = property2.GetValue(value); if (value2 != null) { Debug.Log((object)$"[SBGL-CompPluginCheck] → Auth data: {value2}"); } } } } } catch { } if (ulong.TryParse(name2, out var result)) { num = result; flag3 = true; if (flag) { Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Extracted Steam ID from name: {result}"); } } if (flag3) { if (num == SteamId.op_Implicit(SteamClient.SteamId)) { if (flag) { Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Skipping self ({num})"); } continue; } if (flag) { Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Discovered player: {num}"); } AddPlayerToTracking(num, time); } else if (flag) { Debug.Log((object)("[SBGL-CompPluginCheck] ⚠\ufe0f PlayerCosmetics '" + name2 + "' has no Steam ID in name. Logging details...")); Type type2 = ((object)component).GetType(); PropertyInfo property3 = type2.GetProperty("ownerId", BindingFlags.Instance | BindingFlags.Public); if (property3 != null) { object value3 = property3.GetValue(component); Debug.Log((object)$"[SBGL-CompPluginCheck] → ownerId: {value3}"); } PropertyInfo property4 = type2.GetProperty("netId", BindingFlags.Instance | BindingFlags.Public); if (property4 != null) { object value4 = property4.GetValue(component); Debug.Log((object)$"[SBGL-CompPluginCheck] → netId: {value4}"); } } } else if (flag) { Debug.Log((object)"[SBGL-CompPluginCheck] ⚠\ufe0f PlayerCosmetics has no NetworkIdentity"); } } catch (Exception ex) { if (flag) { Debug.LogError((object)("[SBGL-CompPluginCheck] Error processing PlayerCosmetics: " + ex.Message)); } } } if (flag) { Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Discovery complete: {_playerComplianceStatus.Count} players added to tracking"); } } catch (Exception ex2) { if (flag) { Debug.LogError((object)("[SBGL-CompPluginCheck] Error in PlayerCosmetics discovery: " + ex2.Message)); } } if (_inLobby) { try { foreach (Friend member in ((Lobby)(ref _currentLobby)).Members) { Friend current = member; if (current.Id.Value != 0 && current.Id.Value != SteamId.op_Implicit(SteamClient.SteamId)) { if (!_knownPeers.Contains(current.Id.Value)) { Debug.Log((object)$"[SBGL-CompPluginCheck] ✓ Lobby refresh found new peer: {((Friend)(ref current)).Name} ({current.Id.Value})"); AddPlayerToTracking(current.Id.Value, time); } if (!string.IsNullOrEmpty(((Friend)(ref current)).Name)) { _playerDisplayNames[current.Id.Value] = ((Friend)(ref current)).Name; } } } } catch (Exception ex3) { if (flag) { Debug.LogWarning((object)("[SBGL-CompPluginCheck] Lobby member refresh error: " + ex3.Message)); } } } else if (flag) { Debug.Log((object)"[SBGL-CompPluginCheck] ⚠\ufe0f Not in a tracked lobby yet - OnLobbyEntered has not fired"); } _hasLoggedDiscoveryInfo = true; List list = new List(); foreach (KeyValuePair item in _playerComplianceStatus) { ulong key = item.Key; PlayerComplianceStatus value5 = item.Value; if (!value5.HasReportedMods && _playerFirstSeenTime.TryGetValue(key, out var value6) && time - value6 >= 15f) { list.Add(key); } } foreach (ulong item2 in list) { PlayerComplianceStatus playerComplianceStatus = _playerComplianceStatus[item2]; if (!playerComplianceStatus.HasReportedMods) { Debug.LogWarning((object)$"[SBGL-CompPluginCheck] Player {item2} timed out without reporting mods - marking as non-compliant"); playerComplianceStatus.IsCompliant = false; playerComplianceStatus.ModList = "(No report received)"; SendComplianceNotification(item2, playerComplianceStatus); UpdateUIReport(); } } } catch (Exception ex4) { Debug.LogError((object)("[SBGL-CompPluginCheck] Fatal error in DiscoverLobbyPlayers: " + ex4.Message + "\n" + ex4.StackTrace)); } } private void AddPlayerToTracking(ulong steamId, float now) { if (!_playerComplianceStatus.ContainsKey(steamId)) { _playerComplianceStatus[steamId] = new PlayerComplianceStatus { SteamId = steamId, FirstSeenTime = now, IsCompliant = false, HasReportedMods = false }; if (!_playerFirstSeenTime.ContainsKey(steamId)) { _playerFirstSeenTime[steamId] = now; } _knownPeers.Add(steamId); Debug.Log((object)$"[SBGL-CompPluginCheck] Added player {steamId} to tracking (knownPeers={_knownPeers.Count})"); TrySendCurrentMatchIdToPeer(steamId, "peer discovered"); UpdateUIReport(); } } private static bool ShouldShareCurrentMatchId() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) string currentMatchId = SBGLPlugin.CurrentMatchId; if (string.IsNullOrWhiteSpace(currentMatchId)) { return false; } Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name?.ToLowerInvariant() ?? string.Empty; if (text.Contains("menu") || text.Contains("driving") || text.Contains("range") || text.Contains("lobby")) { return false; } return true; } public IEnumerable GetKnownPeerIds() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(_knownPeers); hashSet.UnionWith(_remotePlayerMods.Keys); hashSet.Remove(SteamId.op_Implicit(SteamClient.SteamId)); return hashSet; } private void TrySendCurrentMatchIdToPeer(ulong peerId, string reason) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid || peerId == 0L || peerId == SteamId.op_Implicit(SteamClient.SteamId) || !ShouldShareCurrentMatchId()) { return; } string currentMatchId = SBGLPlugin.CurrentMatchId; if (string.IsNullOrWhiteSpace(currentMatchId)) { return; } try { byte[] bytes = Encoding.UTF8.GetBytes("SBGL_MATCH_ID:" + currentMatchId); SteamNetworking.SendP2PPacket(SteamId.op_Implicit(peerId), bytes, -1, 2622, (P2PSend)2); Debug.Log((object)$"[SBGL-CompPluginCheck] Sent active match ID {currentMatchId} to peer {peerId} ({reason})"); } catch (Exception ex) { Debug.LogWarning((object)$"[SBGL-CompPluginCheck] Failed to send active match ID to peer {peerId} ({reason}): {ex.Message}"); } } private void AcceptIncomingP2PSessions() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0114: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid) { return; } try { foreach (ulong item in _playerComplianceStatus.Keys.ToList()) { if (item != SteamId.op_Implicit(SteamClient.SteamId)) { try { SteamNetworking.AcceptP2PSessionWithUser(SteamId.op_Implicit(item)); } catch { } } } foreach (ulong item2 in _knownPeers.ToList()) { if (item2 != SteamId.op_Implicit(SteamClient.SteamId)) { try { SteamNetworking.AcceptP2PSessionWithUser(SteamId.op_Implicit(item2)); } catch { } } } if (Time.frameCount % 1200 == 0) { Debug.Log((object)$"[SBGL-CompPluginCheck] SteamClient.IsValid={SteamClient.IsValid}, SteamClient.SteamId={SteamClient.SteamId}, Known Peers={_knownPeers.Count}, Tracked Players={_playerComplianceStatus.Count}"); } } catch (Exception ex) { Debug.LogWarning((object)("[SBGL-CompPluginCheck] Error in AcceptIncomingP2PSessions: " + ex.Message)); } } private void BroadcastMyMods() { //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_015b: 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_0199: 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_01b7: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid) { Debug.Log((object)"[SBGL-CompPluginCheck] BroadcastMyMods: SteamClient not valid"); return; } try { StringBuilder stringBuilder = new StringBuilder("SBGL_REPORT:"); UnifiedPlugin instance = UnifiedPlugin.Instance; string text = ((instance == null) ? null : ((BaseUnityPlugin)instance).Info.Metadata.Version?.ToString()) ?? "0.0.0"; stringBuilder.Append("⚡SBGL.UnifiedMod|" + text + ";"); bool flag = HasMelonLoaderLoaded(); if (flag) { stringBuilder.Append("⚠\ufe0fUSER_HAS_MELONLOADER;"); if (_lastMelonLoaderAnnouncementTime < 0f) { Debug.LogWarning((object)"[SBGL-CompPluginCheck] MelonLoader detected on this client."); } } foreach (PluginInfo value3 in Chainloader.PluginInfos.Values) { if (!ShouldIgnorePluginGuid(value3.Metadata.GUID)) { stringBuilder.Append($"{value3.Metadata.Name}|{value3.Metadata.Version};"); } } Debug.Log((object)$"[SBGL-CompPluginCheck] BroadcastMyMods (MelonLoader={flag})"); string value = stringBuilder.ToString().Substring("SBGL_REPORT:".Length); _remotePlayerMods[SteamId.op_Implicit(SteamClient.SteamId)] = value; if (_playerComplianceStatus.TryGetValue(SteamId.op_Implicit(SteamClient.SteamId), out var value2)) { value2.HasMelonLoader = flag; } if (!_playerDisplayNames.ContainsKey(SteamId.op_Implicit(SteamClient.SteamId))) { _playerDisplayNames[SteamId.op_Implicit(SteamClient.SteamId)] = SteamClient.Name ?? "Me"; } byte[] bytes = Encoding.UTF8.GetBytes(stringBuilder.ToString()); HashSet hashSet = new HashSet(_knownPeers); hashSet.UnionWith(_remotePlayerMods.Keys); hashSet.Remove(SteamId.op_Implicit(SteamClient.SteamId)); if (hashSet.Count > 0) { Debug.Log((object)$"[SBGL-CompPluginCheck] Broadcasting to {hashSet.Count} peers"); } foreach (ulong item in hashSet) { try { SteamNetworking.SendP2PPacket(SteamId.op_Implicit(item), bytes, -1, 2622, (P2PSend)2); } catch (Exception ex) { Debug.LogWarning((object)$"[SBGL-CompPluginCheck] Failed to send to peer {item}: {ex.Message}"); } } } catch (Exception ex2) { Debug.LogError((object)("[SBGL-CompPluginCheck] Error in BroadcastMyMods: " + ex2.Message + "\n" + ex2.StackTrace)); } } private void ListenForModReports() { //IL_010c: 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_0054: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_018c: 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_028d: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) if (!SteamClient.IsValid) { return; } uint num = default(uint); while (SteamNetworking.IsP2PPacketAvailable(ref num, 2622)) { byte[] array = new byte[num]; uint num2 = 0u; SteamId val = default(SteamId); if (!SteamNetworking.ReadP2PPacket(array, ref num2, ref val, 2622)) { continue; } string text = Encoding.UTF8.GetString(array); Debug.Log((object)$"[SBGL-CompPluginCheck] Received from peer {val.Value}: {text}"); _knownPeers.Add(val.Value); if (text.StartsWith("SBGL_POLL_")) { PollManager.Instance?.HandlePacket(text, val.Value); } else if (text.StartsWith("SBGL_MATCH_ID:")) { string text2 = text.Replace("SBGL_MATCH_ID:", "").Trim(); try { ((MonoBehaviour)this).StartCoroutine(ForwardP2PMatchIdWithDelay(text2, 2f, val.Value)); } catch { MatchResultSubmissionService.HandleIncomingMatchIdBroadcast(text2); Debug.Log((object)$"[SBGL-CompPluginCheck] Forwarded P2P Match ID (immediate fallback) from {val.Value}: {text2}"); } } else if (text.StartsWith("SBGL_REPORT:")) { string text3 = text.Replace("SBGL_REPORT:", ""); TrySendCurrentMatchIdToPeer(val.Value, "mod report received"); if (!_playerComplianceStatus.ContainsKey(val.Value)) { _playerComplianceStatus[val.Value] = new PlayerComplianceStatus { FirstSeenTime = Time.time }; } PlayerComplianceStatus playerComplianceStatus = _playerComplianceStatus[val.Value]; playerComplianceStatus.SteamId = val.Value; playerComplianceStatus.HasReportedMods = true; playerComplianceStatus.ModList = text3; playerComplianceStatus.IsCompliant = text3.Contains("⚡SBGL.UnifiedMod"); playerComplianceStatus.HasMelonLoader = text3.Contains("⚠\ufe0fUSER_HAS_MELONLOADER"); if (playerComplianceStatus.HasMelonLoader) { Debug.LogError((object)$"[SBGL-CompPluginCheck] ⚠\ufe0f⚠\ufe0f⚠\ufe0f ALERT: Player {val.Value} HAS MELONLOADER INSTALLED ⚠\ufe0f⚠\ufe0f⚠\ufe0f"); text3 = text3.Replace("⚠\ufe0fUSER_HAS_MELONLOADER;", ""); } if (!playerComplianceStatus.IsCompliant) { Debug.LogError((object)$"[SBGL-CompPluginCheck] ⚠\ufe0f MISSING MOD ALERT: Player {val.Value} does NOT have SBGL.UnifiedMod installed! Their mod list: {text3}"); SendComplianceNotification(val.Value, playerComplianceStatus); } _remotePlayerMods[val.Value] = text3; UpdateUIReport(); } } } private IEnumerator ForwardP2PMatchIdWithDelay(string matchId, float delaySeconds, ulong senderId) { yield return (object)new WaitForSeconds(delaySeconds); try { MatchResultSubmissionService.HandleIncomingMatchIdBroadcast(matchId); Debug.Log((object)$"[SBGL-CompPluginCheck] Forwarded P2P Match ID (delayed {delaySeconds:0.#}s) from {senderId}: {matchId}"); } catch (Exception ex) { Exception ex2 = ex; Debug.LogWarning((object)("[SBGL-CompPluginCheck] Error forwarding Match ID: " + ex2.Message)); } } internal static IEnumerable GetKnownPeers() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) CompetitivePluginCheck competitivePluginCheck = Object.FindFirstObjectByType(); if ((Object)(object)competitivePluginCheck == (Object)null) { return Array.Empty(); } HashSet hashSet = new HashSet(competitivePluginCheck._knownPeers); hashSet.UnionWith(competitivePluginCheck._remotePlayerMods.Keys); if (SteamClient.IsValid) { hashSet.Remove(SteamId.op_Implicit(SteamClient.SteamId)); } return hashSet; } internal static bool HasHigherVersionPeer() { CompetitivePluginCheck competitivePluginCheck = Object.FindFirstObjectByType(); if ((Object)(object)competitivePluginCheck == (Object)null) { return false; } UnifiedPlugin instance = UnifiedPlugin.Instance; Version version = ((instance != null) ? ((BaseUnityPlugin)instance).Info.Metadata.Version : null) ?? new Version(0, 0, 0); foreach (string value in competitivePluginCheck._remotePlayerMods.Values) { if (string.IsNullOrEmpty(value)) { continue; } string[] array = value.Split(';'); foreach (string text in array) { if (text.StartsWith("⚡SBGL.UnifiedMod|")) { string input = text.Substring("⚡SBGL.UnifiedMod|".Length).Trim(); if (Version.TryParse(input, out Version result) && result > version) { return true; } break; } } } return false; } internal static string GetCurrentSteamLobbyId() { //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_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) CompetitivePluginCheck competitivePluginCheck = Object.FindFirstObjectByType(); if (!((Object)(object)competitivePluginCheck == (Object)null) && competitivePluginCheck._inLobby) { SteamId id = ((Lobby)(ref competitivePluginCheck._currentLobby)).Id; if (((SteamId)(ref id)).IsValid) { id = ((Lobby)(ref competitivePluginCheck._currentLobby)).Id; return id.Value.ToString(); } } return string.Empty; } private void SendComplianceNotification(ulong steamId, PlayerComplianceStatus status) { if (_playersNotifiedAbout.Contains(steamId)) { return; } _playersNotifiedAbout.Add(steamId); string text = string.Format("[SBGL] ⚠\ufe0f Player {0} failed compliance check: {1}", steamId, status.HasMelonLoader ? "MelonLoader detected" : "Missing SBGL.UnifiedMod"); Debug.LogWarning((object)("[SBGL-CompPluginCheck] NOTIFICATION: " + text)); try { Type type = Type.GetType("GameAssembly+Chat, GameAssembly"); if (type != null) { MethodInfo method = type.GetMethod("SendChatMessage", BindingFlags.Static | BindingFlags.Public); if (method != null) { method.Invoke(null, new object[1] { text }); Debug.Log((object)"[SBGL-CompPluginCheck] Chat message sent via game API"); } } } catch (Exception ex) { Debug.Log((object)("[SBGL-CompPluginCheck] Chat API not available (this is OK): " + ex.Message)); } } private void AnnounceOwnMelonLoaderToChat() { //IL_00a8: 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) Debug.Log((object)"[SBGL-CompPluginCheck] ✓ AnnounceOwnMelonLoaderToChat() called"); if (!ConfigMelonLoaderChatEnabled) { Debug.Log((object)"[SBGL-CompPluginCheck] MelonLoader chat announcement is disabled in config - skipping"); } else { if (!_inLobby) { return; } string currentLobbyName = _currentLobbyName; Debug.Log((object)("[SBGL-CompPluginCheck] Lobby name check: '" + currentLobbyName + "'")); if (!currentLobbyName.StartsWith("SBGL-")) { Debug.Log((object)("[SBGL-CompPluginCheck] Lobby '" + currentLobbyName + "' is not an SBGL lobby - skipping announcement")); return; } string text = UnifiedPlugin.GetPlayerProfile()?.DisplayName; if (string.IsNullOrEmpty(text)) { text = ((object)SteamClient.SteamId/*cast due to .constrained prefix*/).ToString(); } string text2 = "⚠\ufe0f MELONLOADER DETECTED: Player " + text + " is running MelonLoader!"; Debug.LogWarning((object)("[SBGL-CompPluginCheck] Announcing MelonLoader presence: " + text2)); if (!TrySendChatMessage(text2)) { Debug.Log((object)"[SBGL-CompPluginCheck] ℹ\ufe0f Chat API not available (game may not have public chat API). MelonLoader detection is still being broadcast via P2P to other SBGL mod users."); } } } private bool TrySendChatMessage(string message) { string[] array = new string[3] { "GameAssembly+TextChatManager, GameAssembly", "GameAssembly.TextChatManager, GameAssembly", "TextChatManager, GameAssembly" }; string[] array2 = new string[5] { "SendChatMessage", "PostMessage", "AddMessage", "Send", "SendMessage" }; string[] array3 = array; foreach (string text in array3) { try { Type type = Type.GetType(text); if (type == null) { continue; } Debug.Log((object)("[SBGL-CompPluginCheck] Found TextChatManager type: " + text)); string[] array4 = array2; foreach (string text2 in array4) { try { MethodInfo method = type.GetMethod(text2, BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method != null) { method.Invoke(null, new object[1] { message }); Debug.Log((object)("[SBGL-CompPluginCheck] ✓ Chat message sent via static " + text + "." + text2)); return true; } } catch { } } PropertyInfo propertyInfo = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public) ?? type.GetProperty("Singleton", BindingFlags.Static | BindingFlags.Public) ?? type.GetField("_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.DeclaringType?.GetProperty("_instance", BindingFlags.Static | BindingFlags.Public); if (propertyInfo == null) { FieldInfo field = type.GetField("Instance", BindingFlags.Static | BindingFlags.Public); if (field != null) { object value = field.GetValue(null); if (value != null) { string[] array5 = array2; foreach (string text3 in array5) { try { MethodInfo method2 = type.GetMethod(text3, BindingFlags.Instance | BindingFlags.Public); if (method2 != null) { method2.Invoke(value, new object[1] { message }); Debug.Log((object)("[SBGL-CompPluginCheck] ✓ Chat message sent via " + text + "." + text3 + "() [instance field]")); return true; } } catch { } } } } } else if ((object)propertyInfo != null) { PropertyInfo propertyInfo2 = propertyInfo; if (true) { object value2 = propertyInfo2.GetValue(null); if (value2 != null) { string[] array6 = array2; foreach (string text4 in array6) { try { MethodInfo method3 = type.GetMethod(text4, BindingFlags.Instance | BindingFlags.Public); if (method3 != null) { method3.Invoke(value2, new object[1] { message }); Debug.Log((object)("[SBGL-CompPluginCheck] ✓ Chat message sent via " + text + "." + text4 + "() [singleton]")); return true; } } catch { } } } } } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); Debug.Log((object)("[SBGL-CompPluginCheck] Available methods on " + text + ": " + string.Join(", ", methods.Select((MethodInfo m) => m.Name).Distinct()))); } catch (Exception ex) { Debug.Log((object)("[SBGL-CompPluginCheck] Error trying " + text + ": " + ex.Message)); } } Debug.Log((object)"[SBGL-CompPluginCheck] TextChatManager not found or no compatible method available"); return false; } public void TriggerManualSync() { ((MonoBehaviour)this).StopAllCoroutines(); _isSyncing = false; ((MonoBehaviour)this).StartCoroutine(AutoUpdateLoop()); } private string GetOrdinal(int number) { if (number <= 0) { return number.ToString(); } int num = number % 100; int num2 = num; if ((uint)(num2 - 11) <= 2u) { return number + "th"; } return (number % 10) switch { 1 => number + "st", 2 => number + "nd", 3 => number + "rd", _ => number + "th", }; } private void ResetPlayerData(string userStatus = "Updating...") { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) _activeUsername = userStatus; _playerRank = "N/A"; _playerMMR = "0"; _playerPeak = "0"; _matches = "0"; _lastChange = "0"; _top3s = "0"; _avgScore = "0.0"; _winRate = "0%"; if ((Object)(object)_profileIcon != (Object)null) { ((Graphic)_profileIcon).color = new Color(1f, 1f, 1f, 0f); } if ((Object)(object)_flagIcon != (Object)null) { ((Graphic)_flagIcon).color = new Color(1f, 1f, 1f, 0f); } _playerRegion = ""; _playerState = ""; } private string GetInGameName() { try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "com.rlabrecque.steamworks.net" || a.GetName().Name.Contains("Steamworks")); if (assembly != null) { Type type = assembly.GetType("Steamworks.SteamClient"); if (type != null) { string text = type.GetProperty("Name", BindingFlags.Static | BindingFlags.Public)?.GetValue(null)?.ToString(); if (!string.IsNullOrEmpty(text)) { return text; } } } } catch (Exception ex) { Debug.Log((object)("[SBGL] Steam Reflection failed (Safe): " + ex.Message)); } return ""; } private IEnumerator AutoUpdateLoop() { float waitTimeout = 0f; while (!UnifiedPlugin.IsPlayerProfileResolved() && waitTimeout < 12f) { yield return (object)new WaitForSeconds(0.5f); waitTimeout += 0.5f; } if (UnifiedPlugin.IsPlayerProfileResolved()) { SharedPlayerProfile profile = UnifiedPlugin.GetPlayerProfile(); Debug.Log((object)("[CompetitivePluginCheck] Profile now resolved: " + profile.DisplayName + " (ID: " + profile.ID + ")")); } while (true) { yield return TriggerFullSync(ConfigPlayerId?.Trim()); for (float timer = _configUpdateInterval.Value * 60f; timer > 0f; timer -= 1f) { _timeUntilNextUpdate = timer; UpdateUIReport(); yield return (object)new WaitForSeconds(1f); } } } private IEnumerator TriggerFullSync(string configId) { if (_isSyncing || !string.IsNullOrEmpty(_spectatedPlayerName)) { yield break; } _isSyncing = true; _syncStatus = "Syncing..."; ResetPlayerData(); UpdateUIReport(); string idToUse = ""; if (UnifiedPlugin.IsPlayerProfileResolved()) { SharedPlayerProfile profile = UnifiedPlugin.GetPlayerProfile(); idToUse = profile.ID; ResolvedID = profile.ID; ResolvedName = profile.DisplayName; } else { string steamName = GetInGameName(); if (!string.IsNullOrEmpty(steamName)) { yield return ResolvePlayerIdFromName(steamName); if (ResolvedID != "None") { idToUse = ResolvedID; } } } if ((string.IsNullOrEmpty(idToUse) || idToUse == "None") && !string.IsNullOrEmpty(configId) && configId != "PASTE_ID_HERE") { idToUse = configId; ResolvedID = configId; } yield return CheckPluginsRoutine(); if (!string.IsNullOrEmpty(idToUse) && idToUse != "None" && idToUse != "PASTE_ID_HERE") { yield return FetchPlayerData(idToUse); yield return FetchLeaderboardRank(idToUse); yield return GetNetworkTime(); yield return FetchEvents(); yield return FetchRecentMatches(idToUse); _syncStatus = ((_activeUsername == "Invalid ID" || _activeUsername == "API Offline") ? "Error" : "Connected"); } else { _syncStatus = "ID Required"; ResetPlayerData("Link ID in Settings"); } MenuTabs menuTabs = Object.FindFirstObjectByType(); if ((Object)(object)menuTabs != (Object)null) { SBGLTabManager.Inject(menuTabs); } _isSyncing = false; UpdateUIReport(); } private IEnumerator FetchRecentMatches(string id) { string authToken = UnifiedPlugin.GetCurrentAuthToken(); string url = UnifiedPlugin.GetCurrentBaseApi() + "/match_entry?player_id=eq." + UnityWebRequest.EscapeURL(id) + "&order=match_date.desc&limit=5"; UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", authToken); r.SetRequestHeader("Authorization", "Bearer " + authToken); r.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); yield return r.SendWebRequest(); if ((int)r.result != 1) { yield break; } try { JArray ja = JArray.Parse(r.downloadHandler.text); _recentMatches.Clear(); foreach (JObject item in ((IEnumerable)ja).OfType()) { JToken obj = item["finish_position"]; int pos = (int)((obj != null) ? Extensions.Value((IEnumerable)obj) : 0f); JToken obj2 = item["adjusted_match_score"]; int adj = (int)((obj2 != null) ? Extensions.Value((IEnumerable)obj2) : 0f); JToken obj3 = item["score_vs_par"]; int par = (int)((obj3 != null) ? Extensions.Value((IEnumerable)obj3) : 0f); JToken obj4 = item["mmr_change"]; int mmr = (int)((obj4 != null) ? Extensions.Value((IEnumerable)obj4) : 0f); JToken obj5 = item["post_match_mmr"]; int post = (int)((obj5 != null) ? Extensions.Value((IEnumerable)obj5) : 0f); string dtRaw = ((object)item["match_date"])?.ToString() ?? ""; DateTime dt; string dateStr = (DateTime.TryParse(dtRaw, out dt) ? dt.ToString("MM/dd") : "??/??"); string posStr = GetOrdinal(pos); string mmrColor = ((mmr >= 0) ? "#55FF55" : "#FF5555"); string parSign = ((par > 0) ? "+" : ""); string summary = string.Format("{0} | Pos: {1} | Score: {2} ({3}{4}) | MMR: {5} ({7}{8})", dateStr, posStr, adj, parSign, par, post, mmrColor, (mmr >= 0) ? "+" : "", mmr); _recentMatches.Add(new MatchEntry { match_summary = summary, match_date = dtRaw }); } } catch { } } finally { ((IDisposable)r)?.Dispose(); } } private IEnumerator FetchEvents() { string authToken = UnifiedPlugin.GetCurrentAuthToken(); string url = UnifiedPlugin.GetCurrentBaseApi() + "/pro_series_event?order=event_date.asc&limit=50"; UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", authToken); r.SetRequestHeader("Authorization", "Bearer " + authToken); r.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); yield return r.SendWebRequest(); if ((int)r.result != 1) { yield break; } try { JArray ja = JArray.Parse(r.downloadHandler.text); _upcomingEvents.Clear(); DateTime today = DateTime.Now.Date; foreach (JToken item in ja) { string dateStr = ((object)item[(object)"event_date"])?.ToString() ?? ""; if (DateTime.TryParse(dateStr, out var dt) && dt.Date >= today) { _upcomingEvents.Add(new ProSeriesEvent { name = (((object)item[(object)"name"])?.ToString() ?? "Tournament"), event_date = dateStr }); if (_upcomingEvents.Count >= 5) { break; } } } } catch { } } finally { ((IDisposable)r)?.Dispose(); } } private IEnumerator ResolvePlayerIdFromName(string playerName) { string authToken = UnifiedPlugin.GetCurrentAuthToken(); string url = UnifiedPlugin.GetCurrentBaseApi() + "/match_entry?display_name=ilike." + UnityWebRequest.EscapeURL(playerName) + "&order=match_date.desc&limit=5"; UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", authToken); r.SetRequestHeader("Authorization", "Bearer " + authToken); r.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); yield return r.SendWebRequest(); if ((int)r.result != 1) { yield break; } try { JArray res = JArray.Parse(r.downloadHandler.text); if (((JContainer)res).Count > 0) { ResolvedID = ((object)res[0][(object)"id"])?.ToString() ?? "None"; ResolvedName = ((object)res[0][(object)"display_name"])?.ToString() ?? "Not Found"; if (ConfigPlayerId == "PASTE_ID_HERE") { ConfigPlayerId = ResolvedID; } } else { ResolvedID = "None"; } } catch { ResolvedID = "None"; } } finally { ((IDisposable)r)?.Dispose(); } } private IEnumerator FetchPlayerData(string id) { string authToken = UnifiedPlugin.GetCurrentAuthToken(); string url = UnifiedPlugin.GetCurrentPlayerApi() + "?id=eq." + UnityWebRequest.EscapeURL(id); UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", authToken); r.SetRequestHeader("Authorization", "Bearer " + authToken); r.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); yield return r.SendWebRequest(); if ((int)r.result == 1) { try { JArray ja = JArray.Parse(r.downloadHandler.text); if (((JContainer)ja).Count == 0) { ResetPlayerData("Invalid ID"); yield break; } JToken p = ja[0]; _activeUsername = ((object)p[(object)"display_name"])?.ToString() ?? "Player"; _playerMMR = ((object)p[(object)"current_mmr"])?.ToString() ?? "0"; _playerPeak = ((object)p[(object)"highest_mmr_ever"])?.ToString() ?? "0"; _matches = ((object)p[(object)"matches_played"])?.ToString() ?? "0"; _lastChange = ((object)p[(object)"latest_mmr_change"])?.ToString() ?? "0"; _top3s = ((object)p[(object)"top_3_finishes"])?.ToString() ?? "0"; float.TryParse(((object)p[(object)"average_score_vs_par"])?.ToString() ?? "0", out var avg); _avgScore = avg.ToString("F1"); float w = 0f; float t = 0f; float.TryParse(((object)p[(object)"wins"])?.ToString(), out w); float.TryParse(((object)p[(object)"matches_played"])?.ToString(), out t); _winRate = ((t > 0f) ? $"{w / t * 100f:F0}%" : "0%"); if (p[(object)"profile_pic_url"] != null && !string.IsNullOrEmpty(((object)p[(object)"profile_pic_url"]).ToString())) { ((MonoBehaviour)this).StartCoroutine(DownloadProfilePic(((object)p[(object)"profile_pic_url"]).ToString())); } _playerRegion = ((object)p[(object)"region"])?.ToString() ?? ""; _playerState = ((object)p[(object)"state_province"])?.ToString() ?? ""; JToken obj = p[(object)"region_private"]; ResolveAndShowFlag(regionPrivate: obj != null && obj.ToObject(), stateProvince: _playerState, region: _playerRegion); } catch { ResetPlayerData("Data Error"); } } else { ResetPlayerData("API Offline"); } } finally { ((IDisposable)r)?.Dispose(); } } private IEnumerator FetchLeaderboardRank(string id) { string authToken = UnifiedPlugin.GetCurrentAuthToken(); string url = UnifiedPlugin.GetCurrentPlayerApi() + "?active_status=eq.true&matches_played=gt.0&order=current_mmr.desc&select=id,current_mmr"; UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", authToken); r.SetRequestHeader("Authorization", "Bearer " + authToken); r.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); yield return r.SendWebRequest(); if ((int)r.result != 1) { yield break; } try { JArray ps = JArray.Parse(r.downloadHandler.text); _totalPlayers = ((JContainer)ps).Count.ToString(); int rank = 1; foreach (JToken p in ps) { if (((object)p[(object)"id"])?.ToString() == id) { _playerRank = rank.ToString(); break; } rank++; } } catch { } } finally { ((IDisposable)r)?.Dispose(); } } private IEnumerator GetNetworkTime() { string url = "https://timeapi.io/api/Time/current/zone?timeZone=UTC"; UnityWebRequest webRequest = UnityWebRequest.Get(url); try { webRequest.timeout = 5; yield return webRequest.SendWebRequest(); if ((int)webRequest.result == 1) { try { JObject json = JObject.Parse(webRequest.downloadHandler.text); string timeStr = ((object)json["dateTime"])?.ToString(); if (DateTime.TryParse(timeStr, out var dt)) { _lastSyncTime = dt.ToLocalTime().ToString("HH:mm:ss"); yield break; } } catch { } } } finally { ((IDisposable)webRequest)?.Dispose(); } _lastSyncTime = DateTime.Now.ToString("HH:mm:ss") + " (Local)"; } private IEnumerator DownloadProfilePic(string url) { if (string.IsNullOrEmpty(url)) { yield break; } if (url.StartsWith("http://")) { url = url.Replace("http://", "https://"); } UnityWebRequest request = UnityWebRequestTexture.GetTexture(url); try { request.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); yield return request.SendWebRequest(); if ((int)request.result != 1) { yield break; } Texture2D tex = DownloadHandlerTexture.GetContent(request); _profileIcon.texture = (Texture)(object)tex; if ((Object)(object)tex != (Object)null && ((Texture)tex).width != ((Texture)tex).height) { if (((Texture)tex).width > ((Texture)tex).height) { float u = (float)((Texture)tex).height / (float)((Texture)tex).width; _profileIcon.uvRect = new Rect((1f - u) / 2f, 0f, u, 1f); } else { float v = (float)((Texture)tex).width / (float)((Texture)tex).height; _profileIcon.uvRect = new Rect(0f, (1f - v) / 2f, 1f, v); } } else { _profileIcon.uvRect = new Rect(0f, 0f, 1f, 1f); } ((Graphic)_profileIcon).color = Color.white; } finally { ((IDisposable)request)?.Dispose(); } } private void ResolveAndShowFlag(string stateProvince, string region, bool regionPrivate) { if (regionPrivate) { ((MonoBehaviour)this).StartCoroutine(ShowSbglLogoCoroutine()); return; } string text = NormalizeFlagCode(region); string text2 = (string.IsNullOrEmpty(stateProvince) ? null : (string.IsNullOrEmpty(text) ? NormalizeFlagCode(stateProvince) : (text + "-" + stateProvince.Trim().ToLower()))); if (!string.IsNullOrEmpty(text2)) { ((MonoBehaviour)this).StartCoroutine(DownloadFlagWithFallback(text2, text)); } else if (!string.IsNullOrEmpty(text)) { ((MonoBehaviour)this).StartCoroutine(DownloadFlagWithFallback(text, null)); } else { ((MonoBehaviour)this).StartCoroutine(ShowSbglLogoCoroutine()); } } private string NormalizeFlagCode(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return null; } string text = raw.Trim().ToLower(); if (text == "uk") { text = "gb"; } return (text.Length >= 2) ? text : null; } private IEnumerator DownloadFlagWithFallback(string primaryCode, string fallbackCode) { UnityWebRequest req = UnityWebRequestTexture.GetTexture("https://flagcdn.com/48x36/" + primaryCode + ".png"); try { req.timeout = 5; yield return req.SendWebRequest(); if ((int)req.result == 1 && (Object)(object)_flagIcon != (Object)null) { SetFlagTexture(DownloadHandlerTexture.GetContent(req)); yield break; } } finally { ((IDisposable)req)?.Dispose(); } if (!string.IsNullOrEmpty(fallbackCode)) { ((MonoBehaviour)this).StartCoroutine(DownloadFlagWithFallback(fallbackCode, null)); } else { ((MonoBehaviour)this).StartCoroutine(ShowSbglLogoCoroutine()); } } private void SetFlagTexture(Texture2D tex) { //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0105: 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_0089: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_flagIcon == (Object)null || (Object)(object)tex == (Object)null) { return; } _flagIcon.texture = (Texture)(object)tex; if (((Texture)tex).width != ((Texture)tex).height) { if (((Texture)tex).width > ((Texture)tex).height) { float num = (float)((Texture)tex).height / (float)((Texture)tex).width; _flagIcon.uvRect = new Rect((1f - num) / 2f, 0f, num, 1f); } else { float num2 = (float)((Texture)tex).width / (float)((Texture)tex).height; _flagIcon.uvRect = new Rect(0f, (1f - num2) / 2f, 1f, num2); } } else { _flagIcon.uvRect = new Rect(0f, 0f, 1f, 1f); } ((Graphic)_flagIcon).color = Color.white; } private IEnumerator ShowSbglLogoCoroutine() { if ((Object)(object)_sbglLogoCache != (Object)null) { if ((Object)(object)_flagIcon != (Object)null) { SetFlagTexture(_sbglLogoCache); } yield break; } UnityWebRequest req = UnityWebRequestTexture.GetTexture("https://liquipedia.net/commons/images/1/19/Super_Battle_Golf_League_allmode.png"); try { req.timeout = 10; yield return req.SendWebRequest(); if ((int)req.result == 1 && (Object)(object)_flagIcon != (Object)null) { _sbglLogoCache = DownloadHandlerTexture.GetContent(req); SetFlagTexture(_sbglLogoCache); } } finally { ((IDisposable)req)?.Dispose(); } } private string GetSpectatedPlayerName() { try { PlayerSpectator localPlayerAsSpectator = GameManager.LocalPlayerAsSpectator; if ((Object)(object)localPlayerAsSpectator == (Object)null) { return ""; } PlayerInfo targetPlayer = localPlayerAsSpectator.TargetPlayer; if ((Object)(object)targetPlayer == (Object)null) { return ""; } PlayerId component = ((Component)targetPlayer).GetComponent(); return ((component != null) ? component.PlayerName : null) ?? ""; } catch { return ""; } } private IEnumerator FetchPlayerDataByName(string displayName) { string authToken = UnifiedPlugin.GetCurrentAuthToken(); string url = UnifiedPlugin.GetCurrentPlayerApi() + "?display_name=eq." + UnityWebRequest.EscapeURL(displayName); UnityWebRequest r = UnityWebRequest.Get(url); try { r.SetRequestHeader("apikey", authToken); r.SetRequestHeader("Authorization", "Bearer " + authToken); r.certificateHandler = (CertificateHandler)(object)new BypassCertificate(); yield return r.SendWebRequest(); if ((int)r.result == 1) { try { JArray ja = JArray.Parse(r.downloadHandler.text); if (((JContainer)ja).Count == 0) { _activeUsername = displayName; _playerMMR = "---"; _playerRank = "---"; _winRate = "---"; _matches = "---"; _top3s = "---"; _avgScore = "---"; _lastChange = "0"; _syncStatus = "Not Registered"; UpdateUIReport(); yield break; } JToken p = ja[0]; _activeUsername = ((object)p[(object)"display_name"])?.ToString() ?? displayName; _playerMMR = ((object)p[(object)"current_mmr"])?.ToString() ?? "0"; _playerPeak = ((object)p[(object)"highest_mmr_ever"])?.ToString() ?? "0"; _matches = ((object)p[(object)"matches_played"])?.ToString() ?? "0"; _lastChange = ((object)p[(object)"latest_mmr_change"])?.ToString() ?? "0"; _top3s = ((object)p[(object)"top_3_finishes"])?.ToString() ?? "0"; float.TryParse(((object)p[(object)"average_score_vs_par"])?.ToString() ?? "0", out var avg); _avgScore = avg.ToString("F1"); float w = 0f; float t = 0f; float.TryParse(((object)p[(object)"wins"])?.ToString(), out w); float.TryParse(((object)p[(object)"matches_played"])?.ToString(), out t); _winRate = ((t > 0f) ? $"{w / t * 100f:F0}%" : "0%"); string foundId = ((object)p[(object)"id"])?.ToString() ?? ""; if (!string.IsNullOrEmpty(foundId)) { ((MonoBehaviour)this).StartCoroutine(FetchLeaderboardRank(foundId)); } if (p[(object)"profile_pic_url"] != null && !string.IsNullOrEmpty(((object)p[(object)"profile_pic_url"]).ToString())) { ((MonoBehaviour)this).StartCoroutine(DownloadProfilePic(((object)p[(object)"profile_pic_url"]).ToString())); } _playerRegion = ((object)p[(object)"region"])?.ToString() ?? ""; _playerState = ((object)p[(object)"state_province"])?.ToString() ?? ""; JToken obj = p[(object)"region_private"]; ResolveAndShowFlag(regionPrivate: obj != null && obj.ToObject(), stateProvince: _playerState, region: _playerRegion); _syncStatus = "Spectating"; UpdateUIReport(); } catch { _activeUsername = displayName; UpdateUIReport(); } } else { _activeUsername = displayName; UpdateUIReport(); } } finally { ((IDisposable)r)?.Dispose(); } } private IEnumerator CheckPluginsRoutine() { string url = "https://gist.githubusercontent.com/Kingcox22/59765f02af8dd87179ca920409ff3b27/raw/Approved_Mods.json?t=" + DateTime.Now.Ticks; UnityWebRequest w = UnityWebRequest.Get(url); try { yield return w.SendWebRequest(); if ((int)w.result == 1) { _allowedModsSnapshot = AllowedModsSnapshot.Parse(w.downloadHandler.text); } } finally { ((IDisposable)w)?.Dispose(); } } private void UpdateUIReport() { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) LocalPluginScanResult localPluginScanResult = BuildLocalPluginScanResult(); float.TryParse(_playerMMR, out var result); float.TryParse(_lastChange, out var result2); string rankName = Season2RuleSet.GetRankName(result); string rankColor = Season2RuleSet.GetRankColor(result); if ((Object)(object)_cardNameText != (Object)null) { ((TMP_Text)_cardNameText).text = (_activeUsername ?? "---").ToUpper(); } if ((Object)(object)_cardMMRText != (Object)null) { string text = ((result2 >= 0f) ? "#55FF55" : "#FF5555"); string text2 = "(" + ((result2 >= 0f) ? "+" : "") + _lastChange + ")"; ((TMP_Text)_cardMMRText).text = "" + _playerMMR + " MMR " + rankName + " " + text2 + ""; } Color val = default(Color); if ((Object)(object)_rankColorStripImage != (Object)null && ColorUtility.TryParseHtmlString(rankColor, ref val)) { ((Graphic)_rankColorStripImage).color = new Color(val.r, val.g, val.b, 1f); } if ((Object)(object)_cardRankText != (Object)null) { ((TMP_Text)_cardRankText).text = "#" + _playerRank + " / " + _totalPlayers + " · " + _winRate + " Win Rate"; } if ((Object)(object)_cardSecondaryText != (Object)null) { ((TMP_Text)_cardSecondaryText).text = "" + _avgScore + " Par · " + _matches + " Matches · " + _top3s + " Top 3s"; } if ((Object)(object)_cardSyncText != (Object)null) { ((TMP_Text)_cardSyncText).text = "Sync: " + _lastSyncTime + " | " + _syncStatus; } UpdateDebugWindow(localPluginScanResult); if ((Object)(object)_illegalWarningText != (Object)null) { ((Component)_illegalWarningText).gameObject.SetActive(localPluginScanResult.HasIllegalMods); } if ((Object)(object)_missingWarningText != (Object)null) { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; bool flag = name.Contains("Driving") || name.Contains("Range"); ((Component)_missingWarningText).gameObject.SetActive(localPluginScanResult.MissingModNames.Count > 0 && flag); if (((Component)_missingWarningText).gameObject.activeSelf) { ((TMP_Text)_missingWarningText).text = "MISSING MODS:\n" + string.Join(", ", localPluginScanResult.MissingModNames) + ""; } } } private void UpdateDebugWindow(LocalPluginScanResult localPluginScan) { if ((Object)(object)_debugWindowText == (Object)null || (Object)(object)_debugWindowObj == (Object)null) { return; } if (ConfigShowDebugWindow) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("--- DEBUG INFORMATION ---"); stringBuilder.AppendLine($"Config Show Debug: {ConfigShowDebugWindow}"); stringBuilder.AppendLine($"Remote Players Count: {_remotePlayerMods.Count}"); stringBuilder.AppendLine($"Allowed Mods Count: {_allowedModsSnapshot.Count}"); stringBuilder.AppendLine("Hash Enforcement: " + (_allowedModsSnapshot.HasHashConstraints ? "ON" : "OFF (legacy)")); stringBuilder.AppendLine($"Missing Mods Count: {localPluginScan.MissingModNames.Count}"); stringBuilder.AppendLine($"Tampered Mods Count: {localPluginScan.TamperedModNames.Count}"); stringBuilder.AppendLine($"Suspicious Runtime Assemblies: {localPluginScan.SuspiciousRuntimeAssemblies.Count}"); stringBuilder.AppendLine($"Any Illegal Mods: {localPluginScan.HasIllegalMods}"); stringBuilder.AppendLine("Sync Status: " + _syncStatus); stringBuilder.AppendLine("Last Sync Time: " + _lastSyncTime); stringBuilder.AppendLine("Player ID: " + ConfigPlayerId); stringBuilder.AppendLine("Resolved ID: " + ResolvedID); if (localPluginScan.TamperedModNames.Count > 0) { stringBuilder.AppendLine("--- HASH MISMATCH (TAMPERED) ---"); foreach (string tamperedModName in localPluginScan.TamperedModNames) { stringBuilder.AppendLine(tamperedModName); } } if (localPluginScan.SuspiciousRuntimeAssemblies.Count > 0) { stringBuilder.AppendLine("--- SUSPICIOUS RUNTIME ASSEMBLIES ---"); foreach (string suspiciousRuntimeAssembly in localPluginScan.SuspiciousRuntimeAssemblies) { stringBuilder.AppendLine(suspiciousRuntimeAssembly); } } stringBuilder.AppendLine("--- REMOTE PLAYERS ---"); foreach (KeyValuePair remotePlayerMod in _remotePlayerMods) { stringBuilder.AppendLine($"Player {remotePlayerMod.Key}: {remotePlayerMod.Value}"); } ((TMP_Text)_debugWindowText).text = stringBuilder.ToString(); _debugWindowObj.SetActive(true); } else { _debugWindowObj.SetActive(false); } } private void CreateUI() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_01a7: 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_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Expected O, but got Unknown //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_0380: 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_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Expected O, but got Unknown //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_0466: Unknown result type (might be due to invalid IL or missing references) //IL_0494: Unknown result type (might be due to invalid IL or missing references) //IL_04c2: Unknown result type (might be due to invalid IL or missing references) //IL_04f0: Unknown result type (might be due to invalid IL or missing references) //IL_0506: Unknown result type (might be due to invalid IL or missing references) //IL_0510: Expected O, but got Unknown //IL_0574: Unknown result type (might be due to invalid IL or missing references) //IL_05ab: Unknown result type (might be due to invalid IL or missing references) //IL_05c1: Unknown result type (might be due to invalid IL or missing references) //IL_05d7: Unknown result type (might be due to invalid IL or missing references) //IL_05fc: Unknown result type (might be due to invalid IL or missing references) //IL_060d: Unknown result type (might be due to invalid IL or missing references) //IL_0617: Expected O, but got Unknown //IL_0656: Unknown result type (might be due to invalid IL or missing references) //IL_065e: Unknown result type (might be due to invalid IL or missing references) //IL_0672: Unknown result type (might be due to invalid IL or missing references) //IL_0689: Unknown result type (might be due to invalid IL or missing references) //IL_06a0: Unknown result type (might be due to invalid IL or missing references) //IL_06c7: Unknown result type (might be due to invalid IL or missing references) //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_075c: Unknown result type (might be due to invalid IL or missing references) //IL_078e: Unknown result type (might be due to invalid IL or missing references) _canvasObj = new GameObject("SBG_Canvas"); Canvas val = _canvasObj.AddComponent(); val.renderMode = (RenderMode)0; val.sortingOrder = 99999; _bgObj = new GameObject("BG"); _bgObj.transform.SetParent(_canvasObj.transform, false); _bgImage = _bgObj.AddComponent(); ((Graphic)_bgImage).color = new Color(0.04f, 0.06f, 0.08f, ConfigAlpha); _bgRect = _bgObj.GetComponent(); RectTransform bgRect = _bgRect; RectTransform bgRect2 = _bgRect; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0f); bgRect2.anchorMax = val2; bgRect.anchorMin = val2; _bgRect.pivot = new Vector2(0.5f, 0f); ContentSizeFitter val3 = _bgObj.AddComponent(); val3.horizontalFit = (FitMode)2; val3.verticalFit = (FitMode)2; HorizontalLayoutGroup val4 = _bgObj.AddComponent(); ((LayoutGroup)val4).padding = new RectOffset(14, 12, 10, 10); ((HorizontalOrVerticalLayoutGroup)val4).spacing = 10f; ((LayoutGroup)val4).childAlignment = (TextAnchor)3; ((HorizontalOrVerticalLayoutGroup)val4).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)val4).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val4).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val4).childControlHeight = true; GameObject val5 = new GameObject("Strip"); val5.transform.SetParent(_bgObj.transform, false); _rankColorStripImage = val5.AddComponent(); ((Graphic)_rankColorStripImage).color = new Color(0.5f, 0.5f, 0.5f, 1f); RectTransform component = val5.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = new Vector2(0f, 1f); component.pivot = Vector2.zero; component.anchoredPosition = Vector2.zero; component.sizeDelta = new Vector2(4f, 0f); val5.AddComponent().ignoreLayout = true; _profilePicContainer = new GameObject("PicContainer"); _profilePicContainer.transform.SetParent(_bgObj.transform, false); _profilePicContainer.AddComponent(); LayoutElement val6 = _profilePicContainer.AddComponent(); val6.preferredWidth = 52f; val6.preferredHeight = 52f; val6.flexibleWidth = 0f; val6.flexibleHeight = 0f; Image val7 = new GameObject("PH").AddComponent(); ((Component)val7).transform.SetParent(_profilePicContainer.transform, false); ((Graphic)val7).color = new Color(0.22f, 0.25f, 0.28f, 1f); ((Graphic)val7).rectTransform.anchorMin = Vector2.zero; ((Graphic)val7).rectTransform.anchorMax = Vector2.one; ((Graphic)val7).rectTransform.sizeDelta = Vector2.zero; _profileIcon = new GameObject("I").AddComponent(); ((Component)_profileIcon).transform.SetParent(_profilePicContainer.transform, false); ((Graphic)_profileIcon).rectTransform.anchorMin = Vector2.zero; ((Graphic)_profileIcon).rectTransform.anchorMax = Vector2.one; ((Graphic)_profileIcon).rectTransform.sizeDelta = Vector2.zero; ((Graphic)_profileIcon).color = new Color(1f, 1f, 1f, 0f); GameObject val8 = new GameObject("Stats"); val8.transform.SetParent(_bgObj.transform, false); val8.AddComponent().preferredWidth = 230f; VerticalLayoutGroup val9 = val8.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val9).spacing = 2f; ((LayoutGroup)val9).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)val9).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)val9).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val9).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val9).childControlHeight = true; val8.AddComponent().verticalFit = (FitMode)2; _cardNameText = MakeCardTMP(val8.transform, 17f, (FontStyles)1, Color.white, richText: false); _cardMMRText = MakeCardTMP(val8.transform, 13f, (FontStyles)0, Color.white, richText: true); _cardRankText = MakeCardTMP(val8.transform, 11f, (FontStyles)0, new Color(0.65f, 0.65f, 0.7f), richText: true); _cardSecondaryText = MakeCardTMP(val8.transform, 11f, (FontStyles)0, new Color(0.65f, 0.65f, 0.7f), richText: true); _cardSyncText = MakeCardTMP(val8.transform, 9f, (FontStyles)0, new Color(0.4f, 0.4f, 0.45f), richText: false); _flagContainer = new GameObject("Flag"); _flagContainer.transform.SetParent(_bgObj.transform, false); LayoutElement val10 = _flagContainer.AddComponent(); val10.preferredWidth = 52f; val10.preferredHeight = 52f; val10.flexibleWidth = 0f; val10.flexibleHeight = 0f; _flagIcon = new GameObject("FlagImg").AddComponent(); ((Component)_flagIcon).transform.SetParent(_flagContainer.transform, false); ((Graphic)_flagIcon).rectTransform.anchorMin = Vector2.zero; ((Graphic)_flagIcon).rectTransform.anchorMax = Vector2.one; ((Graphic)_flagIcon).rectTransform.sizeDelta = Vector2.zero; ((Graphic)_flagIcon).color = new Color(1f, 1f, 1f, 0f); _warnContainer = new GameObject("WarnContainer"); _warnContainer.transform.SetParent(_canvasObj.transform, false); RectTransform val11 = _warnContainer.AddComponent(); ((Vector2)(ref val2))..ctor(0.5f, 0f); val11.anchorMax = val2; val11.anchorMin = val2; val11.pivot = new Vector2(0.5f, 0f); val11.anchoredPosition = new Vector2(0f, 15f); val11.sizeDelta = new Vector2(1000f, 200f); VerticalLayoutGroup val12 = _warnContainer.AddComponent(); ((LayoutGroup)val12).childAlignment = (TextAnchor)7; _illegalWarningText = new GameObject("RT").AddComponent(); ((TMP_Text)_illegalWarningText).transform.SetParent(_warnContainer.transform, false); ((TMP_Text)_illegalWarningText).text = "ILLEGAL MODS DETECTED"; ((Graphic)_illegalWarningText).color = Color.red; ((TMP_Text)_illegalWarningText).fontSize = 32f; ((TMP_Text)_illegalWarningText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_illegalWarningText).fontStyle = (FontStyles)1; ((Component)_illegalWarningText).gameObject.SetActive(false); _missingWarningText = new GameObject("YT").AddComponent(); ((TMP_Text)_missingWarningText).transform.SetParent(_warnContainer.transform, false); ((Graphic)_missingWarningText).color = Color.yellow; ((TMP_Text)_missingWarningText).fontSize = 18f; ((TMP_Text)_missingWarningText).alignment = (TextAlignmentOptions)514; ((Component)_missingWarningText).gameObject.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_canvasObj); } private TextMeshProUGUI MakeCardTMP(Transform parent, float fontSize, FontStyles style, Color color, bool richText) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_002a: 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) GameObject val = new GameObject("T"); val.transform.SetParent(parent, false); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).fontSize = fontSize; ((TMP_Text)val2).fontStyle = style; ((Graphic)val2).color = color; ((TMP_Text)val2).richText = richText; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val2).overflowMode = (TextOverflowModes)0; return val2; } } [HarmonyPatch] public static class MenuTabsPatch { [HarmonyPostfix] [HarmonyPatch(typeof(MenuTabs), "Awake")] public static void Postfix(MenuTabs __instance) { SBGLTabManager.Inject(__instance); } } public static class SBGLTabManager { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func <>9__1_0; public static Func <>9__1_1; public static UnityAction <>9__5_2; internal bool b__1_0(GameObject t) { return ((Object)t).name == "SBGL"; } internal bool b__1_1(GameObject t) { return ((Object)t).name.Contains("Controls"); } internal void b__5_2() { Application.OpenURL("https://sbgleague.com/AccountSettings"); } } private const string TAB_NAME = "SBGL"; public static void Inject(MenuTabs menuTabs) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Expected O, but got Unknown //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Expected O, but got Unknown if ((Object)(object)menuTabs == (Object)null) { return; } Transform val = ((Component)menuTabs).transform.Find("Menu/Options Tab"); GameObject val2 = ((IEnumerable)menuTabs.tabs).FirstOrDefault((Func)((GameObject t) => ((Object)t).name == "SBGL")); if ((Object)(object)val2 != (Object)null) { List list = new List(); foreach (Transform item in val2.transform) { Transform val3 = item; list.Add(((Component)val3).gameObject); } list.ForEach((Action)Object.DestroyImmediate); PopulateSBGLPage(val2); return; } Transform val4 = ((val != null) ? val.Find("Controls") : null); GameObject val5 = ((IEnumerable)menuTabs.tabs).FirstOrDefault((Func)((GameObject t) => ((Object)t).name.Contains("Controls"))); if (!((Object)(object)val4 != (Object)null) || !((Object)(object)val5 != (Object)null)) { return; } ((HorizontalOrVerticalLayoutGroup)((Component)val).GetComponent()).spacing = -45f; Button[] tabButtons = menuTabs.tabButtons; foreach (Button val6 in tabButtons) { ((Component)val6).transform.localScale = new Vector3(0.8f, 0.8f, 0.8f); } GameObject val7 = Object.Instantiate(((Component)val4).gameObject, val); ((Object)val7).name = "SBGL"; SetupButtonText(val7, "SBGL"); GameObject val8 = Object.Instantiate(val5, val5.transform.parent); ((Object)val8).name = "SBGL"; if (Object.op_Implicit((Object)(object)val8.GetComponent())) { Object.DestroyImmediate((Object)(object)val8.GetComponent()); } foreach (Transform item2 in val8.transform) { Transform val9 = item2; Object.DestroyImmediate((Object)(object)((Component)val9).gameObject); } PopulateSBGLPage(val8); menuTabs.tabButtons = menuTabs.tabButtons.Append(val7.GetComponent