Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PastaClient v0.0.4
PastaClient.dll
Decompiled 10 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using PastaClient.Patches; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; 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("0Harmony")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("BepInEx")] [assembly: IgnoresAccessChecksTo("BepInEx.Harmony")] [assembly: IgnoresAccessChecksTo("LethalConfig")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("UnityEngine.IMGUIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PastaClient")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("made by Asta")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1+8ac91082f94a2d3dda6e6ee3d9e2dde8fa02fc8c")] [assembly: AssemblyProduct("PastaClient")] [assembly: AssemblyTitle("PastaClient")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PastaClient { public static class ConfigManager { public static ConfigEntry<bool> EnableAutoKick; public static ConfigEntry<int> VoteKickThreshold; public static ConfigEntry<bool> EnableShitterUIOnKick; public static ConfigEntry<string> ServerUrl; public static void Init(ConfigFile cfg) { EnableAutoKick = cfg.Bind<bool>("General", "EnableAutoKick", true, "자동킥 활성화"); VoteKickThreshold = cfg.Bind<int>("General", "VoteKickThreshold", 2, "자동킥 받을 투표 수"); EnableShitterUIOnKick = cfg.Bind<bool>("General", "EnableShitterUIOnKick", true, "킥당했을 때 UI 띄우기"); ServerUrl = cfg.Bind<string>("Server", "ServerUrl", "https://shitter.asta.rs", "서버 URL"); } } public class PastaNetwork : NetworkBehaviour { private static HashSet<ulong> _previousClients = new HashSet<ulong>(); private static HashSet<ulong> _processedConnects = new HashSet<ulong>(); private static HashSet<ulong> _processedDisconnects = new HashSet<ulong>(); public static PastaNetwork Instance { get; private set; } = null; public static void Init() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown GameObject val = new GameObject("PastaNetwork"); Instance = val.AddComponent<PastaNetwork>(); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; SteamNameFetcher.Init((MonoBehaviour)(object)Instance); ServerClient.Init((MonoBehaviour)(object)Instance); } private void Start() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PastaNetwork] Start() called"); } ((MonoBehaviour)Instance).StartCoroutine(RegisterNetworkCallbacks()); ((MonoBehaviour)Instance).StartCoroutine(MonitorPlayerChanges()); } private IEnumerator RegisterNetworkCallbacks() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PastaNetwork] Waiting for NetworkManager to initialize..."); } float timeout = 10f; float elapsed = 0f; while ((Object)(object)NetworkManager.Singleton == (Object)null && elapsed < timeout) { yield return (object)new WaitForSeconds(0.1f); elapsed += 0.1f; } if ((Object)(object)NetworkManager.Singleton == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[PastaNetwork] NetworkManager not found after timeout! Will monitor manually."); } yield break; } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[PastaNetwork] NetworkManager found! Registering callbacks..."); } try { NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected; NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnected; ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[PastaNetwork] Callbacks registered successfully!"); } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)$"[PastaNetwork] Failed to register callbacks: {ex}"); } } } private IEnumerator MonitorPlayerChanges() { yield return (object)new WaitForSeconds(2f); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PastaNetwork] 백업 플레이어 모니터링 시작... (서버/클라이언트 모두 지원)"); } if ((Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsServer) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[PastaNetwork] 클라이언트 모드: 이미 있는 플레이어들 초기 수집 중..."); } HashSet<ulong> initialPlayers = new HashSet<ulong>(); for (int attempt = 0; attempt < 5; attempt++) { yield return (object)new WaitForSeconds(1f); try { PlayerControllerB[] allPlayers = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[PastaNetwork] 초기 수집 시도 {attempt + 1}/5: PlayerControllerB {((allPlayers != null) ? allPlayers.Length : 0)}개 발견"); } if (allPlayers != null) { PlayerControllerB[] array = allPlayers; foreach (PlayerControllerB player in array) { if (!((Object)(object)player != (Object)null) || player.actualClientId == 0) { continue; } ulong steamId = SteamIDHelper.GetSteamID(player.actualClientId); if (steamId != 0L && steamId != player.actualClientId) { initialPlayers.Add(steamId); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[PastaNetwork] 초기 발견된 플레이어: clientId={player.actualClientId}, steamId={steamId}"); } } else if (steamId > 76561190000000000L && steamId < 76561199999999999L) { initialPlayers.Add(steamId); ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[PastaNetwork] 초기 발견된 플레이어 (Steam ID 형식): {steamId}"); } } } } if (attempt >= 2 && initialPlayers.Count > 0) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[PastaNetwork] 플레이어 수집 완료 (시도 {attempt + 1}): {initialPlayers.Count}명"); } break; } continue; } catch (Exception ex) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogError((object)$"[PastaNetwork] 초기 플레이어 수집 실패 (시도 {attempt + 1}): {ex.Message}"); } continue; } } if (initialPlayers.Count > 0) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)$"[PastaNetwork] 총 초기 플레이어 {initialPlayers.Count}명 발견! KickTracker에 저장 중..."); } KickTracker.SetCurrentPlayers(initialPlayers); foreach (ulong playerId in initialPlayers) { if (playerId != 0) { ((MonoBehaviour)Instance).StartCoroutine(CachePlayerNameDelayed(playerId)); } } } else { ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogWarning((object)"[PastaNetwork] 초기 플레이어를 찾지 못했습니다."); } } } while (true) { yield return (object)new WaitForSeconds(0.5f); if ((Object)(object)NetworkManager.Singleton == (Object)null) { continue; } HashSet<ulong> currentClients = new HashSet<ulong>(); if (NetworkManager.Singleton.IsServer) { try { foreach (KeyValuePair<ulong, NetworkClient> kvp in NetworkManager.Singleton.ConnectedClients) { currentClients.Add(kvp.Key); } } catch (Exception ex4) { Exception ex2 = ex4; ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogWarning((object)("[PastaNetwork] ConnectedClients 접근 오류: " + ex2.Message)); } } } try { PlayerControllerB[] allPlayers2 = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array2 = allPlayers2; foreach (PlayerControllerB player2 in array2) { if ((Object)(object)player2 != (Object)null && player2.actualClientId != 0) { ulong steamId2 = SteamIDHelper.GetSteamID(player2.actualClientId); if (steamId2 != 0) { currentClients.Add(steamId2); } } } } catch (Exception ex3) { ManualLogSource log11 = Plugin.Log; if (log11 != null) { log11.LogWarning((object)("[PastaNetwork] PlayerControllerB 찾기 오류: " + ex3.Message)); } } foreach (ulong clientId2 in _previousClients) { if (!currentClients.Contains(clientId2) && !_processedDisconnects.Contains(clientId2)) { _processedDisconnects.Add(clientId2); OnClientDisconnected(clientId2); } } foreach (ulong clientId in currentClients) { if (_previousClients.Contains(clientId) || _processedConnects.Contains(clientId) || clientId == 0) { continue; } _processedConnects.Add(clientId); ulong actualClientId = clientId; if (clientId > 76561190000000000L && clientId < 76561199999999999L && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { foreach (KeyValuePair<ulong, NetworkClient> connectedClient in NetworkManager.Singleton.ConnectedClients) { ulong connectedClientId = connectedClient.Key; ulong connectedSteamId = SteamIDHelper.GetSteamID(connectedClientId); if (connectedSteamId == clientId) { actualClientId = connectedClientId; break; } } } OnClientConnected(actualClientId); } _processedDisconnects.RemoveWhere((ulong id) => !_previousClients.Contains(id)); _processedConnects.RemoveWhere((ulong id) => !currentClients.Contains(id)); _previousClients = currentClients; } } private static void OnClientConnected(ulong clientId) { ((MonoBehaviour)Instance).StartCoroutine(CachePlayerNameDelayed(clientId)); if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer && (Object)(object)Instance != (Object)null && ((NetworkBehaviour)Instance).IsSpawned) { ((MonoBehaviour)Instance).StartCoroutine(SendPlayerListToClient(clientId)); } if ((Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsServer && clientId == NetworkManager.Singleton.LocalClientId) { ((MonoBehaviour)Instance).StartCoroutine(CollectExistingPlayers()); } if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsHost) { ((MonoBehaviour)Instance).StartCoroutine(CheckAndKickPlayerDelayed(clientId)); } } private static IEnumerator CollectExistingPlayers() { for (int attempt = 0; attempt < 5; attempt++) { yield return (object)new WaitForSeconds(1f); if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.IsServer) { yield break; } List<ulong> playerIds = new List<ulong>(); try { PlayerControllerB[] allPlayers = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array = (PlayerControllerB[])(((object)allPlayers) ?? ((object)new PlayerControllerB[0])); foreach (PlayerControllerB player in array) { if ((Object)(object)player != (Object)null && player.actualClientId != 0) { ulong steamId = SteamIDHelper.GetSteamID(player.actualClientId); if (steamId != 0) { playerIds.Add(steamId); } } } if (playerIds.Count <= 0) { continue; } KickTracker.SetCurrentPlayers(playerIds); foreach (ulong playerId in playerIds) { if (playerId != 0) { ((MonoBehaviour)Instance).StartCoroutine(CachePlayerNameDelayed(playerId)); } } yield break; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PastaNetwork] 플레이어 수집 실패 (시도 {attempt + 1}): {ex.Message}"); } } } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[PastaNetwork] 모든 시도 후에도 플레이어를 찾지 못했습니다."); } } private static IEnumerator SendPlayerListToClient(ulong clientId) { yield return (object)new WaitForSeconds(1f); if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer || (Object)(object)Instance == (Object)null || !((NetworkBehaviour)Instance).IsSpawned) { yield break; } List<ulong> playerIds = new List<ulong>(); try { foreach (KeyValuePair<ulong, NetworkClient> connectedClient in NetworkManager.Singleton.ConnectedClients) { ulong connectedClientId = connectedClient.Key; ulong steamId = SteamIDHelper.GetSteamID(connectedClientId); if (steamId != 0) { playerIds.Add(steamId); } } ClientRpcParams clientRpcParams = new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = new ulong[1] { clientId } } }; Instance.SendPlayerListClientRpc(playerIds.ToArray(), clientRpcParams); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("[PastaNetwork] 플레이어 목록 전송 실패: " + ex.Message)); } } } private static IEnumerator CachePlayerNameDelayed(ulong clientId) { yield return (object)new WaitForSeconds(1f); string playerName = GetPlayerName(clientId); if (!string.IsNullOrEmpty(playerName) && playerName != "Unknown") { RecentLeaveTracker.CachePlayerName(clientId, playerName); } } private static string GetPlayerName(ulong clientId) { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return "Unknown"; } if (NetworkManager.Singleton.IsServer && NetworkManager.Singleton.ConnectedClients.TryGetValue(clientId, out var value) && (Object)(object)value.PlayerObject != (Object)null) { string name = ((Object)value.PlayerObject).name; if (!string.IsNullOrEmpty(name) && !IsDefaultPlayerName(name)) { return name; } } PlayerControllerB[] array = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!((Object)(object)val != (Object)null) || val.actualClientId != clientId) { continue; } FieldInfo field = typeof(PlayerControllerB).GetField("playerUsername", BindingFlags.Instance | BindingFlags.Public); if (field != null) { string text = field.GetValue(val) as string; if (!string.IsNullOrEmpty(text)) { return text; } } if (!string.IsNullOrEmpty(((Object)val).name) && !IsDefaultPlayerName(((Object)val).name)) { return ((Object)val).name; } } return "Unknown"; } private static void SendChatMessage(string message) { if ((Object)(object)HUDManager.Instance == (Object)null) { return; } try { MethodInfo[] methods = typeof(HUDManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { string text = methodInfo.Name.ToLower(); if ((!text.Contains("chat") && !text.Contains("message")) || (!text.Contains("add") && !text.Contains("send"))) { continue; } try { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { methodInfo.Invoke(HUDManager.Instance, new object[1] { message }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PastaNetwork] 채팅 메시지 전송 성공: " + methodInfo.Name)); } return; } if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string)) { methodInfo.Invoke(HUDManager.Instance, new object[2] { message, "" }); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[PastaNetwork] 채팅 메시지 전송 성공: " + methodInfo.Name)); } return; } } catch { } } FieldInfo[] fields = typeof(HUDManager).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo[] array2 = fields; foreach (FieldInfo fieldInfo in array2) { string text2 = fieldInfo.Name.ToLower(); if (!text2.Contains("chat") || (!text2.Contains("text") && !text2.Contains("log"))) { continue; } try { object value = fieldInfo.GetValue(HUDManager.Instance); if (value == null) { continue; } PropertyInfo property = value.GetType().GetProperty("text"); if (property != null) { string text3 = (property.GetValue(value) as string) ?? ""; property.SetValue(value, text3 + "\n" + message); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("[PastaNetwork] 채팅 메시지 전송 성공 (필드): " + fieldInfo.Name)); } return; } } catch { } } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"[PastaNetwork] 채팅 메시지 전송 방법을 찾지 못했습니다."); } } catch (Exception arg) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)$"[PastaNetwork] 채팅 메시지 전송 오류: {arg}"); } } } private static bool IsDefaultPlayerName(string name) { if (string.IsNullOrEmpty(name)) { return true; } string text = name.ToLower().Trim(); if (text.StartsWith("player")) { string text2 = text.Substring(6).Trim(); if (string.IsNullOrEmpty(text2)) { return true; } text2 = text2.Replace("#", "").Replace(" ", ""); if (text2.Length > 0 && Regex.IsMatch(text2, "^\\d+$")) { return true; } } if (text == "player") { return true; } return false; } private static void OnClientDisconnected(ulong clientId) { ulong steamID = SteamIDHelper.GetSteamID(clientId); _processedConnects.Remove(steamID); _processedConnects.Remove(clientId); RecentLeaveTracker.Add(clientId); if (!((Object)(object)NetworkManager.Singleton != (Object)null)) { return; } List<ulong> list = new List<ulong>(); if (NetworkManager.Singleton.IsServer) { foreach (KeyValuePair<ulong, NetworkClient> connectedClient in NetworkManager.Singleton.ConnectedClients) { list.Add(connectedClient.Key); } } else { PlayerControllerB[] array = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if ((Object)(object)val != (Object)null && val.actualClientId != 0) { ulong steamID2 = SteamIDHelper.GetSteamID(val.actualClientId); if (steamID2 != 0) { list.Add(steamID2); } } } } KickTracker.SetCurrentPlayers(list); if (!NetworkManager.Singleton.IsServer && clientId == NetworkManager.Singleton.LocalClientId) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PastaNetwork] 킥당한 것으로 감지! UI 표시 중..."); } ((MonoBehaviour)Instance).StartCoroutine(ShowKickUIDelayed()); } } private static IEnumerator ShowKickUIDelayed() { yield return (object)new WaitForSeconds(0.5f); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PastaNetwork] 킥 UI 표시 중..."); } KickUI.ShowKickedBy(); } private static IEnumerator CheckAndKickPlayerDelayed(ulong clientId) { yield return (object)new WaitForSeconds(1.5f); if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost) { yield break; } ulong steamId = SteamIDHelper.GetSteamID(clientId); if (steamId == clientId && clientId < 76561190000000000L) { yield return (object)new WaitForSeconds(1f); steamId = SteamIDHelper.GetSteamID(clientId); } ServerClient.GetShitterInfo(steamId, delegate(bool success, Dictionary<string, object>? infoData) { if (success && infoData != null && infoData.ContainsKey("voteCount")) { int val = (int)infoData["voteCount"]; if (infoData.ContainsKey("fullJson")) { string input = infoData["fullJson"].ToString() ?? ""; string pattern = "\"votes\"\\s*:\\s*\\{([^}]+(?:\\{[^}]+\\}[^}]*)*)\\}"; Match match = Regex.Match(input, pattern); if (match.Success) { string value = match.Groups[1].Value; string pattern2 = "\"(\\d+)\"\\s*:\\s*\\{[^}]*\"reason\"\\s*:\\s*\"([^\"]+)\""; MatchCollection matchCollection = Regex.Matches(value, pattern2); foreach (Match item in matchCollection) { if (item.Groups.Count >= 3 && ulong.TryParse(item.Groups[1].Value, out var result)) { string value2 = item.Groups[2].Value; value2 = value2.Replace("\\\"", "\"").Replace("\\n", "\n").Replace("\\t", "\t"); ShitterDatabase.AddVote(steamId, result, value2); } } } } int voteCount = ShitterDatabase.GetVoteCount(steamId); int num = Math.Max(val, voteCount); if (ConfigManager.EnableAutoKick.Value && num >= ConfigManager.VoteKickThreshold.Value) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[PastaNetwork] 자동 킥: Steam ID {steamId}, 투표 {num}개"); } if ((Object)(object)Instance == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)"[PastaNetwork] Instance is null! Cannot start kick coroutine."); } } else { ((MonoBehaviour)Instance).StartCoroutine(KickPlayerDelayed(clientId, steamId, num)); } } } else if (ShitterDatabase.IsShitter(steamId)) { int voteCount2 = ShitterDatabase.GetVoteCount(steamId); if (ConfigManager.EnableAutoKick.Value && voteCount2 >= ConfigManager.VoteKickThreshold.Value) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[PastaNetwork] 자동 킥 (로컬 DB): Steam ID {steamId}, 투표 {voteCount2}개"); } ((MonoBehaviour)Instance).StartCoroutine(KickPlayerDelayed(clientId, steamId, voteCount2)); } } }); } private static IEnumerator KickPlayerDelayed(ulong clientId, ulong steamId, int votes) { yield return (object)new WaitForSeconds(1f); if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost) { yield break; } ulong actualClientId = clientId; if (clientId > 76561190000000000L && clientId < 76561199999999999L) { actualClientId = SteamIDHelper.GetClientID(steamId); if (actualClientId == steamId || actualClientId == clientId) { foreach (KeyValuePair<ulong, NetworkClient> connectedClient in NetworkManager.Singleton.ConnectedClients) { ulong connectedClientId = connectedClient.Key; ulong connectedSteamId = SteamIDHelper.GetSteamID(connectedClientId); if (connectedSteamId == steamId) { actualClientId = connectedClientId; break; } } } } if (!NetworkManager.Singleton.ConnectedClients.ContainsKey(actualClientId)) { yield break; } try { ShitterInfo shitterInfo = ShitterDatabase.Get(steamId); string playerName = SteamNameFetcher.GetCachedName(steamId); if (playerName == "Unknown") { playerName = GetPlayerName(actualClientId); if (playerName == "Unknown") { playerName = $"플레이어 ({steamId})"; } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"═══════════════════════════════════════════════════════"); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"\ud83d\udeab 자동 킥 실행: {playerName} (Steam ID: {steamId})"); } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"\ud83d\udcca 총 투표 수: {votes}개 (임계값: {ConfigManager.VoteKickThreshold.Value}개)"); } if (shitterInfo != null && shitterInfo.Reasons.Count > 0) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"───────────────────────────────────────────────────────"); } ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)"\ud83d\udcdd 투표 사유:"); } int index = 1; foreach (KeyValuePair<ulong, string> kvp in shitterInfo.Reasons) { ulong voterSteamId = kvp.Key; string reason = kvp.Value; string voterName = SteamNameFetcher.GetCachedName(voterSteamId); if (voterName == "Unknown") { voterName = $"플레이어 ({voterSteamId})"; } ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$" {index}. [{voterName}] {reason}"); } index++; } } else { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)"───────────────────────────────────────────────────────"); } ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)"⚠\ufe0f 투표 사유 정보를 가져올 수 없습니다. (로컬 DB에 없음)"); } } ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)"═══════════════════════════════════════════════════════"); } NetworkManager.Singleton.DisconnectClient(actualClientId); _processedConnects.Remove(steamId); _processedConnects.Remove(actualClientId); if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip("Pasta Client", $"자동킥됨 (Shitter, 투표: {votes})", false, false, "LC_Tip1"); SendChatMessage($"{playerName}이(가) Shitter 투표 {votes}개로 인해 자동 킥되었습니다."); } } catch (Exception ex) { ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogError((object)$"[PastaNetwork] 킥 실행 중 오류: {ex}"); } } } [ServerRpc(RequireOwnership = false)] public void AddShitterServerRpc(ulong targetId, string reason, ulong senderId) { ShitterDatabase.AddVote(targetId, senderId, reason); UpdateShitterClientsClientRpc(targetId); if (!((NetworkBehaviour)this).IsHost || !((Object)(object)NetworkManager.Singleton != (Object)null)) { return; } int voteCount = ShitterDatabase.GetVoteCount(targetId); if (!ConfigManager.EnableAutoKick.Value || voteCount < ConfigManager.VoteKickThreshold.Value) { return; } ulong clientID = SteamIDHelper.GetClientID(targetId); if (NetworkManager.Singleton.ConnectedClients.ContainsKey(clientID)) { ((MonoBehaviour)Instance).StartCoroutine(KickPlayerDelayed(clientID, targetId, voteCount)); return; } foreach (KeyValuePair<ulong, NetworkClient> connectedClient in NetworkManager.Singleton.ConnectedClients) { ulong key = connectedClient.Key; ulong steamID = SteamIDHelper.GetSteamID(key); if (steamID == targetId) { ((MonoBehaviour)Instance).StartCoroutine(KickPlayerDelayed(key, targetId, voteCount)); break; } } } [ClientRpc] public void UpdateShitterClientsClientRpc(ulong id) { Plugin.Log.LogDebug((object)$"[PastaNetwork] UpdateShitterClientsClientRpc: {id}"); ShitterUI.OnDatabaseUpdated(id); } [ClientRpc] public void SendPlayerListClientRpc(ulong[] playerIds, ClientRpcParams rpcParams = default(ClientRpcParams)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[PastaNetwork] 방 플레이어 목록 수신: {playerIds.Length}명"); } KickTracker.SetCurrentPlayers(playerIds); foreach (ulong num in playerIds) { if (num != 0) { ((MonoBehaviour)Instance).StartCoroutine(CachePlayerNameDelayed(num)); } } } public static void RequestAddShitter(ulong targetId, string reason) { string reason2 = reason; if ((Object)(object)Instance == (Object)null) { return; } ulong targetSteamId = SteamIDHelper.GetSteamID(targetId); ulong senderSteamId = 0uL; if ((Object)(object)NetworkManager.Singleton != (Object)null) { senderSteamId = SteamIDHelper.GetSteamID(NetworkManager.Singleton.LocalClientId); } ServerClient.SendShitterVote(targetSteamId, senderSteamId, reason2, delegate(bool success, string response) { if (success) { try { if (response.Contains("\"voteCount\"")) { Match match = Regex.Match(response, "\"voteCount\"\\s*:\\s*(\\d+)"); if (match.Success && int.TryParse(match.Groups[1].Value, out var result) && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer && NetworkManager.Singleton.IsHost && ConfigManager.EnableAutoKick.Value && result >= ConfigManager.VoteKickThreshold.Value) { ulong clientID = SteamIDHelper.GetClientID(targetSteamId); if (NetworkManager.Singleton.ConnectedClients.ContainsKey(clientID)) { ((MonoBehaviour)Instance).StartCoroutine(KickPlayerDelayed(clientID, targetSteamId, result)); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[PastaNetwork] 서버 응답 파싱 오류: " + ex.Message)); } } HUDManager instance = HUDManager.Instance; if (instance != null) { instance.DisplayTip("Pasta Client", "Shitter 추가 요청 전송됨", false, false, "LC_Tip1"); } ShitterDatabase.AddVote(targetSteamId, senderSteamId, reason2); ShitterUI.OnDatabaseUpdated(targetSteamId); if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer && (Object)(object)Instance != (Object)null) { try { ulong localClientId = NetworkManager.Singleton.LocalClientId; Instance.AddShitterServerRpc(targetSteamId, reason2, senderSteamId); } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[PastaNetwork] Failed to call AddShitterServerRpc: " + ex2.Message)); } } } } else { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("[PastaNetwork] Failed to send shitter vote to server: " + response)); } HUDManager instance2 = HUDManager.Instance; if (instance2 != null) { instance2.DisplayTip("Pasta Client", "서버 요청 실패", false, false, "LC_Tip1"); } } }); } } [BepInPlugin("dev523.pasta.client", "Pasta Client", "1.0.0")] public class Plugin : BaseUnityPlugin { public static ManualLogSource Log; private void Awake() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Pasta Client Loaded!"); ConfigManager.Init(((BaseUnityPlugin)this).Config); Harmony val = new Harmony("dev523.pasta.client"); val.PatchAll(); PastaNetwork.Init(); ShitterDatabase.Init(); RecentLeaveTracker.Init((MonoBehaviour?)(object)PastaNetwork.Instance); KickTracker.Init(); ChatCommandHandler.Init(); Object.DontDestroyOnLoad((Object)(object)this); } } public static class KickTracker { public static List<ulong> LastPlayersInRoom = new List<ulong>(); public static void Init() { } public static void SetCurrentPlayers(IEnumerable<ulong> ids) { LastPlayersInRoom = new List<ulong>(ids); } } public class RecentPlayerInfo { public ulong ClientId; public string PlayerName; public bool IsNameLoading; public RecentPlayerInfo(ulong clientId, string playerName, bool isNameLoading = false) { ClientId = clientId; PlayerName = playerName ?? "Unknown"; IsNameLoading = isNameLoading; } } public static class RecentLeaveTracker { private static readonly LinkedList<RecentPlayerInfo> _recent = new LinkedList<RecentPlayerInfo>(); private static readonly Dictionary<ulong, string> _playerNameCache = new Dictionary<ulong, string>(); private const int Max = 10; private static MonoBehaviour? _coroutineRunner; public static void Init(MonoBehaviour? coroutineRunner = null) { _coroutineRunner = coroutineRunner; } public static void Add(ulong clientId) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[RecentLeaveTracker] ========================================"); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[RecentLeaveTracker] 나간 플레이어 리스트에 추가 중..."); } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[RecentLeaveTracker] Steam ID: {clientId}"); } string value; string text = (_playerNameCache.TryGetValue(clientId, out value) ? value : "Unknown"); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("[RecentLeaveTracker] 캐시에서 가져온 이름: " + text)); } if (text == "Unknown") { text = GetPlayerNameFromGame(clientId); ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)("[RecentLeaveTracker] 게임에서 가져온 이름: " + text)); } if (text != "Unknown" && !string.IsNullOrEmpty(text)) { _playerNameCache[clientId] = text; } } bool flag = text == "Unknown"; ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)("[RecentLeaveTracker] 현재 플레이어 이름: " + text)); } ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)$"[RecentLeaveTracker] 이름 로딩 중: {flag}"); } RecentPlayerInfo value2 = new RecentPlayerInfo(clientId, text, flag); _recent.AddFirst(value2); while (_recent.Count > 10) { _recent.RemoveLast(); } ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)$"[RecentLeaveTracker] 리스트에 추가 완료! (총 {_recent.Count}명)"); } if (flag && (Object)(object)_coroutineRunner != (Object)null) { ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)"[RecentLeaveTracker] Steam API로 이름 가져오기 시작..."); } SteamNameFetcher.FetchPlayerNameFromGameOrSteam(clientId, delegate(ulong steamId, string name) { ManualLogSource log15 = Plugin.Log; if (log15 != null) { log15.LogInfo((object)"[RecentLeaveTracker] ========================================"); } ManualLogSource log16 = Plugin.Log; if (log16 != null) { log16.LogInfo((object)"[RecentLeaveTracker] Steam API 이름 가져오기 완료!"); } ManualLogSource log17 = Plugin.Log; if (log17 != null) { log17.LogInfo((object)$"[RecentLeaveTracker] Steam ID: {steamId}"); } ManualLogSource log18 = Plugin.Log; if (log18 != null) { log18.LogInfo((object)("[RecentLeaveTracker] 플레이어 이름: " + name)); } ManualLogSource log19 = Plugin.Log; if (log19 != null) { log19.LogInfo((object)"[RecentLeaveTracker] ========================================"); } UpdatePlayerName(steamId, name); }); } else if (!flag) { ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogInfo((object)"[RecentLeaveTracker] ========================================"); } ManualLogSource log11 = Plugin.Log; if (log11 != null) { log11.LogInfo((object)"[RecentLeaveTracker] 나간 플레이어 정보:"); } ManualLogSource log12 = Plugin.Log; if (log12 != null) { log12.LogInfo((object)$"[RecentLeaveTracker] Steam ID: {clientId}"); } ManualLogSource log13 = Plugin.Log; if (log13 != null) { log13.LogInfo((object)("[RecentLeaveTracker] 플레이어 이름: " + text)); } ManualLogSource log14 = Plugin.Log; if (log14 != null) { log14.LogInfo((object)"[RecentLeaveTracker] ========================================"); } } } public static void CachePlayerName(ulong clientId, string playerName) { if (!string.IsNullOrEmpty(playerName) && playerName != "Unknown") { _playerNameCache[clientId] = playerName; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[RecentLeaveTracker] Cached player name: {clientId} = {playerName}"); } } if (!((Object)(object)_coroutineRunner != (Object)null)) { return; } SteamNameFetcher.FetchPlayerNameFromGameOrSteam(clientId, delegate(ulong steamId, string name) { if (!string.IsNullOrEmpty(name) && name != "Unknown") { _playerNameCache[steamId] = name; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[RecentLeaveTracker] Updated cached name from Steam: {steamId} = {name}"); } } }); } private static void UpdatePlayerName(ulong clientId, string playerName) { foreach (RecentPlayerInfo item in _recent) { if (item.ClientId == clientId) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[RecentLeaveTracker] 리스트에서 이름 업데이트: Steam ID {clientId}, 이전 이름: {item.PlayerName} -> 새 이름: {playerName}"); } item.PlayerName = playerName; item.IsNameLoading = false; if (!string.IsNullOrEmpty(playerName) && playerName != "Unknown") { _playerNameCache[clientId] = playerName; } break; } } } private static string GetPlayerNameFromGame(ulong clientId) { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return "Unknown"; } if (NetworkManager.Singleton.IsServer && NetworkManager.Singleton.ConnectedClients.TryGetValue(clientId, out var value) && (Object)(object)value.PlayerObject != (Object)null) { string name = ((Object)value.PlayerObject).name; if (!string.IsNullOrEmpty(name) && name != "Player") { return name; } } PlayerControllerB[] array = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!((Object)(object)val != (Object)null) || val.actualClientId != clientId) { continue; } FieldInfo field = typeof(PlayerControllerB).GetField("playerUsername", BindingFlags.Instance | BindingFlags.Public); if (field != null) { string text = field.GetValue(val) as string; if (!string.IsNullOrEmpty(text)) { return text; } } if (!string.IsNullOrEmpty(((Object)val).name) && ((Object)val).name != "Player") { return ((Object)val).name; } } return "Unknown"; } public static IEnumerable<RecentPlayerInfo> GetAll() { return _recent; } public static IEnumerable<ulong> GetAllIds() { return _recent.Select((RecentPlayerInfo p) => p.ClientId); } } public class ShitterInfo { public ulong SteamId; public Dictionary<ulong, string> Reasons = new Dictionary<ulong, string>(); public ShitterInfo(ulong id) { SteamId = id; } } public static class ShitterDatabase { private static readonly Dictionary<ulong, ShitterInfo> _shitters = new Dictionary<ulong, ShitterInfo>(); public static void Init() { } public static void AddVote(ulong targetId, ulong voterId, string reason) { if (targetId != voterId) { if (!_shitters.TryGetValue(targetId, out ShitterInfo value)) { value = new ShitterInfo(targetId); _shitters[targetId] = value; } if (!value.Reasons.ContainsKey(voterId)) { value.Reasons[voterId] = reason; } } } public static bool IsShitter(ulong id) { return _shitters.ContainsKey(id); } public static int GetVoteCount(ulong id) { ShitterInfo value; return _shitters.TryGetValue(id, out value) ? value.Reasons.Count : 0; } public static IEnumerable<ShitterInfo> All() { return _shitters.Values; } public static ShitterInfo? Get(ulong id) { ShitterInfo value; return _shitters.TryGetValue(id, out value) ? value : null; } } public static class KickUI { [CompilerGenerated] private static class <>O { public static Action <0>__OnGUI; public static WindowFunction <1>__WindowFunc; } private static bool _open; private static Rect _rect = new Rect(140f, 140f, 400f, 400f); public static void ShowKickedBy() { if (ConfigManager.EnableShitterUIOnKick.Value) { _open = true; UIHelper.Register(OnGUI); } } private static void Close() { _open = false; UIHelper.Unregister(OnGUI); } private static void OnGUI() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown if (!_open) { return; } try { if (((Rect)(ref _rect)).x == 140f && ((Rect)(ref _rect)).y == 140f) { ((Rect)(ref _rect)).x = ((float)Screen.width - ((Rect)(ref _rect)).width) / 2f; ((Rect)(ref _rect)).y = ((float)Screen.height - ((Rect)(ref _rect)).height) / 2f; } Rect rect = _rect; object obj = <>O.<1>__WindowFunc; if (obj == null) { WindowFunction val = WindowFunc; <>O.<1>__WindowFunc = val; obj = (object)val; } _rect = GUILayout.Window(987656, rect, (WindowFunction)obj, "킥당함 - 방 목록", UIStyle.WindowStyle, Array.Empty<GUILayoutOption>()); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[KickUI] Error in OnGUI: {arg}"); } } } private static void WindowFunc(int id) { //IL_007f: 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_022b: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("킥 당했을 때 방에 있던 플레이어 목록입니다.", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Box("", UIStyle.BoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(1f), GUILayout.ExpandWidth(true) }); GUILayout.Space(10f); List<ulong> lastPlayersInRoom = KickTracker.LastPlayersInRoom; if (lastPlayersInRoom != null && lastPlayersInRoom.Count > 0) { GUILayout.BeginScrollView(Vector2.zero, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); foreach (ulong item in lastPlayersInRoom) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label($"[{item}]", UIStyle.LabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); if (GUILayout.Button("Steam 프로필", UIStyle.ButtonStyle, Array.Empty<GUILayoutOption>())) { ulong steamID = SteamIDHelper.GetSteamID(item); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[KickUI] Opening Steam profile: clientId={item}, steamId={steamID}"); } Application.OpenURL($"https://steamcommunity.com/profiles/{steamID}"); } GUILayout.EndHorizontal(); GUILayout.Space(5f); } GUILayout.EndScrollView(); } else { GUILayout.Label("목록이 비어있습니다.", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); } GUILayout.Space(10f); GUILayout.Box("", UIStyle.BoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(1f), GUILayout.ExpandWidth(true) }); GUILayout.Space(5f); if (GUILayout.Button("닫기", UIStyle.ButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { Close(); } GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _rect)).width, 20f)); } } public static class RecentUI { [CompilerGenerated] private static class <>O { public static Action <0>__OnGUI; public static WindowFunction <1>__WindowFunc; } private static bool _open; private static Rect _rect = new Rect(120f, 120f, 450f, 500f); public static void Open() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[RecentUI] Open() called"); } _open = true; UIHelper.Register(OnGUI); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[RecentUI] UI registered, _open = {_open}"); } } public static void Close() { _open = false; UIHelper.Unregister(OnGUI); } private static void OnGUI() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown if (!_open) { return; } try { if (((Rect)(ref _rect)).x == 120f && ((Rect)(ref _rect)).y == 120f) { ((Rect)(ref _rect)).x = ((float)Screen.width - ((Rect)(ref _rect)).width) / 2f; ((Rect)(ref _rect)).y = ((float)Screen.height - ((Rect)(ref _rect)).height) / 2f; } Rect rect = _rect; object obj = <>O.<1>__WindowFunc; if (obj == null) { WindowFunction val = WindowFunc; <>O.<1>__WindowFunc = val; obj = (object)val; } _rect = GUILayout.Window(987655, rect, (WindowFunction)obj, "최근 나간 사람들", UIStyle.WindowStyle, Array.Empty<GUILayoutOption>()); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[RecentUI] Error in OnGUI: {arg}"); } } } private static void WindowFunc(int id) { //IL_007e: 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_0252: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("최근에 방을 나간 플레이어 목록입니다.", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Box("", UIStyle.BoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(1f), GUILayout.ExpandWidth(true) }); GUILayout.Space(10f); List<RecentPlayerInfo> list = RecentLeaveTracker.GetAll().ToList(); if (list.Count > 0) { GUILayout.BeginScrollView(Vector2.zero, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); foreach (RecentPlayerInfo item in list) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); string arg = (item.IsNameLoading ? "Loading..." : item.PlayerName); GUILayout.Label($"[{item.ClientId}] {arg}", UIStyle.LabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); if (GUILayout.Button("Steam 프로필", UIStyle.ButtonStyle, Array.Empty<GUILayoutOption>())) { ulong steamID = SteamIDHelper.GetSteamID(item.ClientId); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[RecentUI] Opening Steam profile: clientId={item.ClientId}, steamId={steamID}"); } Application.OpenURL($"https://steamcommunity.com/profiles/{steamID}"); } GUILayout.EndHorizontal(); GUILayout.Space(5f); } GUILayout.EndScrollView(); } else { GUILayout.Label("목록이 비어있습니다.", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); } GUILayout.Space(10f); GUILayout.Box("", UIStyle.BoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(1f), GUILayout.ExpandWidth(true) }); GUILayout.Space(5f); if (GUILayout.Button("닫기", UIStyle.ButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { Close(); } GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _rect)).width, 20f)); } } [Serializable] public class ShitterVoteRequest { public string targetId = ""; public string senderId = ""; public string reason = ""; public long timestamp; } public static class ServerClient { private static MonoBehaviour? _coroutineRunner; private const string DEFAULT_SERVER_URL = "https://shitter.asta.rs"; public static void Init(MonoBehaviour coroutineRunner) { _coroutineRunner = coroutineRunner; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[ServerClient] Initialized"); } } public static void SendShitterVote(ulong targetId, ulong senderId, string reason, Action<bool, string>? onComplete = null) { if ((Object)(object)_coroutineRunner == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"[ServerClient] CoroutineRunner not initialized!"); } onComplete?.Invoke(arg1: false, "CoroutineRunner not initialized"); return; } string text = ConfigManager.ServerUrl?.Value ?? "https://shitter.asta.rs"; string url = text + "/api/shitter/vote"; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[ServerClient] Sending shitter vote: targetId={targetId}, senderId={senderId}, reason={reason}"); } _coroutineRunner.StartCoroutine(SendVoteCoroutine(url, targetId, senderId, reason, onComplete)); } private static IEnumerator SendVoteCoroutine(string url, ulong targetId, ulong senderId, string reason, Action<bool, string>? onComplete) { ShitterVoteRequest jsonData = new ShitterVoteRequest { targetId = targetId.ToString(), senderId = senderId.ToString(), reason = reason, timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds }; string json = JsonUtility.ToJson((object)jsonData); byte[] bodyRaw = Encoding.UTF8.GetBytes(json); UnityWebRequest request = new UnityWebRequest(url, "POST"); try { request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("User-Agent", "PastaClient/1.0"); yield return request.SendWebRequest(); if ((int)request.result == 1) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[ServerClient] Vote sent successfully: " + request.downloadHandler.text)); } onComplete?.Invoke(arg1: true, request.downloadHandler.text); } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[ServerClient] Failed to send vote: " + request.error)); } onComplete?.Invoke(arg1: false, request.error ?? "Unknown error"); } } finally { ((IDisposable)request)?.Dispose(); } } public static void GetShitterInfo(ulong targetId, Action<bool, Dictionary<string, object>?>? onComplete = null) { if ((Object)(object)_coroutineRunner == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"[ServerClient] CoroutineRunner not initialized!"); } onComplete?.Invoke(arg1: false, null); return; } string arg = ConfigManager.ServerUrl?.Value ?? "https://shitter.asta.rs"; string url = $"{arg}/api/shitter/info/{targetId}"; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[ServerClient] Getting shitter info: targetId={targetId}"); } _coroutineRunner.StartCoroutine(GetInfoCoroutine(url, onComplete)); } private static IEnumerator GetInfoCoroutine(string url, Action<bool, Dictionary<string, object>?>? onComplete) { UnityWebRequest request = UnityWebRequest.Get(url); try { request.SetRequestHeader("User-Agent", "PastaClient/1.0"); yield return request.SendWebRequest(); if ((int)request.result == 1) { try { string json = request.downloadHandler.text; Dictionary<string, object> result = new Dictionary<string, object>(); Match voteCountMatch = Regex.Match(json, "\"voteCount\"\\s*:\\s*(\\d+)"); if (voteCountMatch.Success && int.TryParse(voteCountMatch.Groups[1].Value, out var voteCount)) { result["voteCount"] = voteCount; } else { result["voteCount"] = 0; } Match steamIdMatch = Regex.Match(json, "\"steamId\"\\s*:\\s*\"?(\\d+)\"?"); if (steamIdMatch.Success && ulong.TryParse(steamIdMatch.Groups[1].Value, out var steamId)) { result["steamId"] = steamId; } if (json.Contains("\"votes\"")) { result["hasVotes"] = true; result["fullJson"] = json; } onComplete?.Invoke(arg1: true, result); } catch (Exception ex2) { Exception ex = ex2; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[ServerClient] Error parsing response: {ex}"); } onComplete?.Invoke(arg1: false, null); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[ServerClient] Failed to get info: " + request.error)); } onComplete?.Invoke(arg1: false, null); } } finally { ((IDisposable)request)?.Dispose(); } } } public static class ShitterUI { [CompilerGenerated] private static class <>O { public static Action <0>__OnGUI; public static WindowFunction <1>__WindowFunc; } private static bool _open = false; private static Rect _windowRect = new Rect(100f, 100f, 500f, 550f); private static Vector2 _scroll = Vector2.zero; private static ulong _selectedId = 0uL; private static string _reason = ""; private const string REASON_TEXTAREA_ID = "ShitterReasonTextArea"; public static void Open() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[ShitterUI] Open() called"); } _open = true; UIHelper.Register(OnGUI); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[ShitterUI] UI registered, _open = {_open}"); } } public static void Close() { _open = false; UIHelper.Unregister(OnGUI); } public static void OnDatabaseUpdated(ulong id) { } private static void OnGUI() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown if (!_open) { return; } try { if (((Rect)(ref _windowRect)).x == 100f && ((Rect)(ref _windowRect)).y == 100f) { ((Rect)(ref _windowRect)).x = ((float)Screen.width - ((Rect)(ref _windowRect)).width) / 2f; ((Rect)(ref _windowRect)).y = ((float)Screen.height - ((Rect)(ref _windowRect)).height) / 2f; } Rect windowRect = _windowRect; object obj = <>O.<1>__WindowFunc; if (obj == null) { WindowFunction val = WindowFunc; <>O.<1>__WindowFunc = val; obj = (object)val; } _windowRect = GUILayout.Window(987654, windowRect, (WindowFunction)obj, "Shitter 추가/확인", UIStyle.WindowStyle, Array.Empty<GUILayoutOption>()); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[ShitterUI] Error in OnGUI: {arg}"); } } } private static void WindowFunc(int id) { //IL_0600: 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_0070: 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) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); try { GUILayout.Label("현재 방 플레이어:", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Box("", UIStyle.BoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(1f), GUILayout.ExpandWidth(true) }); _scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) }); try { Dictionary<ulong, string> dictionary = new Dictionary<ulong, string>(); PlayerControllerB[] array = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); if (array != null && array.Length != 0) { PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if ((Object)(object)val != (Object)null && val.actualClientId != 0) { ulong actualClientId = val.actualClientId; ulong steamIDFromPlayer = GetSteamIDFromPlayer(val, actualClientId); string text = GetPlayerNameForUI(val, steamIDFromPlayer); if (IsDefaultPlayerName(text)) { text = "Unknown"; } if (!dictionary.ContainsKey(steamIDFromPlayer)) { dictionary[steamIDFromPlayer] = text; } } } } foreach (ulong item in KickTracker.LastPlayersInRoom) { if (item != 0L && !dictionary.ContainsKey(item)) { string text2 = SteamNameFetcher.GetCachedName(item); if (text2 == "Unknown") { SteamNameFetcher.FetchPlayerNameFromGameOrSteam(item, delegate { }); text2 = "Loading..."; } if (IsDefaultPlayerName(text2)) { text2 = "Loading..."; } dictionary[item] = text2; } } if (dictionary.Count > 0) { foreach (KeyValuePair<ulong, string> item2 in dictionary) { string text3 = $"[{item2.Key}] {item2.Value}"; if (GUILayout.Button(text3, UIStyle.ButtonStyle, Array.Empty<GUILayoutOption>())) { _selectedId = item2.Key; } } } else { GUILayout.Label("플레이어 없음", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[ShitterUI] Error getting players: {arg}"); } GUILayout.Label("오류 발생", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); } finally { GUILayout.EndScrollView(); } GUILayout.Space(10f); GUILayout.Label("최근 나간 플레이어:", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Box("", UIStyle.BoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(1f), GUILayout.ExpandWidth(true) }); List<RecentPlayerInfo> list = RecentLeaveTracker.GetAll().ToList(); if (list.Count > 0) { foreach (RecentPlayerInfo item3 in list) { string text4 = $"[{item3.ClientId}] {item3.PlayerName}"; if (GUILayout.Button(text4, UIStyle.ButtonStyle, Array.Empty<GUILayoutOption>())) { _selectedId = item3.ClientId; } } } else { GUILayout.Label("없음", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); } GUILayout.Space(10f); GUILayout.Box("", UIStyle.BoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(1f), GUILayout.ExpandWidth(true) }); GUILayout.Space(5f); if (_selectedId != 0) { GUILayout.Label($"선택된 플레이어: {_selectedId}", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); } else { GUILayout.Label("플레이어를 선택하세요", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); } GUILayout.Space(5f); GUILayout.Label("이유 입력:", UIStyle.LabelStyle, Array.Empty<GUILayoutOption>()); GUI.SetNextControlName("ShitterReasonTextArea"); _reason = GUILayout.TextArea(_reason, 500, UIStyle.TextFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(120f), GUILayout.ExpandWidth(true) }); string nameOfFocusedControl = GUI.GetNameOfFocusedControl(); if (nameOfFocusedControl == "ShitterReasonTextArea") { UIHelper.SetTextInputActive(active: true); } GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("추가 요청", UIStyle.ButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }) && _selectedId != 0L && !string.IsNullOrWhiteSpace(_reason)) { PastaNetwork.RequestAddShitter(_selectedId, _reason); HUDManager instance = HUDManager.Instance; if (instance != null) { instance.DisplayTip("Pasta Client", "Shitter 추가 요청 전송됨", false, false, "LC_Tip1"); } _reason = ""; _selectedId = 0uL; } if (GUILayout.Button("닫기", UIStyle.ButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { Close(); } GUILayout.EndHorizontal(); } catch (Exception arg2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)$"[ShitterUI] Error in WindowFunc: {arg2}"); } } finally { GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 20f)); } } private static ulong GetSteamIDFromPlayer(PlayerControllerB player, ulong fallbackClientId) { if ((Object)(object)player == (Object)null) { return fallbackClientId; } try { string[] array = new string[6] { "steamId", "playerSteamId", "ownerClientId", "steamID", "SteamID", "PlayerSteamID" }; string[] array2 = array; foreach (string name in array2) { FieldInfo field = typeof(PlayerControllerB).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null)) { continue; } object value = field.GetValue(player); if (value != null) { if (value is ulong num && num != 0) { return num; } if (value is long num2 && num2 > 0) { return (ulong)num2; } } } NetworkObject component = ((Component)player).GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null) { ulong ownerClientId = component.OwnerClientId; if (ownerClientId > 76561190000000000L && ownerClientId < 76561199999999999L) { return ownerClientId; } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[ShitterUI] Error getting Steam ID from PlayerControllerB: " + ex.Message)); } } if (fallbackClientId > 76561190000000000L && fallbackClientId < 76561199999999999L) { return fallbackClientId; } return fallbackClientId; } private static string GetPlayerNameForUI(PlayerControllerB player, ulong steamId) { if ((Object)(object)player == (Object)null) { return "Unknown"; } string text = "Unknown"; FieldInfo field = typeof(PlayerControllerB).GetField("playerUsername", BindingFlags.Instance | BindingFlags.Public); if (field != null) { string text2 = field.GetValue(player) as string; if (!string.IsNullOrEmpty(text2)) { text = text2; } } if (text == "Unknown" && !string.IsNullOrEmpty(((Object)player).name)) { text = ((Object)player).name; } if (text == "Unknown") { text = SteamNameFetcher.GetCachedName(steamId); if (text == "Unknown") { SteamNameFetcher.FetchPlayerNameFromGameOrSteam(steamId, delegate { }); text = "Loading..."; } } return text; } private static bool IsDefaultPlayerName(string name) { if (string.IsNullOrEmpty(name) || name == "Unknown" || name == "Loading...") { return false; } string text = name.ToLower().Trim(); if (text.StartsWith("player")) { string text2 = text.Substring(6).Trim(); if (string.IsNullOrEmpty(text2)) { return true; } text2 = text2.Replace("#", "").Replace(" ", ""); if (text2.Length > 0 && Regex.IsMatch(text2, "^\\d+$")) { return true; } } if (text == "player") { return true; } return false; } } public static class SteamIDHelper { public static ulong GetSteamID(ulong clientId) { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { try { if (NetworkManager.Singleton.ConnectedClients.TryGetValue(clientId, out var value)) { try { if ((Object)(object)value.PlayerObject != (Object)null) { PlayerControllerB component = ((Component)value.PlayerObject).GetComponent<PlayerControllerB>(); if ((Object)(object)component != (Object)null) { ulong steamIDFromPlayer = GetSteamIDFromPlayer(component); if (steamIDFromPlayer != 0L && steamIDFromPlayer != clientId) { return steamIDFromPlayer; } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[SteamIDHelper] Error getting Steam ID from NetworkClient: " + ex.Message)); } } } } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[SteamIDHelper] Error accessing ConnectedClients (may be client): " + ex2.Message)); } } } try { PlayerControllerB[] array = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if ((Object)(object)val != (Object)null && val.actualClientId == clientId) { ulong steamIDFromPlayer2 = GetSteamIDFromPlayer(val); if (steamIDFromPlayer2 != 0) { return steamIDFromPlayer2; } } } } catch (Exception ex3) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[SteamIDHelper] Error searching PlayerControllerB: " + ex3.Message)); } } if (clientId > 76561190000000000L && clientId < 76561199999999999L) { return clientId; } return clientId; } private static ulong GetSteamIDFromPlayer(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return 0uL; } try { string[] array = new string[6] { "steamId", "playerSteamId", "ownerClientId", "steamID", "SteamID", "PlayerSteamID" }; string[] array2 = array; foreach (string name in array2) { FieldInfo field = typeof(PlayerControllerB).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null)) { continue; } object value = field.GetValue(player); if (value != null) { if (value is ulong num && num != 0) { return num; } if (value is long num2 && num2 > 0) { return (ulong)num2; } } } if ((Object)(object)((Component)player).GetComponent<NetworkObject>() != (Object)null) { NetworkObject component = ((Component)player).GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null) { ulong ownerClientId = component.OwnerClientId; if (ownerClientId > 76561190000000000L && ownerClientId < 76561199999999999L) { return ownerClientId; } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[SteamIDHelper] Error getting Steam ID from PlayerControllerB: " + ex.Message)); } } return 0uL; } public static ulong GetClientID(ulong steamId) { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { try { foreach (KeyValuePair<ulong, NetworkClient> connectedClient in NetworkManager.Singleton.ConnectedClients) { ulong key = connectedClient.Key; ulong steamID = GetSteamID(key); if (steamID == steamId) { return key; } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[SteamIDHelper] Error searching ConnectedClients: " + ex.Message)); } } } try { PlayerControllerB[] array = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if ((Object)(object)val != (Object)null && val.actualClientId != 0) { ulong steamIDFromPlayer = GetSteamIDFromPlayer(val); if (steamIDFromPlayer == steamId) { return val.actualClientId; } } } } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[SteamIDHelper] Error searching PlayerControllerB: " + ex2.Message)); } } return steamId; } } public static class SteamNameFetcher { private static readonly Dictionary<ulong, string> _nameCache = new Dictionary<ulong, string>(); private static readonly Dictionary<ulong, bool> _fetching = new Dictionary<ulong, bool>(); private static MonoBehaviour? _coroutineRunner; public static void Init(MonoBehaviour coroutineRunner) { _coroutineRunner = coroutineRunner; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[SteamNameFetcher] Initialized"); } } public static void FetchPlayerName(ulong steamId, Action<ulong, string>? onComplete = null) { string value; if ((Object)(object)_coroutineRunner == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"[SteamNameFetcher] CoroutineRunner not initialized!"); } } else if (_nameCache.TryGetValue(steamId, out value)) { onComplete?.Invoke(steamId, value); } else if (_fetching.ContainsKey(steamId) && _fetching[steamId]) { _coroutineRunner.StartCoroutine(WaitForFetch(steamId, onComplete)); } else { _fetching[steamId] = true; _coroutineRunner.StartCoroutine(FetchSteamNameCoroutine(steamId, onComplete)); } } private static IEnumerator WaitForFetch(ulong steamId, Action<ulong, string>? onComplete) { float timeout = 10f; float elapsed = 0f; while (_fetching.ContainsKey(steamId) && _fetching[steamId] && elapsed < timeout) { yield return (object)new WaitForSeconds(0.5f); elapsed += 0.5f; if (_nameCache.TryGetValue(steamId, out string name)) { onComplete?.Invoke(steamId, name); yield break; } name = null; } if (elapsed >= timeout) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"[SteamNameFetcher] Timeout waiting for Steam ID {steamId}"); } onComplete?.Invoke(steamId, "Unknown"); } } private static IEnumerator FetchSteamNameCoroutine(ulong steamId, Action<ulong, string>? onComplete) { string url = $"https://steamcommunity.com/profiles/{steamId}"; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SteamNameFetcher] Fetching name for Steam ID: {steamId}"); } UnityWebRequest request = UnityWebRequest.Get(url); try { request.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); yield return request.SendWebRequest(); string playerName = "Unknown"; if ((int)request.result == 1) { try { string html = request.downloadHandler.text; int nameStart = html.IndexOf("<span class=\"actual_persona_name\">"); if (nameStart >= 0) { nameStart = html.IndexOf(">", nameStart) + 1; int nameEnd3 = html.IndexOf("<", nameStart); if (nameEnd3 > nameStart) { playerName = html.Substring(nameStart, nameEnd3 - nameStart).Trim(); playerName = DecodeHtmlEntities(playerName); } } if ((playerName == "Unknown" || string.IsNullOrEmpty(playerName)) && nameStart < 0) { nameStart = html.IndexOf("<div class=\"persona_name\">"); if (nameStart >= 0) { nameStart = html.IndexOf(">", nameStart) + 1; int nameEnd2 = html.IndexOf("<", nameStart); if (nameEnd2 > nameStart) { playerName = html.Substring(nameStart, nameEnd2 - nameStart).Trim(); playerName = DecodeHtmlEntities(playerName); } } } if ((playerName == "Unknown" || string.IsNullOrEmpty(playerName)) && nameStart < 0) { nameStart = html.IndexOf("profile_header_actual_link"); if (nameStart >= 0) { nameStart = html.IndexOf(">", nameStart) + 1; int nameEnd = html.IndexOf("<", nameStart); if (nameEnd > nameStart) { playerName = html.Substring(nameStart, nameEnd - nameStart).Trim(); playerName = DecodeHtmlEntities(playerName); } } } if (string.IsNullOrWhiteSpace(playerName)) { playerName = "Unknown"; } } catch (Exception ex2) { Exception ex = ex2; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)$"[SteamNameFetcher] Error parsing HTML: {ex}"); } playerName = "Unknown"; } } else { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[SteamNameFetcher] Failed to fetch Steam profile: " + request.error)); } } if (!string.IsNullOrEmpty(playerName) && playerName != "Unknown") { _nameCache[steamId] = playerName; ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[SteamNameFetcher] Cached name for {steamId}: {playerName}"); } } _fetching[steamId] = false; onComplete?.Invoke(steamId, playerName); } finally { ((IDisposable)request)?.Dispose(); } } public static void FetchPlayerNameFromGameOrSteam(ulong steamId, Action<ulong, string>? onComplete = null) { string playerNameFromGame = GetPlayerNameFromGame(steamId); if (!string.IsNullOrEmpty(playerNameFromGame) && playerNameFromGame != "Unknown") { _nameCache[steamId] = playerNameFromGame; onComplete?.Invoke(steamId, playerNameFromGame); } else { FetchPlayerName(steamId, onComplete); } } private static string GetPlayerNameFromGame(ulong steamId) { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return "Unknown"; } if (NetworkManager.Singleton.IsServer && NetworkManager.Singleton.ConnectedClients != null) { foreach (KeyValuePair<ulong, NetworkClient> connectedClient in NetworkManager.Singleton.ConnectedClients) { ulong key = connectedClient.Key; ulong steamID = SteamIDHelper.GetSteamID(key); if (steamID == steamId) { NetworkClient value = connectedClient.Value; if ((Object)(object)value.PlayerObject != (Object)null) { string name = ((Object)value.PlayerObject).name; if (name != null && !string.IsNullOrEmpty(name) && name != "Player") { return name; } break; } break; } } } PlayerControllerB[] array = Object.FindObjectsByType<PlayerControllerB>((FindObjectsSortMode)0); PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if ((Object)(object)val != (Object)null && val.actualClientId == steamId) { FieldInfo field = typeof(PlayerControllerB).GetField("playerUsername", BindingFlags.Instance | BindingFlags.Public); if (field != null && field.GetValue(val) is string text && !string.IsNullOrEmpty(text)) { return text; } if (!string.IsNullOrEmpty(((Object)val).name) && ((Object)val).name != "Player") { return ((Object)val).name; } } } return "Unknown"; } public static string GetCachedName(ulong steamId) { string value; return _nameCache.TryGetValue(steamId, out value) ? value : "Unknown"; } public static void CacheName(ulong steamId, string name) { if (!string.IsNullOrEmpty(name) && name != "Unknown") { _nameCache[steamId] = name; } } public static void ClearCache() { _nameCache.Clear(); _fetching.Clear(); } private static string DecodeHtmlEntities(string html) { if (string.IsNullOrEmpty(html)) { return html; } return html.Replace("&", "&").Replace("<", "<").Replace(">", ">") .Replace(""", "\"") .Replace("'", "'") .Replace("'", "'") .Replace(" ", " "); } } public static class UIHelper { private class UIHelperBehaviour : MonoBehaviour { private void Update() { if (IsAnyUIOpen()) { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } } private void OnGUI() { _textInputActive = false; Action[] array = _drawers.ToArray(); if (array.Length == 0) { return; } Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; Action[] array2 = array; foreach (Action action in array2) { try { action(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[UIHelper] Error in drawer: {ex}"); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[UIHelper] Stack trace: " + ex.StackTrace)); } } } } } private static readonly List<Action> _drawers = new List<Action>(); private static UIHelperBehaviour? _behaviour; private static bool _textInputActive = false; public static void Register(Action draw) { EnsureInitialized(); if (!_drawers.Contains(draw)) { _drawers.Add(draw); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[UIHelper] Registered drawer, total: {_drawers.Count}"); } UpdateCursorState(); } } public static void Unregister(Action draw) { if (_drawers.Contains(draw)) { _drawers.Remove(draw); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[UIHelper] Unregistered drawer, total: {_drawers.Count}"); } UpdateCursorState(); } } public static bool IsAnyUIOpen() { return _drawers.Count > 0; } public static bool IsTextInputActive() { return _textInputActive; } public static void SetTextInputActive(bool active) { _textInputActive = active; } private static void UpdateCursorState() { if (!((Object)(object)_behaviour == (Object)null) && IsAnyUIOpen()) { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } } private static void EnsureInitialized() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_behaviour != (Object)null)) { GameObject val = new GameObject("PastaUIHelper"); Object.DontDestroyOnLoad((Object)(object)val); _behaviour = val.AddComponent<UIHelperBehaviour>(); ((Object)val).hideFlags = (HideFlags)61; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[UIHelper] UIHelper initialized"); } } } } public static class UIStyle { private static GUIStyle? _windowStyle; private static GUIStyle? _labelStyle; private static GUIStyle? _buttonStyle; private static GUIStyle? _textFieldStyle; private static GUIStyle? _boxStyle; private static bool _stylesInitialized = false; private static Dictionary<string, Texture2D> _textureCache = new Dictionary<string, Texture2D>(); public static GUIStyle WindowStyle { get { InitializeStyles(); return _windowStyle; } } public static GUIStyle LabelStyle { get { InitializeStyles(); return _labelStyle; } } public static GUIStyle ButtonStyle { get { InitializeStyles(); return _buttonStyle; } } public static GUIStyle TextFieldStyle { get { InitializeStyles(); return _textFieldStyle; } } public static GUIStyle BoxStyle { get { InitializeStyles(); return _boxStyle; } } public static void InitializeStyles() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_009c: Expected O, but got Unknown //IL_00a6: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //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_00e4: Expected O, but got Unknown //IL_00ea: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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_0134: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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_0189: 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_019f: 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_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Expected O, but got Unknown //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01dc: 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_01fb: 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_0211: 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_0236: 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_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Expected O, but got Unknown //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Expected O, but got Unknown //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Expected O, but got Unknown //IL_02be: 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_02cd: Expected O, but got Unknown //IL_02d3: Expected O, but got Unknown if (!_stylesInitialized) { GUIStyle val = new GUIStyle(GUI.skin.window); val.normal.background = CreateColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.95f)); val.onNormal.background = CreateColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.95f)); val.border = new RectOffset(8, 8, 20, 8); val.padding = new RectOffset(10, 10, 25, 10); _windowStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label); val2.normal.textColor = Color.white; val2.fontSize = 14; val2.fontStyle = (FontStyle)0; val2.alignment = (TextAnchor)3; val2.padding = new RectOffset(5, 5, 5, 5); _labelStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.button); val3.normal.background = CreateColorTexture(new Color(0.2f, 0.2f, 0.2f, 1f)); val3.normal.textColor = Color.white; val3.hover.background = CreateColorTexture(new Color(0.3f, 0.3f, 0.3f, 1f)); val3.hover.textColor = Color.white; val3.active.background = CreateColorTexture(new Color(0.15f, 0.15f, 0.15f, 1f)); val3.active.textColor = Color.white; val3.fontSize = 13; val3.padding = new RectOffset(10, 10, 5, 5); val3.alignment = (TextAnchor)4; _buttonStyle = val3; GUIStyle val4 = new GUIStyle(GUI.skin.textField); val4.normal.background = CreateColorTexture(new Color(0.15f, 0.15f, 0.15f, 1f)); val4.normal.textColor = Color.white; val4.focused.background = CreateColorTexture(new Color(0.2f, 0.2f, 0.2f, 1f)); val4.focused.textColor = Color.white; val4.padding = new RectOffset(5, 5, 5, 5); val4.fontSize = 12; _textFieldStyle = val4; GUIStyle val5 = new GUIStyle(GUI.skin.box); val5.normal.background = CreateColorTexture(new Color(0.05f, 0.05f, 0.05f, 0.8f)); val5.border = new RectOffset(1, 1, 1, 1); val5.padding = new RectOffset(5, 5, 5, 5); _boxStyle = val5; _stylesInitialized = true; } } private static Texture2D CreateColorTexture(Color color) { //IL_000e: 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) //IL_002a: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) string key = $"{color.r}_{color.g}_{color.b}_{color.a}"; if (_textureCache.TryGetValue(key, out Texture2D value)) { if ((Object)(object)value != (Object)null) { return value; } _textureCache.Remove(key); } Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, color); val.Apply(); ((Object)val).hideFlags = (HideFlags)52; _textureCache[key] = val; return val; } public static void ResetStyles() { _stylesInitialized = false; _windowStyle = null; _labelStyle = null; _buttonStyle = null; _textFieldStyle = null; _boxStyle = null; foreach (Texture2D value in _textureCache.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } _textureCache.Clear(); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "PastaClient"; public const string PLUGIN_NAME = "PastaClient"; public const string PLUGIN_VERSION = "1.2.1"; } } namespace PastaClient.Patches { public class ChatCommandHandler : MonoBehaviour { private static ChatCommandHandler instance; private TMP_InputField? chatInputField; private string lastInput = ""; public static void Init() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)instance != (Object)null)) { GameObject val = new GameObject("ChatCommandHandler"); instance = val.AddComponent<ChatCommandHandler>(); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; } } private void Update() { if (!((Object)(object)HUDManager.Instance == (Object)null) && !((Object)(object)chatInputField != (Object)null)) { FindChatInputField(); if ((Object)(object)chatInputField != (Object)null) { SubscribeToChatInput(); } } } private void FindChatInputField() { FieldInfo[] fields = typeof(HUDManager).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { object value = fieldInfo.GetValue(HUDManager.Instance); TMP_InputField val = (TMP_InputField)((value is TMP_InputField) ? value : null); if (val != null) { chatInputField = val; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[ChatCommandHandler] Found chat input field: " + fieldInfo.Name)); } break; } InputField val2 = (InputField)((value is InputField) ? value : null); if (val2 != null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[ChatCommandHandler] Found UI InputField: " + fieldInfo.Name)); } } } } private void SubscribeToChatInput() { if (!((Object)(object)chatInputField == (Object)null)) { ((UnityEvent<string>)(object)chatInputField.onSubmit).AddListener((UnityAction<string>)OnChatSubmit); ((UnityEvent<string>)(object)chatInputField.onEndEdit).AddListener((UnityAction<string>)OnChatSubmit); } } private void OnChatSubmit(string input) { if (!string.IsNullOrWhiteSpace(input) && !(input == lastInput)) { input = input.Trim(); lastInput = input; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[ChatCommandHandler] Chat submitted: " + input)); } if (ProcessChatCommand(input) && (Object)(object)chatInputField != (Object)null) { chatInputField.text = ""; lastInput = ""; } } } private bool ProcessChatCommand(string input) { if (input.StartsWith(":st", StringComparison.OrdinalIgnoreCase)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[ChatCommandHandler] :st command detected"); } ShitterUI.Open(); return true; } if (input.StartsWith(":who", StringComparison.OrdinalIgnoreCase)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[ChatCommandHandler] :who command detected"); } RecentUI.Open(); return true; } return false; } } [HarmonyPatch(typeof(PlayerControllerB), "Update")] public class PlayerInputPatch { private static void Prefix(PlayerControllerB __instance, ref bool ___isPlayerDead, ref bool ___isTypingChat) { } } [HarmonyPatch(typeof(Input), "GetAxis", new Type[] { typeof(string) })] public class InputPatch { private static bool Prefix(string axisName, ref float __result) { if (UIHelper.IsTextInputActive()) { __result = 0f; return false; } if (UIHelper.IsAnyUIOpen() && (axisName == "Mouse X" || axisName == "Mouse Y")) { __result = 0f; return false; } return true; } } [HarmonyPatch(typeof(Input), "GetKey", new Type[] { typeof(KeyCode) })] public class InputGetKeyPatch { private static bool Prefix(KeyCode key, ref bool __result) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if (UIHelper.IsTextInputActive() && (int)key != 27) { __result = false; return false; } return true; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }