using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using PeakVoiceFix.Patches; using Photon.Pun; using Photon.Realtime; using Photon.Voice.PUN; using Photon.Voice.Unity; using Steamworks; using TMPro; using UnityEngine; 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: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BetterPeakVoiceFix")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2+d55073ad32ec27c6a40cf9eee51ea95bd1307645")] [assembly: AssemblyProduct("BetterPeakVoiceFix")] [assembly: AssemblyTitle("BetterPeakVoiceFix")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.2.0")] [module: UnverifiableCode] namespace PeakVoiceFix { public static class L { private static string _lang = "中文"; private static Dictionary _current; private static readonly Dictionary ZH = new Dictionary { { "ui_title", "语音详细状态" }, { "ui_debug_title", "语音修复调试控制台 (Alt+J) | EvCode:186" }, { "btn_copy_all", "复制全部" }, { "btn_export", "导出文件" }, { "btn_clear", "清空" }, { "btn_dump", "打印语音底层名单" }, { "filter_all", "全部" }, { "filter_local", "本机" }, { "client_not_connected", "客户端未连接" }, { "voice_player_list", "语音底层名单" }, { "ghost_tag", "[幽灵]" }, { "exported_to", "已导出" }, { "export_failed", "失败" }, { "copied", "已复制" }, { "voice_single_player", "本机语音: 单人游戏" }, { "voice_local", "本机语音: " }, { "voice_connected", "已连接" }, { "voice_count", "语音连接人数:" }, { "id_mismatch", "人ID错位" }, { "local_ping", "本机延迟: " }, { "local_server", "本机已连服务器:" }, { "host_server", "房主已连服务器:" }, { "label_local", "本机" }, { "label_sync", "同步:" }, { "label_abnormal", "异常:" }, { "warn_majority", "⚠ [警告] 多数玩家({0}人)在另一频道!" }, { "virtual_player", "虚拟玩家1" }, { "sos_snapshot", "[SOS快照]" }, { "sos_majority", "多数派" }, { "sos_person", "人" }, { "sos_detected", "检测到 {0} 掉线" }, { "sos_target", "目标" }, { "sos_last", "上次" }, { "unknown", "未知" }, { "cache_snapshot", "[缓存快照]" }, { "seconds_ago", "秒前" }, { "majority_server", "多数派服务器:" }, { "not_connected", "未连接" }, { "history", "[历史]" }, { "notification_disconnected", "连接断开" }, { "manual_operation", "[系统] 手动操作..." }, { "state_synced", "同步" }, { "state_isolated", "孤立" }, { "state_connecting", "连接中" }, { "state_disconnected", "断开" }, { "state_connected", "已连接" }, { "state_mismatch", "错位" }, { "state_abnormal", "异常" }, { "state_unknown", "未知" }, { "state_left", "已退" }, { "state_status", "状态" }, { "cs_initializing", "初始化中..." }, { "cs_authenticating", "验证中..." }, { "cs_authenticated", "已验证" }, { "cs_joining", "加入中..." }, { "cs_joined", "已连接" }, { "cs_disconnecting", "断开中..." }, { "cs_disconnected", "断开" }, { "cs_connecting_game", "连接到游戏服务器中..." }, { "cs_connecting_master", "连接到主服务器中..." }, { "cs_connecting_name", "连接到名称服务器中..." }, { "detail_waiting_data", "(等待数据...)" }, { "detail_fetching", "获取中..." }, { "detail_connecting_to", "正在连接" }, { "detail_joined_voice", "已连入语音服" }, { "detail_connecting_local", "连接中..." }, { "detail_latency", "延迟" }, { "ls_joined", "已连接" }, { "ls_disconnected", "已断开" }, { "log_state_change", "状态变更" }, { "log_host_ip_change", "房主连接服务器变动" }, { "log_host_disconnected", "[Host] 房主意外断开,正在自动恢复..." }, { "log_loop_blind", "[循环] 已失败{0}次,暂时切换为盲连..." }, { "log_wrong_freq", "[异频] 当前:{0} 目标:{1} | 纠正({2}/2)" }, { "log_compromise", "[妥协] 纠正失败,驻留当前IP: {0}" }, { "log_reflect_fail", "无法反射设置服务器 IP" }, { "log_reflect_error", "反射失败" }, { "log_decision", "[决策] 目标变更" }, { "log_majority", "多数派({0}人)" }, { "log_host", "房主" }, { "log_auto_blind", "自动(盲连)" }, { "log_sos_send", "[SOS] 发送求救" }, { "log_sos_target", "目标" }, { "log_sos_local", "本机" }, { "log_sos_manual", "手动断开 (Manual)" }, { "log_sos_received", "收到 {0} SOS (目标:{1})" }, { "log_alt_k_disconnect", "[系统] Alt+K 手动断开" }, { "log_alt_k_reconnect", "[系统] Alt+K 强制重连" }, { "log_cache_snapshot", "[缓存快照] 记录数" }, { "cfg_cat_ui", "UI设置" }, { "cfg_cat_net", "网络设置" }, { "cfg_cat_adv", "高级与调试" }, { "cfg_ui_position", "选择UI面板显示在屏幕的哪一侧。" }, { "cfg_toggle_key", "切换语音面板显示的按键。" }, { "cfg_show_pro", "是否在面板中显示具体的已连接语音服务器IP地址和调试信息。" }, { "cfg_offset_x_r", "距离屏幕右边缘的水平距离。" }, { "cfg_offset_y_r", "距离屏幕上边缘的垂直距离。" }, { "cfg_offset_x_l", "距离屏幕左边缘的水平距离。" }, { "cfg_offset_y_l", "距离屏幕上边缘的垂直距离。" }, { "cfg_font_size", "面板文字的基础大小。" }, { "cfg_host_symbol", "显示在房主名字前的特殊符号。" }, { "cfg_timeout", "如果连接卡住,超过多少秒判定为断开。" }, { "cfg_retry_interval", "每次自动重连之间的冷却时间。" }, { "cfg_manual_reconnect", "允许按 Alt+K 强制断开或重连语音。" }, { "cfg_ghost_fix", "尝试从场景中搜寻名字以修复 Unknown 问题。" }, { "cfg_max_name_len", "显示名字的最大字符数。" }, { "cfg_latency_offset", "Ping值显示的水平像素偏移。" }, { "cfg_auto_hide", "当所有人连接正常时,自动隐藏简易模式的UI。" }, { "cfg_show_ping", "在简易模式下方显示本机延迟。" }, { "cfg_hide_menu", "打开ESC菜单时隐藏UI。" }, { "cfg_virtual_player", "添加一个假玩家用于测试UI布局。" }, { "cfg_virtual_name", "假玩家的名字。" }, { "cfg_language", "语言(重启游戏生效) | Language(Need restart)" }, { "cfgn_ui_position", "UI位置" }, { "cfgn_toggle_key", "详细UI切换键" }, { "cfgn_show_pro", "显示连接到的语音服务器IP和详细信息" }, { "cfgn_offset_x_r", "右侧边距" }, { "cfgn_offset_y_r", "顶部边距(右)" }, { "cfgn_offset_x_l", "左侧边距" }, { "cfgn_offset_y_l", "顶部边距(左)" }, { "cfgn_font_size", "字体大小" }, { "cfgn_host_symbol", "房主标记符号" }, { "cfgn_timeout", "重连超时时间 (s)" }, { "cfgn_retry_interval", "重试间隔 (s)" }, { "cfgn_manual_reconnect", "启用手动重置 (Alt+K)" }, { "cfgn_max_name_len", "最大名字长度" }, { "cfgn_latency_offset", "延迟对齐偏移量" }, { "cfgn_auto_hide", "自动隐藏简易UI" }, { "cfgn_show_ping", "简易模式显示Ping" }, { "cfgn_hide_menu", "ESC菜单界面时隐藏" }, { "cfgn_virtual_player", "启用虚拟玩家" }, { "cfgn_virtual_name", "虚拟玩家名字" }, { "cfgn_language", "语言 | Language" } }; private static readonly Dictionary EN = new Dictionary { { "ui_title", "Voice Detail Status" }, { "ui_debug_title", "Voice Fix Debug Console (Alt+J) | EvCode:186" }, { "btn_copy_all", "Copy All" }, { "btn_export", "Export" }, { "btn_clear", "Clear" }, { "btn_dump", "Dump Photon Player List" }, { "filter_all", "All" }, { "filter_local", "Local" }, { "client_not_connected", "Client not connected" }, { "voice_player_list", "Photon Player List" }, { "ghost_tag", "[Ghost]" }, { "exported_to", "Exported to" }, { "export_failed", "Failed" }, { "copied", "Copied" }, { "voice_single_player", "Local Voice: Single Player" }, { "voice_local", "Local Voice: " }, { "voice_connected", "Connected" }, { "voice_count", "Voice Connected: " }, { "id_mismatch", "ID Mismatched" }, { "local_ping", "Local Ping: " }, { "local_server", "Local Server:" }, { "host_server", "Host Server:" }, { "label_local", "Local" }, { "label_sync", "Sync:" }, { "label_abnormal", "Abnormal:" }, { "warn_majority", "⚠ [WARN] Majority ({0}) on another channel!" }, { "virtual_player", "Virtual Player 1" }, { "sos_snapshot", "[SOS Snapshot]" }, { "sos_majority", "Majority" }, { "sos_person", "" }, { "sos_detected", "Detected {0} disconnected" }, { "sos_target", "Target" }, { "sos_last", "Last" }, { "unknown", "Unknown" }, { "cache_snapshot", "[Cache Snapshot]" }, { "seconds_ago", "sec ago" }, { "majority_server", "Majority Server:" }, { "not_connected", "Not Connected" }, { "history", "[History]" }, { "notification_disconnected", "Disconnected" }, { "manual_operation", "[System] Manual operation..." }, { "state_synced", "Synced" }, { "state_isolated", "Isolated" }, { "state_connecting", "Connecting" }, { "state_disconnected", "Disconnected" }, { "state_connected", "Connected" }, { "state_mismatch", "Mismatched" }, { "state_abnormal", "Abnormal" }, { "state_unknown", "Unknown" }, { "state_left", "Left" }, { "state_status", "Status" }, { "cs_initializing", "Initializing..." }, { "cs_authenticating", "Authenticating..." }, { "cs_authenticated", "Authenticated" }, { "cs_joining", "Joining..." }, { "cs_joined", "Joined" }, { "cs_disconnecting", "Disconnecting..." }, { "cs_disconnected", "Disconnected" }, { "cs_connecting_game", "Connecting to game server..." }, { "cs_connecting_master", "Connecting to master server..." }, { "cs_connecting_name", "Connecting to name server..." }, { "detail_waiting_data", "(Waiting for data...)" }, { "detail_fetching", "Fetching..." }, { "detail_connecting_to", "Connecting to" }, { "detail_joined_voice", "Joined voice server" }, { "detail_connecting_local", "Connecting..." }, { "detail_latency", "Latency" }, { "ls_joined", "Connected" }, { "ls_disconnected", "Disconnected" }, { "log_state_change", "State change" }, { "log_host_ip_change", "Host server connection change" }, { "log_host_disconnected", "[Host] Unexpected disconnect, recovering..." }, { "log_loop_blind", "[Loop] Failed {0} times, switching to blind..." }, { "log_wrong_freq", "[WrongIP] Current:{0} Target:{1} | Fix({2}/2)" }, { "log_compromise", "[Compromise] Fix failed, staying on: {0}" }, { "log_reflect_fail", "Cannot set Server IP via reflection" }, { "log_reflect_error", "Reflection failed" }, { "log_decision", "[Decision] Target changed" }, { "log_majority", "Majority ({0})" }, { "log_host", "Host" }, { "log_auto_blind", "Auto (Blind)" }, { "log_sos_send", "[SOS] Sending SOS" }, { "log_sos_target", "Target" }, { "log_sos_local", "Local" }, { "log_sos_manual", "Manual Disconnect" }, { "log_sos_received", "Received {0} SOS (Target:{1})" }, { "log_alt_k_disconnect", "[System] Alt+K Manual disconnect" }, { "log_alt_k_reconnect", "[System] Alt+K Force reconnect" }, { "log_cache_snapshot", "[Cache Snapshot] Count" }, { "cfg_cat_ui", "UI Settings" }, { "cfg_cat_net", "Network Settings" }, { "cfg_cat_adv", "Advanced & Debug" }, { "cfg_ui_position", "Which side of the screen to display the UI panel." }, { "cfg_toggle_key", "Key to toggle voice panel display." }, { "cfg_show_pro", "Show connected voice server IP and debug info in the panel." }, { "cfg_offset_x_r", "Horizontal offset from right edge." }, { "cfg_offset_y_r", "Vertical offset from top edge." }, { "cfg_offset_x_l", "Horizontal offset from left edge." }, { "cfg_offset_y_l", "Vertical offset from top edge." }, { "cfg_font_size", "Base font size of the panel." }, { "cfg_host_symbol", "Symbol displayed before host's name." }, { "cfg_timeout", "Seconds before connection is deemed disconnected." }, { "cfg_retry_interval", "Cooldown between auto-reconnect attempts." }, { "cfg_manual_reconnect", "Allow Alt+K to force disconnect/reconnect voice." }, { "cfg_ghost_fix", "Try to find names from scene to fix Unknown issue." }, { "cfg_max_name_len", "Max display characters for names." }, { "cfg_latency_offset", "Horizontal pixel offset for ping display." }, { "cfg_auto_hide", "Auto-hide simple UI when all connections are normal." }, { "cfg_show_ping", "Show local ping below simple mode UI." }, { "cfg_hide_menu", "Hide UI when ESC menu is open." }, { "cfg_virtual_player", "Add a fake player for UI layout testing." }, { "cfg_virtual_name", "Name of the fake player." }, { "cfg_language", "语言(重启游戏生效) | Language(Need restart)" }, { "cfgn_ui_position", "UI Position" }, { "cfgn_toggle_key", "Toggle Key" }, { "cfgn_show_pro", "Show Server IP & Details" }, { "cfgn_offset_x_r", "Right Margin" }, { "cfgn_offset_y_r", "Top Margin (Right)" }, { "cfgn_offset_x_l", "Left Margin" }, { "cfgn_offset_y_l", "Top Margin (Left)" }, { "cfgn_font_size", "Font Size" }, { "cfgn_host_symbol", "Host Symbol" }, { "cfgn_timeout", "Reconnect Timeout (s)" }, { "cfgn_retry_interval", "Retry Interval (s)" }, { "cfgn_manual_reconnect", "Enable Manual Reset (Alt+K)" }, { "cfgn_max_name_len", "Max Name Length" }, { "cfgn_latency_offset", "Latency Alignment Offset" }, { "cfgn_auto_hide", "Auto-Hide Simple UI" }, { "cfgn_show_ping", "Show Ping in Simple Mode" }, { "cfgn_hide_menu", "Hide on ESC Menu" }, { "cfgn_virtual_player", "Enable Virtual Player" }, { "cfgn_virtual_name", "Virtual Player Name" }, { "cfgn_language", "语言 | Language" } }; public static string DetectDefault() { CultureInfo currentUICulture = CultureInfo.CurrentUICulture; return currentUICulture.Name.StartsWith("zh") ? "中文" : "English"; } public static void Init(string lang) { _lang = lang; _current = ((_lang == "中文") ? ZH : EN); } public static string Get(string key) { if (_current != null && _current.TryGetValue(key, out var value)) { return value; } if (ZH.TryGetValue(key, out var value2)) { return value2; } return key; } public static string Get(string key, params object[] args) { return string.Format(Get(key), args); } } public class CacheEntry { public string IP; public float LastSeenTime; public string PlayerName; public byte RemoteState; public string ModVersion; } public class SOSData { public int ActorNumber; public string PlayerName; public string TargetIP; public string OriginIP; public float ReceiveTime; } public static class NetworkManager { public static Dictionary PlayerCache = new Dictionary(); public static List ActiveSOSList = new List(); public static List HostHistory = new List(); private static string LastKnownHostIP = ""; private static string LastDecisionLog = ""; private static ClientState lastClientState = (ClientState)14; public static PunVoiceClient punVoice; private static float nextRetryTime = 0f; private static float lastPingPublishTime = 0f; private static float lastSOSTime = 0f; private static float nextSummaryLogTime = 0f; private static bool wasInRoom = false; private const float SCAN_INTERVAL = 30f; private const float CACHE_TTL = 180f; private const float PING_PUBLISH_INTERVAL = 89f; private const string PROP_IP = "PVF_IP"; private const string PROP_PING = "PVF_Ping"; private const byte TYPE_SOS = 0; private const byte TYPE_LOG = 1; private const byte TYPE_STATE = 2; public static string TargetGameServer { get; private set; } public static bool ConnectedUsingHost { get; private set; } = true; public static bool IsBlindConnect { get; private set; } = false; public static int WrongIPCount { get; private set; } = 0; public static int ConnectionFailCount { get; private set; } = 0; public static int TotalRetryCount { get; private set; } = 0; public static string LastErrorMessage { get; private set; } = ""; public static float LastScanTime { get; private set; } = 0f; public static float LastHostUpdateTime { get; private set; } = 0f; public static string GetPlayerName(int actorNumber) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) string text = "Unknown"; Player val = null; if (PhotonNetwork.CurrentRoom != null) { val = PhotonNetwork.CurrentRoom.GetPlayer(actorNumber, false); } if (val != null && !string.IsNullOrEmpty(val.NickName)) { text = val.NickName; UpdatePlayerCache(actorNumber, text); return text; } if (PlayerCache.ContainsKey(actorNumber)) { string playerName = PlayerCache[actorNumber].PlayerName; if (!string.IsNullOrEmpty(playerName) && playerName != "Unknown" && !playerName.StartsWith("Player ")) { return playerName; } } if ((text == "Unknown" || string.IsNullOrEmpty(text)) && val != null && !string.IsNullOrEmpty(val.UserId)) { try { if (ulong.TryParse(val.UserId, out var result)) { string friendPersonaName = SteamFriends.GetFriendPersonaName(new CSteamID(result)); if (!string.IsNullOrEmpty(friendPersonaName) && friendPersonaName != "[unknown]") { text = friendPersonaName; UpdatePlayerCache(actorNumber, text); return text; } } } catch (Exception) { } } if (text == "Unknown" || string.IsNullOrEmpty(text)) { string text2 = ScavengeNameFromScene(actorNumber); if (!string.IsNullOrEmpty(text2)) { UpdatePlayerCache(actorNumber, text2); return text2; } } if (text == "Unknown") { return $"Player {actorNumber}"; } return text; } public static string ScavengeNameFromScene(int actorNumber) { try { PhotonView[] array = Object.FindObjectsOfType(); PhotonView[] array2 = array; foreach (PhotonView val in array2) { if (!((Object)(object)val == (Object)null) && val.OwnerActorNr == actorNumber) { if (val.Owner != null && !string.IsNullOrEmpty(val.Owner.NickName)) { return val.Owner.NickName; } TextMeshProUGUI componentInChildren = ((Component)val).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null && !string.IsNullOrEmpty(((TMP_Text)componentInChildren).text)) { return ((TMP_Text)componentInChildren).text; } TextMeshPro componentInChildren2 = ((Component)val).GetComponentInChildren(true); if ((Object)(object)componentInChildren2 != (Object)null && !string.IsNullOrEmpty(((TMP_Text)componentInChildren2).text)) { return ((TMP_Text)componentInChildren2).text; } Text componentInChildren3 = ((Component)val).GetComponentInChildren(true); if ((Object)(object)componentInChildren3 != (Object)null && !string.IsNullOrEmpty(componentInChildren3.text)) { return componentInChildren3.text; } } } } catch (Exception) { } return null; } public static void UpdatePlayerCache(int actorNumber, string name, string ip = null, string version = null) { if (!PlayerCache.ContainsKey(actorNumber)) { PlayerCache[actorNumber] = new CacheEntry(); } if (!string.IsNullOrEmpty(name) && name != "Unknown") { PlayerCache[actorNumber].PlayerName = name; } if (!string.IsNullOrEmpty(ip)) { PlayerCache[actorNumber].IP = ip; } if (!string.IsNullOrEmpty(version)) { PlayerCache[actorNumber].ModVersion = version; } PlayerCache[actorNumber].LastSeenTime = Time.unscaledTime; } public static bool IsGhost(int voiceActorNumber) { if (PhotonNetwork.CurrentRoom == null) { return true; } if (PhotonNetwork.CurrentRoom.GetPlayer(voiceActorNumber, false) != null) { return false; } if ((Object)(object)punVoice == (Object)null || ((VoiceConnection)punVoice).Client == null || ((LoadBalancingClient)((VoiceConnection)punVoice).Client).CurrentRoom == null) { return true; } Dictionary players = ((LoadBalancingClient)((VoiceConnection)punVoice).Client).CurrentRoom.Players; if (!players.TryGetValue(voiceActorNumber, out var value)) { return true; } string userId = value.UserId; if (string.IsNullOrEmpty(userId)) { return true; } Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { if (!string.IsNullOrEmpty(val.UserId) && val.UserId == userId) { return false; } } return true; } public static bool IsPlayerInVoiceRoom(int gameActorNumber) { if ((Object)(object)punVoice == (Object)null || ((VoiceConnection)punVoice).Client == null || ((LoadBalancingClient)((VoiceConnection)punVoice).Client).CurrentRoom == null) { return false; } if (PhotonNetwork.CurrentRoom == null) { return false; } Player player = PhotonNetwork.CurrentRoom.GetPlayer(gameActorNumber, false); if (player == null) { return false; } if (((LoadBalancingClient)((VoiceConnection)punVoice).Client).CurrentRoom.Players.ContainsKey(gameActorNumber)) { return true; } string userId = player.UserId; if (string.IsNullOrEmpty(userId)) { return false; } foreach (KeyValuePair player2 in ((LoadBalancingClient)((VoiceConnection)punVoice).Client).CurrentRoom.Players) { if (!string.IsNullOrEmpty(player2.Value.UserId) && player2.Value.UserId == userId) { return true; } } return false; } public static int GetGhostCount() { if ((Object)(object)punVoice == (Object)null || ((VoiceConnection)punVoice).Client == null || ((LoadBalancingClient)((VoiceConnection)punVoice).Client).CurrentRoom == null) { return 0; } if (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount <= 1) { return 0; } int num = 0; foreach (int key in ((LoadBalancingClient)((VoiceConnection)punVoice).Client).CurrentRoom.Players.Keys) { if (IsGhost(key)) { num++; } } return num; } public static void SystemUpdate() { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00b9: 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_00d9: 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_00e5: Invalid comparison between Unknown and I4 //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)punVoice == (Object)null) { GameObject val = GameObject.Find("VoiceClient"); if ((Object)(object)val != (Object)null) { punVoice = val.GetComponent(); } } if (!PhotonNetwork.InRoom) { if (wasInRoom) { ResetRoomScopedState(); } wasInRoom = false; return; } wasInRoom = true; if ((Object)(object)punVoice != (Object)null && ((VoiceConnection)punVoice).Client != null) { ClientState state = ((LoadBalancingClient)((VoiceConnection)punVoice).Client).State; if (state != lastClientState) { string message = string.Format("{0}: {1} -> {2}", L.Get("log_state_change"), lastClientState, state); BroadcastLog(message); SendStateSync(state); if ((int)state == 9) { ConnectionFailCount = 0; } UpdateDataLayer(force: true); lastClientState = state; } } UpdateDataLayer(); HandleInputAndState(); ManageSOSList(); if ((Object)(object)punVoice != (Object)null && ((VoiceConnection)punVoice).Client != null && Time.unscaledTime >= nextRetryTime) { if (PhotonNetwork.IsMasterClient) { HandleHostLogic(); } else { HandleClientLogic(); } } } public static void SendStateSync(ClientState state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) byte b = (byte)state; object[] array = new object[3] { (byte)2, b, "v1.0.2" }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)0 }; PhotonNetwork.RaiseEvent((byte)186, (object)array, val, SendOptions.SendUnreliable); } public static void BroadcastLog(string message) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0057: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) string playerName = GetPlayerName(PhotonNetwork.LocalPlayer.ActorNumber); if ((Object)(object)VoiceUIManager.Instance != (Object)null) { VoiceUIManager.Instance.AddLog(playerName, message, isLocal: true); } byte b = 186; object[] array = new object[2] { (byte)1, message }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)0 }; PhotonNetwork.RaiseEvent(b, (object)array, val, SendOptions.SendReliable); } private static void UpdateDataLayer(bool force = false) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 bool flag = Time.unscaledTime - lastPingPublishTime > 89f; if ((force || flag) && (Object)(object)punVoice != (Object)null && ((VoiceConnection)punVoice).Client != null) { lastPingPublishTime = Time.unscaledTime; Hashtable val = new Hashtable(); val[(object)"PVF_Ping"] = PhotonNetwork.GetPing(); string ip = (string)(val[(object)"PVF_IP"] = (((int)((LoadBalancingClient)((VoiceConnection)punVoice).Client).State == 9) ? ((LoadBalancingClient)((VoiceConnection)punVoice).Client).GameServerAddress : "")); PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); UpdatePlayerCache(PhotonNetwork.LocalPlayer.ActorNumber, PhotonNetwork.LocalPlayer.NickName, ip, "v1.0.2"); } if (!force) { if (Time.unscaledTime - LastScanTime > 30f) { LastScanTime = Time.unscaledTime; ScanPlayers(); } if (Time.unscaledTime > nextSummaryLogTime) { PrintSummaryLog(); nextSummaryLogTime = Time.unscaledTime + 60f; } } } private static void PrintSummaryLog() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(string.Format("{0}:{1}", L.Get("log_cache_snapshot"), PlayerCache.Count)); foreach (KeyValuePair item in PlayerCache) { string arg = (string.IsNullOrEmpty(item.Value.IP) ? "N/A" : item.Value.IP); stringBuilder.AppendLine($" - {item.Value.PlayerName}: {arg} (St:{item.Value.RemoteState})"); } if ((Object)(object)VoiceUIManager.Instance != (Object)null) { VoiceUIManager.Instance.AddLog("System", stringBuilder.ToString(), isLocal: true); } } private static void ScanPlayers() { Player[] playerListOthers = PhotonNetwork.PlayerListOthers; foreach (Player val in playerListOthers) { object value = null; if (!((Dictionary)(object)val.CustomProperties).TryGetValue((object)"PVF_IP", out value) || !(value is string text)) { continue; } string playerName = GetPlayerName(val.ActorNumber); UpdatePlayerCache(val.ActorNumber, playerName, text); if (!val.IsMasterClient) { continue; } if (!string.IsNullOrEmpty(text)) { LastHostUpdateTime = Time.unscaledTime; } if (!string.IsNullOrEmpty(LastKnownHostIP) && LastKnownHostIP != text && !string.IsNullOrEmpty(text)) { string message = L.Get("log_host_ip_change") + ": " + LastKnownHostIP + " -> " + text; BroadcastLog(message); if (HostHistory.Count > 5) { HostHistory.RemoveAt(0); } HostHistory.Add($"[{DateTime.Now:HH:mm:ss}] {text}"); } LastKnownHostIP = text; } List list = (from x in PlayerCache where Time.unscaledTime - x.Value.LastSeenTime > 180f select x.Key).ToList(); foreach (int item in list) { PlayerCache.Remove(item); } } private static void ManageSOSList() { for (int num = ActiveSOSList.Count - 1; num >= 0; num--) { SOSData sOSData = ActiveSOSList[num]; if (Time.unscaledTime - sOSData.ReceiveTime > 60f) { ActiveSOSList.RemoveAt(num); } else { string playerName = GetPlayerName(sOSData.ActorNumber); if ((string.IsNullOrEmpty(playerName) || playerName == "Unknown" || playerName.StartsWith("Player ")) && (PhotonNetwork.CurrentRoom == null || PhotonNetwork.CurrentRoom.GetPlayer(sOSData.ActorNumber, false) == null)) { ActiveSOSList.RemoveAt(num); } else if (PhotonNetwork.CurrentRoom != null) { Player player = PhotonNetwork.CurrentRoom.GetPlayer(sOSData.ActorNumber, false); object value = null; if (player != null && ((Dictionary)(object)player.CustomProperties).TryGetValue((object)"PVF_IP", out value) && value is string value2 && !string.IsNullOrEmpty(value2)) { ActiveSOSList.RemoveAt(num); } } } } } private static void HandleHostLogic() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if ((int)((LoadBalancingClient)((VoiceConnection)punVoice).Client).State == 14) { BroadcastLog(L.Get("log_host_disconnected")); ((VoiceConnection)punVoice).ConnectUsingSettings(PhotonNetwork.PhotonServerSettings.AppSettings); nextRetryTime = Time.unscaledTime + 5f; } } private static void HandleClientLogic() { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Invalid comparison between Unknown and I4 //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Invalid comparison between Unknown and I4 string mode; string text = DecideTargetIP(out mode); if (!string.IsNullOrEmpty(text) && ConnectionFailCount > 0 && ConnectionFailCount % 6 >= 3) { BroadcastLog(L.Get("log_loop_blind", ConnectionFailCount)); text = null; mode += "->BlindLoop"; } if (string.IsNullOrEmpty(text)) { IsBlindConnect = true; TargetGameServer = null; if ((int)((LoadBalancingClient)((VoiceConnection)punVoice).Client).State == 14) { PerformReconnect("Blind"); } return; } IsBlindConnect = false; TargetGameServer = text; string gameServerAddress = ((LoadBalancingClient)((VoiceConnection)punVoice).Client).GameServerAddress; ClientState state = ((LoadBalancingClient)((VoiceConnection)punVoice).Client).State; if ((int)state == 9) { if (gameServerAddress == TargetGameServer) { WrongIPCount = 0; ConnectionFailCount = 0; TotalRetryCount = 0; return; } WrongIPCount++; if (WrongIPCount <= 2) { BroadcastLog(L.Get("log_wrong_freq", gameServerAddress, TargetGameServer, WrongIPCount) ?? ""); ((LoadBalancingClient)((VoiceConnection)punVoice).Client).Disconnect(); PerformReconnect(mode); } else if (WrongIPCount == 3) { BroadcastLog(L.Get("log_compromise", gameServerAddress)); } } else if ((int)state == 14) { ConnectionFailCount++; PerformReconnect(mode); } } private static void SetGameServerAddress(LoadBalancingClient client, string ip) { try { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; PropertyInfo property = ((object)client).GetType().GetProperty("GameServerAddress", bindingAttr); if (property != null && property.CanWrite) { property.SetValue(client, ip); return; } Type type = ((object)client).GetType(); while (type != null) { FieldInfo field = type.GetField("GameServerAddress", bindingAttr); if (field == null) { field = type.GetField("k__BackingField", bindingAttr); } if (field != null) { field.SetValue(client, ip); return; } type = type.BaseType; } if (VoiceFix.logger != null) { VoiceFix.logger.LogError((object)L.Get("log_reflect_fail")); } } catch (Exception arg) { if (VoiceFix.logger != null) { VoiceFix.logger.LogError((object)string.Format("{0}: {1}", L.Get("log_reflect_error"), arg)); } } } private static string DecideTargetIP(out string mode) { int maxCount; string majorityIP = GetMajorityIP(out maxCount); if (!string.IsNullOrEmpty(majorityIP) && maxCount >= 2) { mode = L.Get("log_majority", maxCount); ConnectedUsingHost = false; LogDecision(mode, majorityIP); return majorityIP; } if (!string.IsNullOrEmpty(LastKnownHostIP)) { mode = L.Get("log_host"); ConnectedUsingHost = true; LogDecision(mode, LastKnownHostIP); return LastKnownHostIP; } mode = L.Get("log_auto_blind"); ConnectedUsingHost = false; LogDecision(mode, "Auto"); return null; } private static void LogDecision(string mode, string target) { string text = mode + "->" + target; if (text != LastDecisionLog) { BroadcastLog(L.Get("log_decision") + ": " + text); LastDecisionLog = text; } } public static string GetMajorityIP(out int maxCount) { maxCount = 0; if (PlayerCache.Count == 0) { return null; } Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in PlayerCache) { if (!string.IsNullOrEmpty(item.Value.IP)) { if (!dictionary.ContainsKey(item.Value.IP)) { dictionary[item.Value.IP] = 0; } dictionary[item.Value.IP]++; } } string result = null; foreach (KeyValuePair item2 in dictionary) { if (item2.Value > maxCount) { maxCount = item2.Value; result = item2.Key; } } return result; } private static void PerformReconnect(string mode) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Invalid comparison between Unknown and I4 TotalRetryCount++; nextRetryTime = Time.unscaledTime + VoiceFix.RetryInterval.Value; if (Time.unscaledTime - lastSOSTime > 20f && PhotonNetwork.IsConnectedAndReady) { lastSOSTime = Time.unscaledTime; SendSOS(string.IsNullOrEmpty(TargetGameServer) ? "Unknown" : TargetGameServer); } if ((int)((LoadBalancingClient)((VoiceConnection)punVoice).Client).State != 14) { ((LoadBalancingClient)((VoiceConnection)punVoice).Client).Disconnect(); } SetGameServerAddress((LoadBalancingClient)(object)((VoiceConnection)punVoice).Client, TargetGameServer); ((VoiceConnection)punVoice).ConnectUsingSettings(PhotonNetwork.PhotonServerSettings.AppSettings); } private static void SendSOS(string targetInfo) { //IL_00c3: 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_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) string text = "Unknown"; if ((Object)(object)punVoice != (Object)null && ((VoiceConnection)punVoice).Client != null) { text = ((LoadBalancingClient)((VoiceConnection)punVoice).Client).GameServerAddress; } if (string.IsNullOrEmpty(text)) { text = "Disconnected"; } BroadcastLog(L.Get("log_sos_send") + " -> " + L.Get("log_sos_target") + ":" + targetInfo + " | " + L.Get("log_sos_local") + ":" + text); object[] array = new object[3] { (byte)0, targetInfo, text }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)0 }; PhotonNetwork.RaiseEvent((byte)186, (object)array, val, SendOptions.SendReliable); } public static void OnEvent(EventData photonEvent) { if (photonEvent.Code != 186) { return; } int senderActor = photonEvent.Sender; string playerName = GetPlayerName(senderActor); if (!(photonEvent.CustomData is object[] array) || array.Length < 2) { return; } byte b = 0; if (array[0] is byte b2) { b = b2; } else if (array[0] is int num) { b = (byte)num; } switch (b) { case 1: { string msg = array[1] as string; if ((Object)(object)VoiceUIManager.Instance != (Object)null) { VoiceUIManager.Instance.AddLog(playerName, msg, isLocal: false); } break; } case 0: { string text3 = array[1] as string; string originIP = "Unknown(old)"; if (array.Length >= 3 && array[2] is string text4) { originIP = text4; } else if (PlayerCache.ContainsKey(senderActor)) { originIP = PlayerCache[senderActor].IP; } ActiveSOSList.RemoveAll((SOSData x) => x.ActorNumber == senderActor); ActiveSOSList.Add(new SOSData { ActorNumber = senderActor, PlayerName = playerName, TargetIP = text3, OriginIP = originIP, ReceiveTime = Time.unscaledTime }); if ((Object)(object)VoiceUIManager.Instance != (Object)null) { VoiceUIManager.Instance.AddLog("System", L.Get("log_sos_received", playerName, text3), isLocal: true); VoiceUIManager.Instance.TriggerNotification(playerName); } break; } case 2: { byte remoteState = 0; if (array[1] is byte b3) { remoteState = b3; } else if (array[1] is int num2) { remoteState = (byte)num2; } string text = ""; if (array.Length >= 3 && array[2] is string text2) { text = text2; } if (PlayerCache.ContainsKey(senderActor)) { PlayerCache[senderActor].RemoteState = remoteState; if (!string.IsNullOrEmpty(text)) { PlayerCache[senderActor].ModVersion = text; } PlayerCache[senderActor].LastSeenTime = Time.unscaledTime; } else { CacheEntry value = new CacheEntry { PlayerName = playerName, LastSeenTime = Time.unscaledTime, RemoteState = remoteState, ModVersion = text }; PlayerCache[senderActor] = value; } break; } } } private static void HandleInputAndState() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Invalid comparison between Unknown and I4 if (VoiceFix.EnableManualReconnect == null || !VoiceFix.EnableManualReconnect.Value || (!Input.GetKey((KeyCode)308) && !Input.GetKey((KeyCode)307)) || !Input.GetKeyDown((KeyCode)107)) { return; } if ((Object)(object)punVoice == (Object)null || ((VoiceConnection)punVoice).Client == null) { BroadcastLog("[System] Alt+K ignored: Voice client not ready."); return; } if ((int)((LoadBalancingClient)((VoiceConnection)punVoice).Client).State == 9 || (int)((LoadBalancingClient)((VoiceConnection)punVoice).Client).State == 6 || (int)((LoadBalancingClient)((VoiceConnection)punVoice).Client).State == 1) { BroadcastLog(L.Get("log_alt_k_disconnect")); if (PhotonNetwork.IsConnectedAndReady) { SendSOS(L.Get("log_sos_manual")); } ((LoadBalancingClient)((VoiceConnection)punVoice).Client).Disconnect(); if ((Object)(object)VoiceUIManager.Instance != (Object)null) { VoiceUIManager.Instance.ShowStatsTemporary(); } } else { BroadcastLog(L.Get("log_alt_k_reconnect")); SetGameServerAddress(ip: TargetGameServer = DecideTargetIP(out var _), client: (LoadBalancingClient)(object)((VoiceConnection)punVoice).Client); nextRetryTime = Time.unscaledTime; ConnectionFailCount = 0; ((VoiceConnection)punVoice).ConnectUsingSettings(PhotonNetwork.PhotonServerSettings.AppSettings); } WrongIPCount = 0; TotalRetryCount = 0; } private static void ResetRoomScopedState() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) PlayerCache.Clear(); ActiveSOSList.Clear(); HostHistory.Clear(); TargetGameServer = null; ConnectedUsingHost = true; IsBlindConnect = false; WrongIPCount = 0; ConnectionFailCount = 0; TotalRetryCount = 0; LastErrorMessage = ""; LastKnownHostIP = ""; LastHostUpdateTime = 0f; LastScanTime = 0f; LastDecisionLog = ""; lastClientState = (ClientState)14; nextRetryTime = 0f; lastPingPublishTime = 0f; lastSOSTime = 0f; nextSummaryLogTime = 0f; } } [HarmonyPatch] public static class PhotonRPCFix { [HarmonyPatch(typeof(PhotonNetwork), "RPC", new Type[] { typeof(PhotonView), typeof(string), typeof(RpcTarget), typeof(Player), typeof(bool), typeof(object[]) })] [HarmonyPrefix] public static void PrePhotonNetworkRPC(PhotonView view, string methodName, ref RpcTarget target) { if (methodName == "RemoveSkeletonRPC" && (int)target == 3) { target = (RpcTarget)0; } } } public enum UIPositionEnum { Left, Right } [BepInPlugin("chuxiaaaa.Aiae.BetterPeakVoiceFix", "BetterPeakVoiceFix", "1.0.2")] public class VoiceFix : BaseUnityPlugin { public static VoiceFix Instance; public static ManualLogSource logger; public static ManualLogSource debugLogger; public static ConfigEntry Language; public static ConfigEntry UIPositionSide; public static ConfigEntry ToggleUIKey; public static ConfigEntry ShowProfessionalInfo; public static ConfigEntry OffsetX_Right; public static ConfigEntry OffsetY_Right; public static ConfigEntry OffsetX_Left; public static ConfigEntry OffsetY_Left; public static ConfigEntry FontSize; public static ConfigEntry HostSymbol; public static ConfigEntry ConnectTimeout; public static ConfigEntry RetryInterval; public static ConfigEntry EnableManualReconnect; public static ConfigEntry MaxTotalLength; public static ConfigEntry LatencyOffset; public static ConfigEntry AutoHideNormal; public static ConfigEntry ShowPingInNormal; public static ConfigEntry HideOnMenu; public static ConfigEntry EnableVirtualTestPlayer; public static ConfigEntry TestPlayerName; public const string PLUGIN_VERSION = "1.0.2"; public const string MOD_VERSION = "v1.0.2"; public static KeyCode GetToggleKey() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (Enum.TryParse(ToggleUIKey.Value, ignoreCase: true, out KeyCode result)) { return result; } return (KeyCode)106; } private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Expected O, but got Unknown Instance = this; logger = ((BaseUnityPlugin)this).Logger; debugLogger = new ManualLogSource("VoiceFixDebug"); Logger.Sources.Add((ILogSource)(object)debugLogger); string text = L.DetectDefault(); L.Init(text); Language = ((BaseUnityPlugin)this).Config.Bind("Language", "语言-重启生效 | Language - need restart", text, new ConfigDescription(L.Get("cfg_language"), (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "中文", "English" }), Array.Empty())); L.Init(Language.Value); string text2 = L.Get("cfg_cat_ui"); ConfigFile config = ((BaseUnityPlugin)this).Config; string text3 = L.Get("cfgn_toggle_key"); KeyCode val = (KeyCode)106; ToggleUIKey = config.Bind(text2, text3, ((object)(KeyCode)(ref val)).ToString(), L.Get("cfg_toggle_key")); UIPositionSide = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_ui_position"), UIPositionEnum.Right, new ConfigDescription(L.Get("cfg_ui_position"), (AcceptableValueBase)null, Array.Empty())); ShowProfessionalInfo = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_show_pro"), false, L.Get("cfg_show_pro")); OffsetX_Right = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_offset_x_r"), 20f, L.Get("cfg_offset_x_r")); OffsetY_Right = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_offset_y_r"), 20f, L.Get("cfg_offset_y_r")); OffsetX_Left = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_offset_x_l"), 20f, L.Get("cfg_offset_x_l")); OffsetY_Left = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_offset_y_l"), 20f, L.Get("cfg_offset_y_l")); FontSize = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_font_size"), 21f, L.Get("cfg_font_size")); HostSymbol = ((BaseUnityPlugin)this).Config.Bind(text2, L.Get("cfgn_host_symbol"), "★", L.Get("cfg_host_symbol")); string text4 = L.Get("cfg_cat_net"); ConnectTimeout = ((BaseUnityPlugin)this).Config.Bind(text4, L.Get("cfgn_timeout"), 25f, L.Get("cfg_timeout")); RetryInterval = ((BaseUnityPlugin)this).Config.Bind(text4, L.Get("cfgn_retry_interval"), 8f, L.Get("cfg_retry_interval")); EnableManualReconnect = ((BaseUnityPlugin)this).Config.Bind(text4, L.Get("cfgn_manual_reconnect"), true, L.Get("cfg_manual_reconnect")); string text5 = L.Get("cfg_cat_adv"); MaxTotalLength = ((BaseUnityPlugin)this).Config.Bind(text5, L.Get("cfgn_max_name_len"), 26, new ConfigDescription(L.Get("cfg_max_name_len"), (AcceptableValueBase)(object)new AcceptableValueRange(10, 60), Array.Empty())); LatencyOffset = ((BaseUnityPlugin)this).Config.Bind(text5, L.Get("cfgn_latency_offset"), 350f, L.Get("cfg_latency_offset")); AutoHideNormal = ((BaseUnityPlugin)this).Config.Bind(text5, L.Get("cfgn_auto_hide"), true, L.Get("cfg_auto_hide")); ShowPingInNormal = ((BaseUnityPlugin)this).Config.Bind(text5, L.Get("cfgn_show_ping"), true, L.Get("cfg_show_ping")); HideOnMenu = ((BaseUnityPlugin)this).Config.Bind(text5, L.Get("cfgn_hide_menu"), true, L.Get("cfg_hide_menu")); EnableVirtualTestPlayer = ((BaseUnityPlugin)this).Config.Bind(text5, L.Get("cfgn_virtual_player"), false, L.Get("cfg_virtual_player")); TestPlayerName = ((BaseUnityPlugin)this).Config.Bind(text5, L.Get("cfgn_virtual_name"), "1234567891012141618202224262830323436", L.Get("cfg_virtual_name")); Harmony.CreateAndPatchAll(typeof(LoadBalancingClientPatch), (string)null); Harmony.CreateAndPatchAll(typeof(PhotonRPCFix), (string)null); VoiceUIManager.CreateGlobalInstance(); logger.LogInfo((object)"Better Voice Fix (v1.0.2) 已加载。"); } private void Update() { NetworkManager.SystemUpdate(); } } public class VoiceUIManager : MonoBehaviour { private struct LogEntry { public string Time; public string Player; public string Msg; public bool IsLocal; } private class PlayerRenderData { public string Name; public string IP; public int Ping; public bool IsLocal; public bool IsHost; public bool IsAlive; public bool HasModData; public bool IsInVoiceRoom; public int ActorNumber; public byte RemoteState; } public static VoiceUIManager Instance; public TextMeshProUGUI statsText; private Canvas myCanvas; private bool showDebugConsole = false; private Rect debugWindowRect = new Rect(20f, 20f, 600f, 400f); private Vector2 debugScrollPosition; private Vector2 filterScrollPosition; private List debugLogs = new List(); private int logFilterMode = 0; private int targetActorNumber = -1; private bool isResizing = false; private Rect resizeHandleRect; private Dictionary joinTimes = new Dictionary(); private const string C_GREEN = "#90EE90"; private const string C_PALE_GREEN = "#98FB98"; private const string C_YELLOW = "#F0E68C"; private const string C_RED = "#FF6961"; private const string C_LOW_SAT_RED = "#CD5C5C"; private const string C_TEXT = "#dfdac2"; private const string C_GREY = "#808080"; private const string C_GOLD = "#ffd700"; private const string C_GHOST_GREEN = "#b2d3b2"; private float nextUiUpdateTime = 0f; private bool isDetailMode = false; private float detailModeExpiry = 0f; private float lastFontRetryTime; private string notificationMsg = ""; private float notificationExpiry = 0f; private bool wasSinglePlayer = false; private float singlePlayerEnterTime = 0f; private float lastJoinTimesCleanup = 0f; public static void CreateGlobalInstance() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("BetterVoiceFix_UI"); Object.DontDestroyOnLoad((Object)(object)val); Canvas val2 = val.AddComponent(); val2.renderMode = (RenderMode)0; val2.sortingOrder = 9999; CanvasScaler val3 = val.AddComponent(); val3.uiScaleMode = (ScaleMode)1; val3.referenceResolution = new Vector2(1920f, 1080f); Instance = val.AddComponent(); Instance.myCanvas = val2; Instance.InitText(); } } private void InitText() { //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("StatusText"); val.transform.SetParent(((Component)this).transform, false); ContentSizeFitter val2 = val.AddComponent(); val2.horizontalFit = (FitMode)2; val2.verticalFit = (FitMode)2; statsText = val.AddComponent(); ((TMP_Text)statsText).richText = true; ((Graphic)statsText).raycastTarget = false; ((TMP_Text)statsText).overflowMode = (TextOverflowModes)0; ((TMP_Text)statsText).textWrappingMode = (TextWrappingModes)0; UpdateLayout(); TrySyncFontFromGame(); } public void AddLog(string player, string msg, bool isLocal) { if (debugLogs.Count > 300) { debugLogs.RemoveAt(0); } debugLogs.Add(new LogEntry { Time = DateTime.Now.ToString("HH:mm:ss"), Player = player, Msg = msg, IsLocal = isLocal }); debugScrollPosition.y = float.MaxValue; } private void OnGUI() { //IL_003a: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (showDebugConsole) { GUI.skin.window.normal.background = Texture2D.blackTexture; GUI.backgroundColor = new Color(0f, 0f, 0f, 0.85f); debugWindowRect = GUI.Window(999, debugWindowRect, new WindowFunction(DrawDebugWindow), L.Get("ui_debug_title")); } } private void DrawDebugWindow(int windowID) { //IL_0099: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0253: 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_0262: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: 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_0421: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_0447: Invalid comparison between Unknown and I4 //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0462: Invalid comparison between Unknown and I4 //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Expected O, but got Unknown //IL_0484: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(L.Get("btn_copy_all"), Array.Empty())) { ExportLogs(toFile: false); } if (GUILayout.Button(L.Get("btn_export"), Array.Empty())) { ExportLogs(toFile: true); } if (GUILayout.Button(L.Get("btn_clear"), Array.Empty())) { debugLogs.Clear(); } if (GUILayout.Button(L.Get("btn_dump"), Array.Empty())) { DumpVoicePlayers(); } GUILayout.EndHorizontal(); filterScrollPosition = GUILayout.BeginScrollView(filterScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button((logFilterMode == 0) ? ("[★" + L.Get("filter_all") + "]") : L.Get("filter_all"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { logFilterMode = 0; } if (GUILayout.Button((logFilterMode == 1) ? ("[★" + L.Get("filter_local") + "]") : L.Get("filter_local"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { logFilterMode = 1; } if (PhotonNetwork.InRoom) { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { if (!val.IsLocal) { string playerName = NetworkManager.GetPlayerName(val.ActorNumber); string text = ((playerName.Length > 9) ? playerName.Substring(0, 9) : playerName); string text2 = ((logFilterMode == -1 && targetActorNumber == val.ActorNumber) ? ("[★" + text + "]") : text); if (GUILayout.Button(text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { logFilterMode = -1; targetActorNumber = val.ActorNumber; } } } } GUILayout.EndHorizontal(); GUILayout.EndScrollView(); debugScrollPosition = GUILayout.BeginScrollView(debugScrollPosition, Array.Empty()); foreach (LogEntry debugLog in debugLogs) { if (logFilterMode == 1 && !debugLog.IsLocal) { continue; } if (logFilterMode == -1) { string playerName2 = NetworkManager.GetPlayerName(targetActorNumber); if (debugLog.Player != playerName2) { continue; } } string text3 = (debugLog.IsLocal ? "cyan" : "yellow"); if (debugLog.Player == "System") { text3 = "white"; } GUILayout.Label("[" + debugLog.Time + "] " + debugLog.Player + ": " + debugLog.Msg, new GUIStyle(GUI.skin.label) { richText = true }, Array.Empty()); } GUILayout.EndScrollView(); GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f)); resizeHandleRect = new Rect(((Rect)(ref debugWindowRect)).width - 20f, ((Rect)(ref debugWindowRect)).height - 20f, 20f, 20f); GUI.Label(resizeHandleRect, "◢"); Event current2 = Event.current; if ((int)current2.type == 0 && ((Rect)(ref resizeHandleRect)).Contains(current2.mousePosition)) { isResizing = true; } else if ((int)current2.type == 1) { isResizing = false; } else if ((int)current2.type == 3 && isResizing) { ref Rect reference = ref debugWindowRect; ((Rect)(ref reference)).width = ((Rect)(ref reference)).width + current2.delta.x; ref Rect reference2 = ref debugWindowRect; ((Rect)(ref reference2)).height = ((Rect)(ref reference2)).height + current2.delta.y; if (((Rect)(ref debugWindowRect)).width < 300f) { ((Rect)(ref debugWindowRect)).width = 300f; } if (((Rect)(ref debugWindowRect)).height < 200f) { ((Rect)(ref debugWindowRect)).height = 200f; } } } private void DumpVoicePlayers() { if ((Object)(object)NetworkManager.punVoice == (Object)null || ((VoiceConnection)NetworkManager.punVoice).Client == null || ((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).CurrentRoom == null) { AddLog("System", L.Get("client_not_connected"), isLocal: true); return; } StringBuilder stringBuilder = new StringBuilder(); Dictionary players = ((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).CurrentRoom.Players; stringBuilder.AppendLine(string.Format("=== {0} (Count: {1}) ===", L.Get("voice_player_list"), players.Count)); foreach (KeyValuePair item in players) { int key = item.Key; string playerName = NetworkManager.GetPlayerName(key); bool flag = NetworkManager.IsGhost(key); string text = "N/A"; string text2 = ""; if (NetworkManager.PlayerCache.ContainsKey(key)) { text = NetworkManager.PlayerCache[key].IP; text2 = NetworkManager.PlayerCache[key].ModVersion; } string text3 = (flag ? (" " + L.Get("ghost_tag")) : ""); string text4 = (string.IsNullOrEmpty(text2) ? "" : (" | Ver: " + text2)); stringBuilder.AppendLine($" - ID: {key} | Name: {playerName} | IP: {text}{text4}{text3}"); } AddLog("System", stringBuilder.ToString(), isLocal: true); } private void ExportLogs(bool toFile) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"=== Log Export ({DateTime.Now}) ==="); foreach (LogEntry debugLog in debugLogs) { stringBuilder.AppendLine("[" + debugLog.Time + "] " + debugLog.Player + ": " + debugLog.Msg); } if (toFile) { string text = Path.Combine(Paths.BepInExRootPath, "Log", "BetterVoiceFix_Dump.txt"); try { File.WriteAllText(text, stringBuilder.ToString()); AddLog("System", L.Get("exported_to") + ": " + text, isLocal: true); return; } catch (Exception ex) { AddLog("System", L.Get("export_failed") + ": " + ex.Message, isLocal: true); return; } } GUIUtility.systemCopyBuffer = stringBuilder.ToString(); AddLog("System", L.Get("copied"), isLocal: true); } private void Update() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) if (VoiceFix.ToggleUIKey == null) { return; } if (Input.GetKeyDown(VoiceFix.GetToggleKey())) { if (isDetailMode) { isDetailMode = false; detailModeExpiry = 0f; } else { isDetailMode = true; detailModeExpiry = Time.unscaledTime + 10f; } } if ((Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307)) && Input.GetKeyDown((KeyCode)106)) { showDebugConsole = !showDebugConsole; } if (isDetailMode && Time.unscaledTime > detailModeExpiry) { isDetailMode = false; } CleanupJoinTimes(); Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; bool flag = name == "Airport"; bool inRoom = PhotonNetwork.InRoom; bool flag2 = VoiceFix.HideOnMenu != null && VoiceFix.HideOnMenu.Value && Cursor.visible; bool flag3 = false; if (inRoom && !flag2) { if (isDetailMode) { flag3 = true; } else { bool flag4 = Time.unscaledTime < notificationExpiry; bool flag5 = VoiceFix.AutoHideNormal != null && VoiceFix.AutoHideNormal.Value && IsAllGood(); if ((flag || flag4) && (!flag5 || flag4)) { flag3 = true; } } } if (((Behaviour)myCanvas).enabled != flag3) { ((Behaviour)myCanvas).enabled = flag3; } if (!flag3) { return; } if ((Object)(object)((TMP_Text)statsText).font == (Object)null || ((Object)((TMP_Text)statsText).font).name.Contains("Liberation") || Time.unscaledTime - lastFontRetryTime > 2f) { lastFontRetryTime = Time.unscaledTime; TrySyncFontFromGame(); } if (Time.unscaledTime > nextUiUpdateTime) { if (isDetailMode) { UpdateContent_Detail(); } else { UpdateContent_Normal(); } UpdateLayout(); nextUiUpdateTime = Time.unscaledTime + 0.2f; } } public bool IsDetailModeActive() { return isDetailMode; } private void UpdateLayout() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0117: 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_013c: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)statsText == (Object)null) && VoiceFix.UIPositionSide != null) { bool flag = VoiceFix.UIPositionSide.Value == UIPositionEnum.Right; float num = (flag ? Mathf.Abs(VoiceFix.OffsetX_Right.Value) : Mathf.Abs(VoiceFix.OffsetX_Left.Value)); float num2 = (flag ? Mathf.Abs(VoiceFix.OffsetY_Right.Value) : Mathf.Abs(VoiceFix.OffsetY_Left.Value)); RectTransform rectTransform = ((TMP_Text)statsText).rectTransform; if (flag) { rectTransform.anchorMin = new Vector2(1f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(1f, 1f); rectTransform.anchoredPosition = new Vector2(0f - num, 0f - num2); ((TMP_Text)statsText).alignment = (TextAlignmentOptions)257; } else { rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(0f, 1f); rectTransform.pivot = new Vector2(0f, 1f); rectTransform.anchoredPosition = new Vector2(num, 0f - num2); ((TMP_Text)statsText).alignment = (TextAlignmentOptions)257; } ((TMP_Text)statsText).fontSize = VoiceFix.FontSize.Value; } } public void TriggerNotification(string playerName) { notificationMsg = "" + playerName + ": " + FormatStatusTag(L.Get("notification_disconnected"), "#F0E68C"); notificationExpiry = Time.unscaledTime + 5f; } public void ShowStatsTemporary() { notificationMsg = "" + L.Get("manual_operation") + ""; notificationExpiry = Time.unscaledTime + 5f; } private string GetLocalizedState(ClientState state) { //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_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 ClientState val = state; ClientState val2 = val; if ((int)val2 != 9) { if ((int)val2 == 14) { return L.Get("ls_disconnected"); } return ((object)(ClientState)(ref state)).ToString(); } return L.Get("ls_joined"); } private string GetMyStateRaw(out string color) { int joined = 0; int total = 0; GetVoiceCounts(out joined, out total); if (IsVoiceConnected()) { if (joined > 1) { color = "#90EE90"; return L.Get("state_synced"); } if (joined == 1 && PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount > 1) { color = "#F0E68C"; return L.Get("state_isolated"); } color = "#90EE90"; return L.Get("state_synced"); } if (IsConnectingLocal()) { color = "#F0E68C"; return L.Get("state_connecting"); } color = "#FF6961"; return L.Get("state_disconnected"); } private void AppendCommonStats(StringBuilder sb, bool forceShow) { //IL_011c: Unknown result type (might be due to invalid IL or missing references) bool flag = PhotonNetwork.OfflineMode || (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount <= 1); if (flag) { if (!wasSinglePlayer) { singlePlayerEnterTime = Time.unscaledTime; wasSinglePlayer = true; } } else { wasSinglePlayer = false; } if (!(!forceShow && flag) || !(Time.unscaledTime > singlePlayerEnterTime + 10f)) { if (flag) { sb.Append("" + L.Get("voice_single_player") + "\n"); } else { string color; string myStateRaw = GetMyStateRaw(out color); string text = myStateRaw; if ((Object)(object)NetworkManager.punVoice != (Object)null && ((VoiceConnection)NetworkManager.punVoice).Client != null && myStateRaw != L.Get("state_synced") && myStateRaw != L.Get("state_isolated")) { text = GetLocalizedState(((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).State); } sb.Append("" + L.Get("voice_local") + ""); if (IsVoiceConnected()) { if (PhotonNetwork.IsMasterClient) { sb.Append("" + L.Get("voice_connected") + ""); } else { sb.Append("" + L.Get("voice_connected") + " " + FormatStatusTag(myStateRaw, color)); } } else { sb.Append("" + text + ""); } sb.Append("\n"); GetVoiceCounts(out var joined, out var total); int ghostCount = NetworkManager.GetGhostCount(); int num = joined + ghostCount; if ((Object)(object)NetworkManager.punVoice != (Object)null && ((VoiceConnection)NetworkManager.punVoice).Client != null && ((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).CurrentRoom != null) { num = ((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).CurrentRoom.Players.Count; } int num2 = total; sb.Append("" + L.Get("voice_count") + ""); string arg = "#F0E68C"; if (num == 1 && num2 >= 3) { arg = "#FF6961"; } else if (ghostCount > 0) { arg = "#b2d3b2"; } else if (num == num2) { arg = "#90EE90"; } sb.Append($"{num}"); sb.Append(string.Format("/{2}", "#dfdac2", "#dfdac2", num2)); if (ghostCount > 0) { sb.Append(string.Format(" ({2} {4})", "#dfdac2", "#b2d3b2", ghostCount, "#dfdac2", L.Get("id_mismatch"))); } sb.Append("\n"); } if (VoiceFix.ShowPingInNormal != null && VoiceFix.ShowPingInNormal.Value) { int ping = PhotonNetwork.GetPing(); string text2 = ((ping < 100) ? "#90EE90" : ((ping < 200) ? "#F0E68C" : "#FF6961")); sb.Append(string.Format("{1}{3}ms\n", "#dfdac2", L.Get("local_ping"), text2, ping)); } } if (Time.unscaledTime < notificationExpiry) { sb.Append(notificationMsg + "\n"); } } private void UpdateContent_Normal() { StringBuilder stringBuilder = new StringBuilder(); AppendCommonStats(stringBuilder, forceShow: false); ((TMP_Text)statsText).text = stringBuilder.ToString(); } private void UpdateContent_Detail() { StringBuilder stringBuilder = new StringBuilder(); bool value = VoiceFix.ShowProfessionalInfo.Value; float value2 = VoiceFix.LatencyOffset.Value; stringBuilder.Append("" + L.Get("ui_title") + " (v1.0.2)\n"); stringBuilder.Append("------------------\n"); string currentIP = GetCurrentIP(); string color; string myStateRaw = GetMyStateRaw(out color); stringBuilder.Append("" + L.Get("local_server") + " "); if (!PhotonNetwork.IsMasterClient || !IsVoiceConnected()) { string text = FormatStatusTag(myStateRaw, color); stringBuilder.Append(text + " "); } if (value) { stringBuilder.Append(" " + currentIP + ""); } stringBuilder.Append("\n"); stringBuilder.Append("" + L.Get("host_server") + " "); if (PhotonNetwork.IsMasterClient) { GetVoiceCounts(out var joined, out var total); int num = total - joined; if (num < 0) { num = 0; } stringBuilder.Append("[" + L.Get("label_local") + "] "); string text2 = ((joined >= total) ? "#90EE90" : "#F0E68C"); stringBuilder.Append(string.Format("[{2}{4}/{5}] ", "#dfdac2", "#dfdac2", L.Get("label_sync"), text2, joined, total, "#dfdac2")); string text3 = ((num > 0) ? "#CD5C5C" : "#dfdac2"); stringBuilder.Append(string.Format("[{2}{4}]", "#dfdac2", "#dfdac2", L.Get("label_abnormal"), text3, num, "#dfdac2")); } else { Player masterClient = PhotonNetwork.MasterClient; string s = ((masterClient != null) ? NetworkManager.GetPlayerName(masterClient.ActorNumber) : L.Get("unknown")); string text4 = ""; if (masterClient != null && NetworkManager.PlayerCache.TryGetValue(masterClient.ActorNumber, out var value3)) { text4 = value3.IP; } stringBuilder.Append("[" + Truncate(s, 0, isHost: false) + "]"); if (!string.IsNullOrEmpty(text4)) { stringBuilder.Append(": " + text4 + ""); } } stringBuilder.Append("\n"); if (PhotonNetwork.IsMasterClient) { int c; string majorityIP = GetMajorityIP(out c); if (c >= 2 && !string.IsNullOrEmpty(majorityIP) && majorityIP != currentIP) { stringBuilder.Append("" + L.Get("warn_majority", c) + "\n"); } } int ghostCount = NetworkManager.GetGhostCount(); List list = new List(); if (VoiceFix.EnableVirtualTestPlayer != null && VoiceFix.EnableVirtualTestPlayer.Value) { string name = ((VoiceFix.TestPlayerName != null) ? VoiceFix.TestPlayerName.Value : "Test"); list.Add(new PlayerRenderData { Name = L.Get("virtual_player"), IP = "", Ping = 0, IsLocal = false, IsHost = false, IsAlive = true, HasModData = false, IsInVoiceRoom = false }); list.Add(new PlayerRenderData { Name = name, IP = currentIP, Ping = 50, IsLocal = false, IsHost = false, IsAlive = true, HasModData = true, IsInVoiceRoom = true }); } HashSet hashSet = new HashSet(); if (PhotonNetwork.PlayerList != null) { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { int actorNumber = val.ActorNumber; hashSet.Add(actorNumber); string text5 = ""; int ping = 0; bool hasModData = false; bool isLocal = val.IsLocal; bool isMasterClient = val.IsMasterClient; bool flag = false; flag = NetworkManager.IsPlayerInVoiceRoom(actorNumber); if (isLocal) { text5 = GetCurrentIP(); ping = PhotonNetwork.GetPing(); hasModData = true; flag = IsVoiceConnected(); } else { if (((Dictionary)(object)val.CustomProperties).TryGetValue((object)"PVF_IP", out object value4)) { text5 = (string)value4; } if (((Dictionary)(object)val.CustomProperties).TryGetValue((object)"PVF_Ping", out object value5)) { ping = (int)value5; } if (!string.IsNullOrEmpty(text5)) { hasModData = true; } } string playerName = NetworkManager.GetPlayerName(actorNumber); byte remoteState = 0; if (NetworkManager.PlayerCache.ContainsKey(actorNumber)) { remoteState = NetworkManager.PlayerCache[actorNumber].RemoteState; } list.Add(new PlayerRenderData { Name = playerName, IP = text5, Ping = ping, IsLocal = isLocal, IsHost = isMasterClient, IsAlive = true, HasModData = hasModData, IsInVoiceRoom = flag, ActorNumber = actorNumber, RemoteState = remoteState }); } } foreach (KeyValuePair item in NetworkManager.PlayerCache) { int key = item.Key; if (!hashSet.Contains(key) && !(Time.unscaledTime - item.Value.LastSeenTime > 5f)) { list.Add(new PlayerRenderData { Name = item.Value.PlayerName, IP = item.Value.IP, Ping = 0, IsLocal = false, IsHost = false, IsAlive = false, HasModData = true, IsInVoiceRoom = false, ActorNumber = key, RemoteState = item.Value.RemoteState }); } } list.Sort((PlayerRenderData a, PlayerRenderData b) => b.IsLocal.CompareTo(a.IsLocal)); stringBuilder.Append(""); foreach (PlayerRenderData item2 in list) { BuildPlayerEntry(stringBuilder, item2.Name, item2.IP, item2.Ping, item2.IsLocal, item2.IsHost, value, value2, item2.IsAlive, item2.HasModData, item2.IsInVoiceRoom, ghostCount > 0, item2.ActorNumber, item2.RemoteState); } stringBuilder.Append(""); stringBuilder.Append("------------------\n"); AppendCommonStats(stringBuilder, forceShow: true); if (NetworkManager.ActiveSOSList.Count > 0) { stringBuilder.Append("------------------\n"); stringBuilder.Append("" + L.Get("sos_snapshot") + "\n"); int c2; string majorityIP2 = GetMajorityIP(out c2); stringBuilder.Append(string.Format("{1}: {2} ({3}{4})\n", "#dfdac2", L.Get("sos_majority"), majorityIP2, c2, L.Get("sos_person"))); foreach (SOSData activeSOS in NetworkManager.ActiveSOSList) { stringBuilder.Append("" + L.Get("sos_detected", activeSOS.PlayerName) + "\n"); string text6 = (string.IsNullOrEmpty(activeSOS.OriginIP) ? L.Get("unknown") : activeSOS.OriginIP); stringBuilder.Append(" " + L.Get("sos_target") + ": (" + activeSOS.TargetIP + ") | " + L.Get("sos_last") + ": " + text6 + "\n"); } } if (value) { stringBuilder.Append("------------------\n"); int c3; string majorityIP3 = GetMajorityIP(out c3); float num2 = Time.unscaledTime - NetworkManager.LastScanTime; stringBuilder.Append(string.Format("{1} ({2:F0}{3})\n", "#dfdac2", L.Get("cache_snapshot"), num2, L.Get("seconds_ago"))); stringBuilder.Append(string.Format("{1} {3} ({5}{6})\n", "#dfdac2", L.Get("majority_server"), "#dfdac2", majorityIP3, "#dfdac2", c3, L.Get("sos_person"))); IEnumerable>> enumerable = from x in NetworkManager.PlayerCache group x by x.Value.IP; foreach (IGrouping> item3 in enumerable) { if (!(item3.Key == majorityIP3)) { string text7 = (string.IsNullOrEmpty(item3.Key) ? L.Get("not_connected") : item3.Key); IEnumerable values = item3.Select((KeyValuePair x) => x.Value.PlayerName).Take(3); string text8 = string.Join(",", values); stringBuilder.Append(" - " + text7 + ": " + text8 + "\n"); } } if (NetworkManager.HostHistory.Count > 0) { stringBuilder.Append("" + L.Get("history") + " " + NetworkManager.HostHistory[NetworkManager.HostHistory.Count - 1] + "\n"); } stringBuilder.Append(""); } ((TMP_Text)statsText).text = stringBuilder.ToString(); } private string GetClientStateLocalized(ClientState state) { //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_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected I4, but got Unknown ClientState val = state; ClientState val2 = val; return (int)val2 switch { 0 => L.Get("cs_initializing"), 1 => L.Get("cs_authenticating"), 2 => L.Get("cs_authenticated"), 8 => L.Get("cs_joining"), 9 => L.Get("cs_joined"), 13 => L.Get("cs_disconnecting"), 14 => L.Get("cs_disconnected"), 6 => L.Get("cs_connecting_game"), 12 => L.Get("cs_connecting_master"), 16 => L.Get("cs_connecting_name"), _ => ((object)(ClientState)(ref state)).ToString(), }; } private void BuildPlayerEntry(StringBuilder sb, string name, string ip, int ping, bool isLocal, bool isHost, bool pro, float alignX, bool isAlive, bool hasModData, bool isInVoiceRoom, bool hasGhosts, int actorNumber = -1, byte remoteState = 0) { //IL_0088: 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_008e: Invalid comparison between Unknown and I4 //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Invalid comparison between Unknown and I4 //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Invalid comparison between Unknown and I4 //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_05f4: Unknown result type (might be due to invalid IL or missing references) int num = 0; if (!hasModData) { string text = (isHost ? ("" + VoiceFix.HostSymbol.Value + " ") : ""); string text2 = L.Get("state_unknown"); string colorHex = "#dfdac2"; bool flag = false; string text3 = ""; if (isInVoiceRoom) { text2 = L.Get("state_connected"); colorHex = "#98FB98"; } else if (remoteState != 0) { ClientState val = (ClientState)remoteState; if ((int)val == 14 || (int)val == 13) { text2 = L.Get("state_disconnected"); colorHex = "#FF6961"; } else if ((int)val == 9) { text2 = L.Get("state_connected"); colorHex = "#98FB98"; } else { text2 = L.Get("state_connecting"); colorHex = "#F0E68C"; flag = true; text3 = GetClientStateLocalized(val); if (actorNumber != -1) { joinTimes[actorNumber] = Time.unscaledTime; } } } else if (actorNumber != -1) { if (!joinTimes.ContainsKey(actorNumber)) { joinTimes[actorNumber] = Time.unscaledTime; } float num2 = ((VoiceFix.ConnectTimeout != null) ? VoiceFix.ConnectTimeout.Value : 25f); if (Time.unscaledTime - joinTimes[actorNumber] < num2) { text2 = L.Get("state_connecting"); colorHex = "#F0E68C"; } else if (hasGhosts) { text2 = L.Get("state_mismatch"); colorHex = "#b2d3b2"; } else { text2 = L.Get("state_disconnected"); colorHex = "#FF6961"; } } num = 8; string text4 = Truncate(name, num, isHost); sb.Append(FormatStatusTag(text2, colorHex) + " " + text + "" + text4 + "\n"); if (flag && pro) { sb.Append(" » " + text3 + "\n"); } return; } bool flag2 = !string.IsNullOrEmpty(ip); string text5 = ""; string text6 = "#90EE90"; bool flag3 = false; if (!flag2) { if (isAlive && ping > 0) { text5 = L.Get("state_connecting"); text6 = "#F0E68C"; flag3 = true; num = 8; } else { text5 = L.Get("state_disconnected"); text6 = "#FF6961"; num = 6; } } else if (IsIPMatch(ip)) { text5 = L.Get("state_synced"); text6 = "#90EE90"; num = 6; } else { text5 = L.Get("state_abnormal"); text6 = "#CD5C5C"; num = 6; } if (isLocal) { num = 6; if (IsConnectingLocal()) { flag3 = true; } } if (!isAlive && !isLocal) { text5 = L.Get("state_left"); text6 = "#808080"; num = 6; } string text7 = FormatStatusTag(text5, text6); string text8 = (isHost ? ("" + VoiceFix.HostSymbol.Value + " ") : ""); if (isLocal) { text7 = "[" + L.Get("label_local") + "]"; } string text9 = Truncate(name, num, isHost); string text10 = ((!isAlive && !isLocal) ? "#808080" : "#90EE90"); string text11 = ""; string text12 = ((ping < 100) ? "#90EE90" : ((ping < 200) ? "#F0E68C" : "#FF6961")); if (ping > 0 || (isAlive && string.IsNullOrEmpty(ip))) { text11 = string.Format("| {2}:{4}ms", alignX, "#dfdac2", L.Get("detail_latency"), text12, ping); } sb.Append(text7 + " " + text8 + "" + text9 + "" + text11 + "\n"); if (pro && !isLocal) { string text13 = ip; string text14 = L.Get("state_connected"); if (remoteState != 0 && remoteState != 9 && remoteState != 14) { text13 = GetClientStateLocalized((ClientState)remoteState); text14 = L.Get("state_status"); sb.Append(" » " + text13 + "\n"); return; } string text15 = (flag3 ? L.Get("detail_waiting_data") : (string.IsNullOrEmpty(ip) ? "N/A" : ip)); if (flag3 && string.IsNullOrEmpty(ip)) { text15 = "" + L.Get("detail_fetching") + ""; } string text16 = (flag3 ? L.Get("detail_connecting_to") : L.Get("detail_joined_voice")); sb.Append(" » " + text16 + ": " + text15 + "\n"); } else if (pro && isLocal && flag3) { string text17 = L.Get("detail_connecting_local"); if ((Object)(object)NetworkManager.punVoice != (Object)null && ((VoiceConnection)NetworkManager.punVoice).Client != null) { text17 = GetClientStateLocalized(((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).State); } sb.Append(" » " + text17 + "\n"); } } private void GetVoiceCounts(out int joined, out int total) { joined = 0; total = 0; if (PhotonNetwork.PlayerList == null) { return; } total = PhotonNetwork.PlayerList.Length; if (total <= 1) { joined = (IsVoiceConnected() ? 1 : 0); return; } if ((Object)(object)NetworkManager.punVoice != (Object)null && ((VoiceConnection)NetworkManager.punVoice).Client != null && ((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).CurrentRoom != null) { foreach (KeyValuePair player in ((LoadBalancingClient)((VoiceConnection)NetworkManager.punVoice).Client).CurrentRoom.Players) { if (!NetworkManager.IsGhost(player.Key)) { joined++; } } return; } Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { CacheEntry value; if (val.IsLocal) { if (IsVoiceConnected()) { joined++; } } else if (NetworkManager.PlayerCache.TryGetValue(val.ActorNumber, out value) && !string.IsNullOrEmpty(value.IP)) { joined++; } } } private string FormatStatusTag(string text, string colorHex) { return "[" + text + "]"; } private LoadBalancingClient GetVoiceClient() { if ((Object)(object)NetworkManager.punVoice != (Object)null) { return (LoadBalancingClient)(object)((VoiceConnection)NetworkManager.punVoice).Client; } GameObject val = GameObject.Find("VoiceClient"); if ((Object)(object)val != (Object)null) { PunVoiceClient component = val.GetComponent(); if ((Object)(object)component != (Object)null) { return (LoadBalancingClient)(object)((VoiceConnection)component).Client; } } return null; } private string GetCurrentIP() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 LoadBalancingClient voiceClient = GetVoiceClient(); return (voiceClient != null && (int)voiceClient.State == 9) ? voiceClient.GameServerAddress : ""; } private bool IsVoiceConnected() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 LoadBalancingClient voiceClient = GetVoiceClient(); return voiceClient != null && (int)voiceClient.State == 9; } private bool IsConnectingLocal() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 LoadBalancingClient voiceClient = GetVoiceClient(); return voiceClient != null && ((int)voiceClient.State == 6 || (int)voiceClient.State == 1); } private bool IsMismatch() { if (NetworkManager.WrongIPCount > 2) { return true; } string targetGameServer = NetworkManager.TargetGameServer; string currentIP = GetCurrentIP(); return !string.IsNullOrEmpty(targetGameServer) && !string.IsNullOrEmpty(currentIP) && targetGameServer != currentIP; } private bool IsIPMatch(string otherIP) { return otherIP == GetCurrentIP(); } private string GetMajorityIP(out int c) { return NetworkManager.GetMajorityIP(out c); } private bool IsAllGood() { if (!IsVoiceConnected() || IsMismatch() || NetworkManager.TotalRetryCount > 0) { return false; } GetVoiceCounts(out var joined, out var total); int ghostCount = NetworkManager.GetGhostCount(); return joined >= total && ghostCount == 0; } private string Truncate(string s, int prefixWeight, bool isHost) { if (string.IsNullOrEmpty(s)) { return ""; } int num = 26; if (VoiceFix.MaxTotalLength != null) { num = VoiceFix.MaxTotalLength.Value; } int num2 = num - prefixWeight; if (num2 < 6) { num2 = 6; } if (isHost) { num2 -= 2; } int num3 = 0; for (int i = 0; i < s.Length; i++) { int num4 = ((s[i] <= 'ÿ') ? 1 : 2); if (num3 + num4 > num2) { return s.Substring(0, i) + "..."; } num3 += num4; } return s; } private void CleanupJoinTimes() { if (Time.unscaledTime - lastJoinTimesCleanup < 60f) { return; } lastJoinTimesCleanup = Time.unscaledTime; if (PhotonNetwork.CurrentRoom == null) { joinTimes.Clear(); return; } List list = new List(); foreach (KeyValuePair joinTime in joinTimes) { if (PhotonNetwork.CurrentRoom.GetPlayer(joinTime.Key, false) == null) { list.Add(joinTime.Key); } } foreach (int item in list) { joinTimes.Remove(item); } } private void TrySyncFontFromGame() { if (!((Object)(object)statsText == (Object)null)) { PlayerConnectionLog val = Object.FindFirstObjectByType(); if ((Object)(object)val != (Object)null && (Object)(object)val.text != (Object)null) { ((TMP_Text)statsText).font = ((TMP_Text)val.text).font; ((TMP_Text)statsText).fontSharedMaterial = ((TMP_Text)val.text).fontSharedMaterial; } } } } } namespace PeakVoiceFix.Patches { [HarmonyPatch(typeof(LoadBalancingClient))] public class LoadBalancingClientPatch { [HarmonyPatch("OnEvent")] [HarmonyPrefix] public static void OnEventPrefix(EventData photonEvent) { if (photonEvent.Code == 186) { NetworkManager.OnEvent(photonEvent); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }