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 LateJoinNow v1.0.9
Zichen-LateJoinNow-1.0.9.dll
Decompiled 11 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using Zichen_LateJoinNow.Common; using Zichen_LateJoinNow.I18N; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Zichen-LateJoinNow-1.0.9")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+1e059071d3b2bcf585769b06a3b726d514a70e6a")] [assembly: AssemblyProduct("Zichen-LateJoinNow-1.0.9")] [assembly: AssemblyTitle("Zichen-LateJoinNow-1.0.9")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Zichen_LateJoinNow { [BepInPlugin("zichen.latejoinnow", "LateJoinNow", "1.0.9")] public class Plugin : BaseUnityPlugin { private sealed class StateSyncEntry { public string TypeName; public string FieldName; public string RpcName; public bool CastToInt; public Type ResolvedType; public FieldInfo ResolvedField; public bool ResolveAttempted; } internal enum LateJoinState { Pending, Queued, Processing, Finalizing, Done, Aborted } internal sealed class LateJoinEntry { public Player Target; public int ActorNumber; public string NickName; public LateJoinState State; public string StartScene; } private delegate void OwnershipUpdateDelegate(int[] viewOwnerPairs, int targetActor); public const string PluginGuid = "zichen.latejoinnow"; public const string PluginName = "LateJoinNow"; public const string PluginVersion = "1.0.9"; internal static Plugin Instance; internal static Harmony harmony; private I18NRegistry _i18n; private I18NSwitcher _i18nSwitcher; private static ConfigEntry<bool> _cfgModEnabled; private static ConfigEntry<bool> _cfgPublicRoomPrefix; private static ConfigEntry<bool> _cfgTeleportToHostOnJoin; private static ConfigEntry<bool> _cfgCoopMode; private static ConfigEntry<bool> _cfgAllowLobby; private static ConfigEntry<bool> _cfgAllowShop; private static ConfigEntry<bool> _cfgAllowLevel; private static ConfigEntry<bool> _cfgHudEnabled; private static ConfigEntry<bool> _cfgShowJoinStatus; private static ConfigEntry<bool> _cfgHudShowRoomType; private static ConfigEntry<bool> _cfgHudShowRoomName; private static ConfigEntry<bool> _cfgHudShowPlayerCount; private static ConfigEntry<int> _cfgHudFontSize; private static ConfigEntry<int> _cfgHudOpacity; private static ConfigEntry<bool> _cfgVerboseLog; private static ConfigEntry<bool> _cfgShowConflictWarning; private float _enforceTimer; private const float EnforceInterval = 1f; private static readonly FieldRef<RoundDirector, bool> _roundAllExtractionPointsCompletedRef = TryFieldRef<RoundDirector, bool>("allExtractionPointsCompleted"); private const float SceneAllowedCacheSeconds = 0.5f; private static float _sceneAllowedCacheUntil; private static bool _sceneAllowedCachedValue; private static float _inGameCacheUntil; private static bool _inGameCachedValue; private static bool _clientLateJoinFixesDone; private static bool _clientLateJoinCoroutineRunning; private static FieldInfo _physGrabSpawnedField; private static Type _physGrabHingeType; private static GUIStyle _hudLabelStyle; private static GUIStyle _hudShadowStyle; private static int _cachedFontSize = -1; private static int _cachedOpacity = -1; private static readonly List<string> _hudLines = new List<string>(3); private const float HudRefreshInterval = 1f; private static float _hudNextRefreshAt; private static bool _hudCacheUseChinese; private static bool _hudCacheShowPlayerCount; private static bool _hudCacheShowRoomType; private static bool _hudCacheShowRoomName; private static int _hudCachePlayerCount; private static int _hudCacheMaxPlayers; private static bool _hudCacheLobbyTypeKnown; private static LobbyTypes _hudCacheLobbyType; private static string _hudCacheServerName; private static bool _hudCacheRoomOpen; private static readonly List<string> _emptyList = new List<string>(0); private static readonly List<string> _lateJoinStatusLines = new List<string>(4); private static readonly List<LateJoinEntry> _hudEntriesScratch = new List<LateJoinEntry>(8); private static readonly Comparison<LateJoinEntry> _entrySortDesc = (LateJoinEntry a, LateJoinEntry b) => b.ActorNumber.CompareTo(a.ActorNumber); private const float StatusLinesRefreshSeconds = 0.25f; private static float _statusLinesNextRefreshAt; private static int _statusLinesLastEntryCount = -1; private static int _statusLinesLastCoopMask; private static readonly FieldRef<PlayerAvatar, string> _avatarPlayerNameRef = TryAvatarPlayerNameRef(); private static readonly StateSyncEntry[] _stateSyncEntries = new StateSyncEntry[22] { S("ExtractionPoint", "StateSetRPC", castToInt: false), S("ShopKeycardDoor", "StateSetRPC"), S("UpgradeStand", "StateSetRPC"), S("ItemValuableBox", "StateSetRPC"), S("ItemMine", "StateSetRPC", castToInt: true, "state"), S("ItemMelee", "StateSetRPC"), S("ItemGun", "StateSetRPC", castToInt: true, "stateCurrent"), S("ItemDrone", "StateSetRPC"), S("ItemVehicle", "SetStateRPC"), S("ItemCartCannonMain", "StateSetRPC", castToInt: true, "stateCurrent"), S("FanTrap", "SetStateRPC", castToInt: false), S("CrystalBallValuable", "SetStateRPC", castToInt: false), S("BlenderValuable", "SetStateRPC", castToInt: false), S("FlamethrowerValuable", "SetStateRPC", castToInt: false), S("IceSawValuable", "SetStateRPC", castToInt: false), S("FireExtinguisherValuable", "SetStateRPC", castToInt: false), S("ScreamDollValuable", "SetStateRPC", castToInt: false), S("JackhammerValuable", "SetStateRPC", castToInt: false), S("TrafficLightValuable", "SetStateRPC", castToInt: false), S("ValuableWizardTimeGlass", "SetStateRPC", castToInt: false), S("ValuableArcticSnowBike", "SetStateRPC"), S("ValuableEgg", "SetStateRPC", castToInt: false) }; private static FieldInfo _epIsLockedFieldInfo; private static readonly Dictionary<int, LateJoinEntry> _lateJoinEntries = new Dictionary<int, LateJoinEntry>(); private static readonly Queue<int> _lateJoinQueue = new Queue<int>(); private static bool _schedulerRunning; private static readonly FieldRef<PlayerAvatar, PlayerDeathHead> _playerDeathHeadRef = AccessTools.FieldRefAccess<PlayerAvatar, PlayerDeathHead>("playerDeathHead"); private static readonly FieldRef<PlayerAvatar, PlayerTumble> _playerTumbleRef = AccessTools.FieldRefAccess<PlayerAvatar, PlayerTumble>("tumble"); private static readonly FieldRef<PlayerAvatar, bool> _avatarAnimCompleted = TryAvatarAnimCompletedRef(); private static readonly FieldRef<Module, bool> _modTop = AccessTools.FieldRefAccess<Module, bool>("ConnectingTop"); private static readonly FieldRef<Module, bool> _modBottom = AccessTools.FieldRefAccess<Module, bool>("ConnectingBottom"); private static readonly FieldRef<Module, bool> _modRight = AccessTools.FieldRefAccess<Module, bool>("ConnectingRight"); private static readonly FieldRef<Module, bool> _modLeft = AccessTools.FieldRefAccess<Module, bool>("ConnectingLeft"); private static readonly FieldRef<Module, bool> _modFirst = AccessTools.FieldRefAccess<Module, bool>("First"); private static readonly FieldRef<Module, bool> _modDone = AccessTools.FieldRefAccess<Module, bool>("SetupDone"); private static readonly FieldRef<ValuableObject, bool> _valSet = AccessTools.FieldRefAccess<ValuableObject, bool>("dollarValueSet"); private static readonly FieldRef<ValuableObject, float> _valCurrent = AccessTools.FieldRefAccess<ValuableObject, float>("dollarValueCurrent"); private static readonly FieldRef<ValuableObject, bool> _valDisc = AccessTools.FieldRefAccess<ValuableObject, bool>("discovered"); private static readonly FieldRef<ValuableObject, bool> _valHaul = AccessTools.FieldRefAccess<ValuableObject, bool>("inStartRoom"); private static readonly FieldRef<MenuPageLobby, List<string>> _menuLobbyJoiningPlayersRef = TryMenuLobbyListRef("joiningPlayers"); private static readonly FieldRef<MenuPageLobby, float> _menuLobbyJoiningTimerRef = TryMenuLobbyFloatRef("joiningPlayersTimer"); private static readonly FieldRef<MenuPageLobby, float> _menuLobbyJoiningEndTimerRef = TryMenuLobbyFloatRef("joiningPlayersEndTimer"); private static readonly MethodInfo _ownershipUpdateMethod = typeof(PhotonNetwork).GetMethod("OwnershipUpdate", BindingFlags.Static | BindingFlags.NonPublic); private static readonly OwnershipUpdateDelegate _ownershipUpdate = ((_ownershipUpdateMethod != null) ? ((OwnershipUpdateDelegate)Delegate.CreateDelegate(typeof(OwnershipUpdateDelegate), _ownershipUpdateMethod)) : null); private static readonly int[] _ownershipScratch = new int[2]; private static bool _refreshOldPlayersBusy; private static readonly List<(PlayerAvatar av, int ownerActor)> _oldAvatarsScratch = new List<(PlayerAvatar, int)>(8); private static readonly WaitForSecondsRealtime _wait02 = new WaitForSecondsRealtime(0.2f); private static readonly WaitForSecondsRealtime _wait05 = new WaitForSecondsRealtime(0.5f); private static readonly WaitForSecondsRealtime _wait1 = new WaitForSecondsRealtime(1f); private static readonly WaitForSecondsRealtime _wait2 = new WaitForSecondsRealtime(2f); private static readonly WaitForSecondsRealtime _wait25 = new WaitForSecondsRealtime(2.5f); private static readonly WaitForSecondsRealtime _wait15 = new WaitForSecondsRealtime(1.5f); private const int ModuleBatchSize = 8; private const int ValuableBatchSize = 6; private static readonly FieldRef<ItemAttributes, int> _itemValueRef = TryItemValueRef(); private const int ItemBatchSize = 8; private static readonly int[] _projectileOwnershipScratch = new int[2]; private const float ChangeLevelCooldownSeconds = 0f; private const float WaitGeneratedTimeoutSeconds = 60f; private const float WaitGeneratedPollInterval = 0.2f; private const float CooldownLockedSentinelSeconds = 600f; private const string RoomNamePrefixCN = "[中途加入] "; private const string RoomNamePrefixEN = "[Late Join] "; private static readonly FieldRef<DataDirector, string> _networkServerNameRef = TryFieldRefRoom(); private static readonly FieldRef<RunManager, List<PlayerVoiceChat>> _runManagerVoiceChatsRef = TryFieldRefRM(); private static readonly HashSet<int> _scenePersistentViewIds = new HashSet<int>(64); private static float _changeLevelCooldownUntil; private static bool _changeLevelCooldownRunning; private static bool? _lastSceneAllowed; private static readonly WaitForSecondsRealtime _waitGeneratedPoll = new WaitForSecondsRealtime(0.2f); private static bool _steamLobbyLocked; private static float _steamUnlockEnsureNextAt; private const float SteamUnlockEnsureInterval = 5f; private static FieldInfo _steamPrivateLobbyField; private static FieldInfo _steamCurrentLobbyField; private static PropertyInfo _steamLobbyIdProp; private static PropertyInfo _steamLobbyIdIsValidProp; private static MethodInfo _steamJoinLobbyMethod; private static FieldInfo _steamIdValueField; internal const int CoopProtocolVersion = 1; internal const string CoopVersionKey = "LateJoinNow.Version"; internal const string CoopProtoKey = "LateJoinNow.Protocol"; internal const string CoopReadyKey = "LateJoinNow.Ready"; internal const string CoopModuleCountKey = "LateJoinNow.ModuleCount"; private static readonly Dictionary<int, long> _coopReadyByActor = new Dictionary<int, long>(8); private static readonly Dictionary<int, int> _coopModuleCountByActor = new Dictionary<int, int>(8); private static readonly Dictionary<int, bool> _coopByActor = new Dictionary<int, bool>(8); private static bool _hostCoopCompatible; private static PeerCoopCallbackListener _coopListener; private static readonly Hashtable _presencePropsScratch = new Hashtable(); private static readonly Hashtable _readyPropsScratch = new Hashtable(); private static readonly Hashtable _clearPropsScratch = new Hashtable(); public static bool PublicRoomPrefixEnabled { get { if (_cfgPublicRoomPrefix != null) { return _cfgPublicRoomPrefix.Value; } return true; } } public static bool TeleportToHostOnJoinEnabled { get { if (_cfgTeleportToHostOnJoin != null) { return _cfgTeleportToHostOnJoin.Value; } return false; } } public static bool CoopModeEnabled { get { if (_cfgCoopMode != null) { return _cfgCoopMode.Value; } return true; } } public static bool AllowLobby { get { if (_cfgAllowLobby != null) { return _cfgAllowLobby.Value; } return true; } } public static bool AllowShop { get { if (_cfgAllowShop != null) { return _cfgAllowShop.Value; } return true; } } public static bool AllowLevel { get { if (_cfgAllowLevel != null) { return _cfgAllowLevel.Value; } return true; } } public static bool HudEnabled { get { if (_cfgHudEnabled != null) { return _cfgHudEnabled.Value; } return true; } } public static bool ShowJoinStatusEnabled { get { if (_cfgShowJoinStatus != null) { return _cfgShowJoinStatus.Value; } return true; } } public static bool HudShowRoomType { get { if (_cfgHudShowRoomType != null) { return _cfgHudShowRoomType.Value; } return true; } } public static bool HudShowRoomName { get { if (_cfgHudShowRoomName != null) { return _cfgHudShowRoomName.Value; } return true; } } public static bool HudShowPlayerCount { get { if (_cfgHudShowPlayerCount != null) { return _cfgHudShowPlayerCount.Value; } return true; } } public static int HudFontSize { get { if (_cfgHudFontSize != null) { return _cfgHudFontSize.Value; } return 16; } } public static int HudOpacityPercent { get { if (_cfgHudOpacity != null) { return _cfgHudOpacity.Value; } return 80; } } public static bool VerboseLogEnabled { get { if (_cfgVerboseLog != null) { return _cfgVerboseLog.Value; } return false; } } public static bool ShowConflictWarningEnabled { get { if (_cfgShowConflictWarning != null) { return _cfgShowConflictWarning.Value; } return true; } } internal static Dictionary<int, LateJoinEntry>.ValueCollection LateJoinEntryValues => _lateJoinEntries.Values; internal static int PendingLateJoinerCount => _lateJoinEntries.Count; internal static bool IsInChangeLevelCooldown => Time.unscaledTime < _changeLevelCooldownUntil; private static float CooldownLeft => Mathf.Max(0f, _changeLevelCooldownUntil - Time.unscaledTime); public static bool IsStaticModEnabled() { if (_cfgModEnabled != null) { return _cfgModEnabled.Value; } return false; } public static bool UseChinese() { return Instance?._i18n?.UseChinese == true; } internal static void Verbose(string msg) { if (VerboseLogEnabled) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)("[Verbose] " + msg)); } } } internal static void LogInfo(string msg) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)msg); } } internal static void LogWarning(string msg) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)msg); } } internal static void LogError(string msg) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogError((object)msg); } } internal static bool IsHookActive() { if (IsStaticModEnabled()) { return CompatibilityReport.RuntimeReady; } return false; } private static FieldRef<TInst, TFld> TryFieldRef<TInst, TFld>(string name) { try { return (AccessTools.Field(typeof(TInst), name) != null) ? AccessTools.FieldRefAccess<TInst, TFld>(name) : null; } catch { return null; } } public static bool IsCurrentSceneAllowed() { float unscaledTime = Time.unscaledTime; if (unscaledTime < _sceneAllowedCacheUntil) { return _sceneAllowedCachedValue; } _sceneAllowedCacheUntil = unscaledTime + 0.5f; _sceneAllowedCachedValue = ComputeSceneAllowed(); return _sceneAllowedCachedValue; } private static bool IsInGame() { float unscaledTime = Time.unscaledTime; if (unscaledTime < _inGameCacheUntil) { return _inGameCachedValue; } _inGameCacheUntil = unscaledTime + 0.5f; try { _inGameCachedValue = SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel(); } catch { _inGameCachedValue = false; } return _inGameCachedValue; } private static bool ComputeSceneAllowed() { if (!IsStaticModEnabled() || !PhotonNetwork.IsMasterClient || (Object)(object)RunManager.instance == (Object)null) { return false; } try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null && currentRoom.MaxPlayers > 0 && currentRoom.PlayerCount >= currentRoom.MaxPlayers) { return false; } if (SemiFunc.RunIsLobby()) { return AllowLobby; } if (SemiFunc.RunIsShop()) { return AllowShop; } if (SemiFunc.RunIsArena()) { return false; } if (!SemiFunc.RunIsLevel()) { return false; } if (!AllowLevel) { return false; } if ((Object)(object)RoundDirector.instance != (Object)null && _roundAllExtractionPointsCompletedRef != null) { try { if (_roundAllExtractionPointsCompletedRef.Invoke(RoundDirector.instance)) { return false; } } catch { } } return true; } catch (Exception ex) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)("场景判定失败:" + ex.Message)); } } return false; } public static bool IsInPreGameMenu() { try { if ((Object)(object)RunManager.instance == (Object)null) { return false; } if (SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel() || SemiFunc.RunIsArena()) { return false; } return true; } catch { return false; } } private void Awake() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown DetachFromManager(); Instance = this; ConfigVersionResetter.ResetIfVersionChanged(((BaseUnityPlugin)this).Config, "1.0.9", ((BaseUnityPlugin)this).Logger); BindConfig(); if (!IsStaticModEnabled()) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"LateJoinNow v1.0.9 已禁用。"); return; } CompatibilityReport.Build(); try { harmony = new Harmony("zichen.latejoinnow"); InstallPatchesSafely(); _i18nSwitcher = new I18NSwitcher(_i18n, harmony, "LateJoinNow", ((BaseUnityPlugin)this).Logger); _i18nSwitcher.InstallRepoConfigHook(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LateJoinNow v1.0.9 已加载。"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("LateJoinNow 启动失败:" + ex.Message)); } if (CompatibilityReport.HasIssues) { ((MonoBehaviour)this).StartCoroutine(WaitAndShowCompatPopup()); } if (ShowConflictWarningEnabled) { ConflictDetect.Detect(); if (ConflictDetect.HasConflict) { ((MonoBehaviour)this).StartCoroutine(WaitAndShowConflictPopup()); } } } private void Update() { if (IsHookActive() && PhotonNetwork.IsMasterClient) { _enforceTimer -= Time.unscaledDeltaTime; if (!(_enforceTimer > 0f)) { _enforceTimer = 1f; EnforceLobbyState(); } } } private void BindConfig() { bool defaultChinese = LanguageDetector.DetectDefault(); _i18n = new I18NRegistry(((BaseUnityPlugin)this).Config); _i18n.BindLanguage(defaultChinese); _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Name", "模组名称", "Late Join Now", "中途立即加入"); _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Version", "模组版本号", "1.0.9", "1.0.9"); _cfgModEnabled = _i18n.Bind("A.Global", "A.全局设置", "Mod Enabled", "模组启用", "Disable to turn off the whole mod.", "关闭后整个模组全部功能失效。", defaultValue: true); _cfgPublicRoomPrefix = _i18n.Bind("A.Global", "A.全局设置", "Public Room Name Prefix", "公开房名加前缀", "Auto add [Late Join] prefix to public room names.", "创建公开房时自动加 [中途加入] 前缀。", defaultValue: true); _cfgTeleportToHostOnJoin = _i18n.Bind("A.Global", "A.全局设置", "Teleport To Host On Join", "加入后传送到房主位置", "When enabled, late joiners are teleported to the host; if the host is dead, the closest alive player is used.", "开启时新加入者直接传送到房主位置;若房主死亡,则传送到最近一个活着的玩家位置。", defaultValue: false); _cfgCoopMode = _i18n.Bind("A.Global", "A.全局设置", "Coop Mode", "双端协同优化", "Enable optimized late join when both host and client have this mod (same version). Disable to fall back to legacy single-end mode (debug only).", "双端都装同版本模组时启用协同优化(更快加载、自动刷新外观/语音)。关闭后所有客户端按单端模式跑(仅调试用)。", defaultValue: true); _cfgAllowLobby = _i18n.Bind("B.Scene Whitelist", "B.场景白名单", "Allow Joining In Truck", "允许在卡车阶段加入", "Allow late joiners during the truck stage.", "卡车阶段允许中途加入。", defaultValue: true); _cfgAllowShop = _i18n.Bind("B.Scene Whitelist", "B.场景白名单", "Allow Joining In Shop", "允许在商店阶段加入", "Allow late joiners during the shop stage.", "商店阶段允许中途加入。", defaultValue: true); _cfgAllowLevel = _i18n.Bind("B.Scene Whitelist", "B.场景白名单", "Allow Joining In Level", "允许在关卡进行中加入", "Allow late joiners while a level is in progress.", "关卡进行中允许中途加入。", defaultValue: true); _cfgHudEnabled = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Enabled", "启用", "Show the room info HUD at the bottom-left.", "左下角显示房间信息框。", defaultValue: true); _cfgShowJoinStatus = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Join Status", "显示加入状态", "Show join progress of late joiners.", "显示中途加入玩家的进度。", defaultValue: true); _cfgHudShowPlayerCount = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Player Count", "显示房间人数", "Show current/max player count.", "显示当前人数/最大人数。", defaultValue: true); _cfgHudShowRoomType = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Room Type", "显示房间类型", "Show public/private/singleplayer.", "显示公开/私人/单人。", defaultValue: true); _cfgHudShowRoomName = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Room Name", "显示房间名称", "Show room name.", "显示房间名称。", defaultValue: true); _cfgHudFontSize = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Font Size", "字体大小", "HUD font size.", "HUD 字体大小。", 16, (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 28)); _cfgHudOpacity = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Opacity/%", "文字透明度/%", "HUD opacity.", "HUD 透明度。", 80, (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 100)); _cfgVerboseLog = _i18n.Bind("D.Debug", "D.调试", "Verbose Log", "详细日志", "Output detailed debug log.", "输出详细调试日志。", defaultValue: false); _cfgShowConflictWarning = _i18n.Bind("D.Debug", "D.调试", "Conflict Mod Warning", "同类模组冲突警告", "Show warning when same-kind mods are detected.", "检测到同类 mod 时弹警告。", defaultValue: true); if (_cfgCoopMode != null) { _cfgCoopMode.SettingChanged += OnCoopModeChanged; } } private static void OnCoopModeChanged(object _, EventArgs __) { try { if (CoopModeEnabled) { AnnouncePresenceAndRescan(); LogInfo("协同模式已开启:重扫房间内对端协同状态。"); } else { ClearOwnCoopProperties(); ResetCoopState(); LogInfo("协同模式已关闭:清理协同状态,所有客户端按单端模式跑。"); } } catch (Exception ex) { Verbose("OnCoopModeChanged 失败:" + ex.Message); } } private void DetachFromManager() { ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)52; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } internal static void TryRunClientLateJoinFixes(PlayerAvatar localAvatar) { if ((Object)(object)localAvatar == (Object)null || (Object)(object)Instance == (Object)null || !IsStaticModEnabled() || PhotonNetwork.IsMasterClient || _clientLateJoinFixesDone || _clientLateJoinCoroutineRunning) { return; } try { if (!SemiFunc.IsMultiplayer()) { return; } } catch { return; } try { if (!SemiFunc.RunIsLobby() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsLevel()) { return; } } catch { return; } _clientLateJoinCoroutineRunning = true; ((MonoBehaviour)Instance).StartCoroutine(ClientLateJoinFixesCoroutine()); } private static IEnumerator ClientLateJoinFixesCoroutine() { try { bool flag = false; try { flag = SemiFunc.RunIsLevel(); } catch { } if (flag) { float deadline = Time.unscaledTime + 30f; while (Time.unscaledTime < deadline && (!((Object)(object)LevelGenerator.Instance != (Object)null) || !LevelGenerator.Instance.Generated)) { yield return null; } if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { TryAnnounceCoopReady(0, "level-not-generated"); yield break; } } float coopDeadline = Time.unscaledTime + 1f; while (Time.unscaledTime < coopDeadline && !IsClientCoopActive()) { yield return null; } if (!IsClientCoopActive()) { TryAnnounceCoopReady(0, "host-not-coop"); yield break; } _clientLateJoinFixesDone = true; yield return null; if (!IsClientCoopActive() || PhotonNetwork.CurrentRoom == null) { TryAnnounceCoopReady(0, "coop-deactivated"); yield break; } int num = 0; int num2 = 0; bool flag2 = false; try { flag2 = SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel(); } catch { } List<PlayerCosmetics> cosmeticsList = new List<PlayerCosmetics>(8); try { List<PlayerAvatar> list = GameDirector.instance?.PlayerList; if (list != null) { foreach (PlayerAvatar item in list) { if ((Object)(object)item?.photonView == (Object)null || item.photonView.IsMine) { continue; } try { PlayerCosmetics componentInChildren = ((Component)item).GetComponentInChildren<PlayerCosmetics>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.SetupCosmetics(false, true, (List<int>)null); componentInChildren.SetupColors(false, (int[])null); num++; cosmeticsList.Add(componentInChildren); } } catch (Exception ex) { Verbose("客户端外观刷新失败:" + ex.Message); } if (!flag2) { continue; } try { PlayerVoiceChat componentInChildren2 = ((Component)item).GetComponentInChildren<PlayerVoiceChat>(); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.ToggleMixer(false, false); num2++; } } catch (Exception ex2) { Verbose("客户端语音刷新失败:" + ex2.Message); } } } } catch (Exception ex3) { Verbose("远程玩家批量刷新异常:" + ex3.Message); } LogInfo($"客户端协同补丁:外观刷新 {num} 人,语音刷新 {num2} 人。"); yield return _wait02; if (IsClientCoopActive() && PhotonNetwork.CurrentRoom != null) { try { foreach (PlayerCosmetics item2 in cosmeticsList) { if (!((Object)(object)item2 == (Object)null)) { try { item2.SetupCosmetics(false, true, (List<int>)null); item2.SetupColors(false, (int[])null); } catch { } } } } catch { } } bool flag3 = false; try { flag3 = SemiFunc.RunIsLevel(); } catch { } if (flag3) { int num3 = TryForceSpawnAllPhysGrabObjects(); if (num3 > 0) { LogInfo($"客户端协同补丁:强制 {num3} 个物品标记为已生成。"); } } int receivedModules = 0; if (flag3) { try { Module[] array = Object.FindObjectsOfType<Module>(); if (array != null) { receivedModules = array.Length; } } catch { } } if (flag3) { try { PlayerAvatar val = PlayerController.instance?.playerAvatarScript; if ((Object)(object)val?.photonView != (Object)null && val.photonView.IsMine) { val.LoadingLevelAnimationCompleted(); } } catch (Exception ex4) { Verbose("发送 LoadingLevelAnimationCompleted 失败:" + ex4.Message); } } TryAnnounceCoopReady(receivedModules, "fixes-complete"); } finally { _clientLateJoinCoroutineRunning = false; } } private static void TryAnnounceCoopReady(int receivedModules = 0, string reason = null) { try { AnnounceCoopReady(receivedModules); if (!string.IsNullOrEmpty(reason)) { Verbose("协同 ack(" + reason + ")"); } } catch (Exception ex) { Verbose("发协同 ack 失败:" + ex.Message); } } internal static void ResetClientCoopState() { _clientLateJoinFixesDone = false; _clientLateJoinCoroutineRunning = false; } internal static void TryForceSpawnSingle(PhysGrabObject p) { if ((Object)(object)p == (Object)null || PhotonNetwork.IsMasterClient || !IsClientCoopActive() || !_clientLateJoinFixesDone) { return; } try { if (!SemiFunc.RunIsLevel()) { return; } } catch { return; } if (HasHinge(p)) { try { Object.Destroy((Object)(object)((Component)p).gameObject); return; } catch { return; } } FieldInfo physGrabSpawnedField = GetPhysGrabSpawnedField(); if (physGrabSpawnedField == null) { return; } try { physGrabSpawnedField.SetValue(p, true); if ((Object)(object)p.rb != (Object)null && p.rb.isKinematic) { p.rb.isKinematic = false; } } catch { } } private static FieldInfo GetPhysGrabSpawnedField() { if (_physGrabSpawnedField != null) { return _physGrabSpawnedField; } try { _physGrabSpawnedField = AccessTools.Field(typeof(PhysGrabObject), "spawned"); } catch { } return _physGrabSpawnedField; } private static Type GetPhysGrabHingeType() { if (_physGrabHingeType != null) { return _physGrabHingeType; } try { _physGrabHingeType = AccessTools.TypeByName("PhysGrabHinge"); } catch { } return _physGrabHingeType; } private static bool HasHinge(PhysGrabObject p) { Type physGrabHingeType = GetPhysGrabHingeType(); if (physGrabHingeType == null) { return false; } try { return (Object)(object)((Component)p).GetComponent(physGrabHingeType) != (Object)null; } catch { return false; } } private static int TryForceSpawnAllPhysGrabObjects() { FieldInfo physGrabSpawnedField = GetPhysGrabSpawnedField(); if (physGrabSpawnedField == null) { return 0; } int num = 0; int num2 = 0; try { PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>(); foreach (PhysGrabObject val in array) { if ((Object)(object)val == (Object)null) { continue; } if (HasHinge(val)) { try { Object.Destroy((Object)(object)((Component)val).gameObject); num2++; } catch { } continue; } try { physGrabSpawnedField.SetValue(val, true); if ((Object)(object)val.rb != (Object)null && val.rb.isKinematic) { val.rb.isKinematic = false; } num++; } catch { } } } catch (Exception ex) { Verbose("强制 spawned 失败:" + ex.Message); } if (num2 > 0) { Verbose($"本地销毁 {num2} 个门,避免卡门。"); } return num; } private void OnGUI() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 if (!IsHookActive() || !PhotonNetwork.IsMasterClient) { return; } Event current = Event.current; if (current == null || (int)current.type != 7) { return; } bool flag = IsInGame(); bool flag2 = HudEnabled && flag && PhotonNetwork.CurrentRoom != null; bool flag3 = ShowJoinStatusEnabled && PendingLateJoinerCount > 0; if (flag2 || flag3) { EnsureHudStyles(); if (flag2) { RefreshHudLinesIfNeeded(); } else { _hudLines.Clear(); } DrawHud(); } } private static void EnsureHudStyles() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_008d: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) int hudFontSize = HudFontSize; int hudOpacityPercent = HudOpacityPercent; if (_hudLabelStyle == null || _cachedFontSize != hudFontSize || _cachedOpacity != hudOpacityPercent) { hudFontSize = Mathf.Clamp(hudFontSize, 10, 28); hudOpacityPercent = Mathf.Clamp(hudOpacityPercent, 20, 100); if (_hudLabelStyle == null || _cachedFontSize != hudFontSize || _cachedOpacity != hudOpacityPercent) { float num = (float)hudOpacityPercent / 100f; _hudLabelStyle = new GUIStyle { fontSize = hudFontSize, richText = true }; _hudLabelStyle.normal.textColor = new Color(1f, 1f, 1f, num); _hudShadowStyle = new GUIStyle { fontSize = hudFontSize, richText = true }; _hudShadowStyle.normal.textColor = new Color(0f, 0f, 0f, num * 0.7f); _cachedFontSize = hudFontSize; _cachedOpacity = hudOpacityPercent; } } } private static void RefreshHudLinesIfNeeded() { //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime < _hudNextRefreshAt) { return; } _hudNextRefreshAt = Time.unscaledTime + 1f; bool flag = UseChinese(); bool hudShowPlayerCount = HudShowPlayerCount; bool hudShowRoomType = HudShowRoomType; bool hudShowRoomName = HudShowRoomName; int num = 0; int num2 = 0; bool flag2 = false; try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { num = currentRoom.PlayerCount; num2 = currentRoom.MaxPlayers; flag2 = currentRoom.IsOpen && (num2 == 0 || num < num2); } } catch { } LobbyTypes? lobbyTypeOrNull = GetLobbyTypeOrNull(); string text = ReadServerName(); bool multi = false; try { multi = SemiFunc.IsMultiplayer(); } catch { } if (flag != _hudCacheUseChinese || hudShowPlayerCount != _hudCacheShowPlayerCount || hudShowRoomType != _hudCacheShowRoomType || hudShowRoomName != _hudCacheShowRoomName || num != _hudCachePlayerCount || num2 != _hudCacheMaxPlayers || lobbyTypeOrNull.HasValue != _hudCacheLobbyTypeKnown || (lobbyTypeOrNull.HasValue && lobbyTypeOrNull.Value != _hudCacheLobbyType) || text != _hudCacheServerName || flag2 != _hudCacheRoomOpen) { _hudCacheUseChinese = flag; _hudCacheShowPlayerCount = hudShowPlayerCount; _hudCacheShowRoomType = hudShowRoomType; _hudCacheShowRoomName = hudShowRoomName; _hudCachePlayerCount = num; _hudCacheMaxPlayers = num2; _hudCacheLobbyTypeKnown = lobbyTypeOrNull.HasValue; _hudCacheLobbyType = lobbyTypeOrNull.GetValueOrDefault(); _hudCacheServerName = text; _hudCacheRoomOpen = flag2; _hudLines.Clear(); if (hudShowPlayerCount) { _hudLines.Add((flag ? "房间人数:" : "Players: ") + GetPlayerCountText(num, num2, multi, flag)); } if (hudShowRoomType) { _hudLines.Add((flag ? "房间类型:" : "Room: ") + GetRoomTypeText(lobbyTypeOrNull, multi, flag, flag2)); } if (hudShowRoomName) { _hudLines.Add((flag ? "房间名称:" : "Name: ") + GetRoomNameText(lobbyTypeOrNull, text, multi, flag)); } } } private static void DrawHud() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) float num = (float)((_cachedFontSize > 0) ? _cachedFontSize : 16) * 1.35f; List<string> list = (ShowJoinStatusEnabled ? BuildLateJoinStatusLines() : _emptyList); int num2 = list.Count + _hudLines.Count; if (num2 != 0) { float num3 = 12f; float num4 = (float)Screen.height - num * (float)num2 - 12f; for (int i = 0; i < list.Count; i++) { float num5 = num4 + (float)i * num; GUI.Label(new Rect(num3 + 1f, num5 + 1f, 480f, num), list[i], _hudShadowStyle); GUI.Label(new Rect(num3, num5, 480f, num), list[i], _hudLabelStyle); } for (int j = 0; j < _hudLines.Count; j++) { float num6 = num4 + (float)(list.Count + j) * num; GUI.Label(new Rect(num3 + 1f, num6 + 1f, 480f, num), _hudLines[j], _hudShadowStyle); GUI.Label(new Rect(num3, num6, 480f, num), _hudLines[j], _hudLabelStyle); } } } private static List<string> BuildLateJoinStatusLines() { int pendingLateJoinerCount = PendingLateJoinerCount; int num = ComputeCoopMaskForHud(); float unscaledTime = Time.unscaledTime; if (pendingLateJoinerCount == _statusLinesLastEntryCount && num == _statusLinesLastCoopMask && unscaledTime < _statusLinesNextRefreshAt) { return _lateJoinStatusLines; } _statusLinesNextRefreshAt = unscaledTime + 0.25f; _statusLinesLastEntryCount = pendingLateJoinerCount; _statusLinesLastCoopMask = num; _lateJoinStatusLines.Clear(); if (pendingLateJoinerCount == 0) { return _lateJoinStatusLines; } _hudEntriesScratch.Clear(); foreach (LateJoinEntry lateJoinEntryValue in LateJoinEntryValues) { _hudEntriesScratch.Add(lateJoinEntryValue); } _hudEntriesScratch.Sort(_entrySortDesc); bool flag = UseChinese(); for (int i = 0; i < _hudEntriesScratch.Count; i++) { LateJoinEntry lateJoinEntry = _hudEntriesScratch[i]; string text; switch (lateJoinEntry.State) { case LateJoinState.Pending: text = (flag ? "等待连接" : "Connecting"); break; case LateJoinState.Queued: text = (flag ? "排队中" : "Queued"); break; case LateJoinState.Processing: case LateJoinState.Finalizing: text = (flag ? "加载中" : "Loading"); break; case LateJoinState.Done: text = (flag ? "已中途加入" : "Late Joined"); break; default: continue; } if (lateJoinEntry.State == LateJoinState.Done && IsCoopActorActive(lateJoinEntry.ActorNumber)) { text += (flag ? "(优化)" : " (Optimized)"); } _lateJoinStatusLines.Add(ResolveDisplayName(lateJoinEntry.ActorNumber, lateJoinEntry.NickName) + ": " + text); } return _lateJoinStatusLines; } private static int ComputeCoopMaskForHud() { int num = 0; foreach (LateJoinEntry lateJoinEntryValue in LateJoinEntryValues) { if (lateJoinEntryValue.State == LateJoinState.Done) { int num2 = (IsCoopActorActive(lateJoinEntryValue.ActorNumber) ? 1 : 0); num = num * 31 + (lateJoinEntryValue.ActorNumber << 1) + num2; } } return num; } private static LobbyTypes? GetLobbyTypeOrNull() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameManager.instance == (Object)null) { return null; } try { if (CompatibilityReport.GameManagerLobbyTypeRef != null) { return CompatibilityReport.GameManagerLobbyTypeRef.Invoke(GameManager.instance); } if (CompatibilityReport.GameManagerLobbyTypeField?.GetValue(GameManager.instance) is LobbyTypes value) { return value; } } catch { } return null; } private static string ReadServerName() { try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom == null) { return null; } string text = null; Hashtable customProperties = ((RoomInfo)currentRoom).CustomProperties; if (customProperties != null && ((Dictionary<object, object>)(object)customProperties).TryGetValue((object)"server_name", out object value)) { text = value as string; } if (string.IsNullOrWhiteSpace(text)) { text = currentRoom.Name; } return string.IsNullOrEmpty(text) ? null : text; } catch { return null; } } private static string GetRoomTypeText(LobbyTypes? lt, bool multi, bool cn, bool roomOpen) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected I4, but got Unknown if (!multi) { if (!cn) { return "Singleplayer"; } return "单人模式"; } string text; if (lt.HasValue) { LobbyTypes value = lt.Value; text = (int)value switch { 1 => cn ? "公开服务器" : "Public", 0 => cn ? "私人服务器" : "Private", 2 => cn ? "匹配房间" : "Matchmaking", _ => cn ? "未知" : "Unknown", }; } else { try { text = ((PhotonNetwork.CurrentRoom == null) ? (cn ? "未知" : "Unknown") : ((!PhotonNetwork.CurrentRoom.IsVisible) ? (cn ? "私人服务器" : "Private") : (cn ? "公开服务器" : "Public"))); } catch { text = (cn ? "未知" : "Unknown"); } } string text2 = ((!roomOpen) ? (cn ? "禁止加入" : "Closed") : (cn ? "可加入" : "Open")); return text + " - " + text2; } private static string GetRoomNameText(LobbyTypes? lt, string serverName, bool multi, bool cn) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 if (!multi) { if (!cn) { return "N/A"; } return "无"; } bool num; if (!lt.HasValue) { if (PhotonNetwork.CurrentRoom == null) { goto IL_004f; } num = !PhotonNetwork.CurrentRoom.IsVisible; } else { num = (int)lt.Value == 0; } if (num) { if (!cn) { return "Friends Only"; } return "仅限好友加入"; } goto IL_004f; IL_004f: if (string.IsNullOrEmpty(serverName)) { if (!cn) { return "Unknown"; } return "未知"; } return serverName; } private static string GetPlayerCountText(int pc, int mp, bool multi, bool cn) { if (!multi) { return "1/1"; } if (pc > 0) { return pc + "/" + ((mp > 0) ? mp : pc); } if (!cn) { return "Unknown"; } return "未知"; } private static FieldRef<PlayerAvatar, string> TryAvatarPlayerNameRef() { try { return (AccessTools.Field(typeof(PlayerAvatar), "playerName") != null) ? AccessTools.FieldRefAccess<PlayerAvatar, string>("playerName") : null; } catch { return null; } } internal static string ResolveDisplayName(int actorNumber, string fallback) { try { if (GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (player == null) { continue; } PhotonView photonView = player.photonView; int? obj; if (photonView == null) { obj = null; } else { Player owner = photonView.Owner; obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null)); } if (obj != actorNumber) { continue; } if (_avatarPlayerNameRef != null) { string text = _avatarPlayerNameRef.Invoke(player); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } Player owner2 = player.photonView.Owner; string text2 = ((owner2 != null) ? owner2.NickName : null); if (!string.IsNullOrWhiteSpace(text2)) { return text2.Trim(); } break; } } } catch { } return fallback; } private static StateSyncEntry S(string type, string rpc, bool castToInt = true, string field = "currentState") { return new StateSyncEntry { TypeName = type, FieldName = field, RpcName = rpc, CastToInt = castToInt }; } internal static IEnumerator SendStateSyncStreamed(Player target, LateJoinEntry lateE, PhotonView[] allPVs) { if (target == null) { yield break; } int sent = 0; int skipped = 0; int errors = 0; StateSyncEntry[] stateSyncEntries = _stateSyncEntries; for (int i = 0; i < stateSyncEntries.Length; i++) { ResolveEntryReflection(stateSyncEntries[i]); } if (allPVs == null) { try { allPVs = Object.FindObjectsOfType<PhotonView>(); } catch { } } if (allPVs == null) { yield break; } if (_epIsLockedFieldInfo == null) { try { _epIsLockedFieldInfo = AccessTools.Field(typeof(ExtractionPoint), "isLocked"); } catch { } } StateSyncEntry[] stateSyncEntries2 = _stateSyncEntries; foreach (StateSyncEntry stateSyncEntry in stateSyncEntries2) { if (stateSyncEntry.ResolvedType == null || stateSyncEntry.ResolvedField == null) { continue; } PhotonView[] array = allPVs; foreach (PhotonView val in array) { if ((Object)(object)val == (Object)null || val.ViewID == 0) { continue; } Component component = ((Component)val).GetComponent(stateSyncEntry.ResolvedType); MonoBehaviour val2 = (MonoBehaviour)(object)((component is MonoBehaviour) ? component : null); if ((Object)(object)val2 == (Object)null) { continue; } try { object value = stateSyncEntry.ResolvedField.GetValue(val2); if (value == null) { skipped++; continue; } val.RPC(stateSyncEntry.RpcName, target, new object[1] { stateSyncEntry.CastToInt ? ((object)Convert.ToInt32(value)) : value }); sent++; if (!(stateSyncEntry.ResolvedType == typeof(ExtractionPoint)) || !(_epIsLockedFieldInfo != null)) { continue; } try { if ((bool)_epIsLockedFieldInfo.GetValue(val2)) { object[] array2 = new object[2] { val.ViewID, true }; RaiseEventOptions val3 = new RaiseEventOptions(); val3.TargetActors = new int[1] { target.ActorNumber }; PhotonNetwork.RaiseEvent((byte)171, (object)array2, val3, SendOptions.SendReliable); } } catch { } } catch (Exception ex) { errors++; Verbose("状态同步失败:" + ex.Message); } } yield return null; if (lateE.State == LateJoinState.Aborted || !IsTargetStillOnline(lateE.Target)) { yield break; } } Verbose($"状态同步:sent={sent} skipped={skipped} errors={errors},分帧完成。"); LogInfo(target.NickName + " 状态同步完成。"); } private static void ResolveEntryReflection(StateSyncEntry entry) { if (entry.ResolveAttempted) { return; } entry.ResolveAttempted = true; try { Type type = typeof(PlayerAvatar).Assembly.GetType(entry.TypeName, throwOnError: false); if (!(type == null)) { FieldInfo fieldInfo = AccessTools.Field(type, entry.FieldName); if (!(fieldInfo == null)) { entry.ResolvedType = type; entry.ResolvedField = fieldInfo; } } } catch { } } internal static void ClearLocalCachesForClient() { } internal static bool TryGetLateJoinEntryByActor(int actor, out LateJoinEntry entry) { return _lateJoinEntries.TryGetValue(actor, out entry); } private static FieldRef<PlayerAvatar, bool> TryAvatarAnimCompletedRef() { try { return (AccessTools.Field(typeof(PlayerAvatar), "levelAnimationCompleted") != null) ? AccessTools.FieldRefAccess<PlayerAvatar, bool>("levelAnimationCompleted") : null; } catch { return null; } } private static FieldRef<MenuPageLobby, List<string>> TryMenuLobbyListRef(string name) { try { return (AccessTools.Field(typeof(MenuPageLobby), name) != null) ? AccessTools.FieldRefAccess<MenuPageLobby, List<string>>(name) : null; } catch { return null; } } private static FieldRef<MenuPageLobby, float> TryMenuLobbyFloatRef(string name) { try { return (AccessTools.Field(typeof(MenuPageLobby), name) != null) ? AccessTools.FieldRefAccess<MenuPageLobby, float>(name) : null; } catch { return null; } } internal static void NetworkManagerOnPlayerEnteredRoomPostfix(Player p) { if (p != null) { UpdateCoopStateFor(p); } if (IsHookActive() && PhotonNetwork.IsMasterClient && p != null && PhotonNetwork.CurrentRoom != null && IsSnapshotNeededForCurrentScene()) { _lateJoinEntries[p.ActorNumber] = new LateJoinEntry { Target = p, ActorNumber = p.ActorNumber, NickName = p.NickName, State = LateJoinState.Pending, StartScene = CurrentSceneName() }; LogInfo(p.NickName + " 中途加入,等待同步。"); } } internal static void NetworkManagerOnPlayerLeftRoomPostfix(Player p) { if (p == null) { return; } RemoveCoopActor(p.ActorNumber); if (_lateJoinEntries.TryGetValue(p.ActorNumber, out var value)) { value.State = LateJoinState.Aborted; Verbose(p.NickName + " 离开房间,同步取消。"); } try { if ((Object)(object)MenuPageLobby.instance != (Object)null && _menuLobbyJoiningPlayersRef != null) { List<string> list = _menuLobbyJoiningPlayersRef.Invoke(MenuPageLobby.instance); if (list != null && list.Count > 0 && list.Remove(p.NickName)) { if (_menuLobbyJoiningTimerRef != null) { _menuLobbyJoiningTimerRef.Invoke(MenuPageLobby.instance) = 0f; } if (_menuLobbyJoiningEndTimerRef != null) { _menuLobbyJoiningEndTimerRef.Invoke(MenuPageLobby.instance) = 0f; } Verbose(p.NickName + " 离开,已清理大厅按钮锁定状态。"); } } } catch { } if (!PhotonNetwork.IsMasterClient) { return; } try { if (GameDirector.instance?.PlayerList == null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { int? obj2; if (player == null) { obj2 = null; } else { PhotonView photonView = player.photonView; if (photonView == null) { obj2 = null; } else { Player owner = photonView.Owner; obj2 = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null)); } } if (obj2 != p.ActorNumber) { continue; } PlayerCosmetics componentInChildren = ((Component)player).GetComponentInChildren<PlayerCosmetics>(); if (!((Object)(object)componentInChildren == (Object)null)) { PhotonView component = ((Component)componentInChildren).GetComponent<PhotonView>(); if (!((Object)(object)component == (Object)null)) { PhotonNetwork.RemoveBufferedRPCs(component.ViewID, (string)null, (int[])null); Verbose(p.NickName + " 离开,已清理外观缓存。"); break; } } } } catch { } try { TakeoverOrphanedPhysGrabObjects(p.ActorNumber); } catch (Exception ex) { Verbose("接管孤儿物品失败:" + ex.Message); } } private static void TakeoverOrphanedPhysGrabObjects(int leftActor) { int num = 0; PhysGrabObject[] array = null; try { array = Object.FindObjectsOfType<PhysGrabObject>(); } catch { } if (array == null) { return; } PhysGrabObject[] array2 = array; foreach (PhysGrabObject val in array2) { if ((Object)(object)val == (Object)null) { continue; } PhotonView component = ((Component)val).GetComponent<PhotonView>(); if (!((Object)(object)component == (Object)null) && component.ViewID != 0 && (component.Owner == null || component.Owner.ActorNumber == leftActor)) { try { component.TransferOwnership(PhotonNetwork.LocalPlayer); num++; } catch { } } } if (num > 0) { Verbose($"已接管 {num} 个孤儿物品到主机。"); } } internal static void NetworkManagerPlayerSpawnedRPCPostfix(PhotonMessageInfo info) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!IsHookActive() || !PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null) { return; } Player sender = info.Sender; if (sender == null || sender.ActorNumber == PhotonNetwork.LocalPlayer.ActorNumber || !_lateJoinEntries.TryGetValue(sender.ActorNumber, out var value) || value.State != LateJoinState.Pending) { return; } if (!IsSnapshotNeededForCurrentScene()) { value.State = LateJoinState.Aborted; return; } value.State = LateJoinState.Queued; value.Target = sender; _lateJoinQueue.Enqueue(sender.ActorNumber); LogInfo($"{sender.NickName} 开始排队同步(队列 {_lateJoinQueue.Count} 人)。"); if (!_schedulerRunning && (Object)(object)Instance != (Object)null) { _schedulerRunning = true; ((MonoBehaviour)Instance).StartCoroutine(LateJoinSchedulerCoroutine()); } } private static IEnumerator LateJoinSchedulerCoroutine() { try { while (_lateJoinQueue.Count > 0) { if (!IsStaticModEnabled() || !PhotonNetwork.IsMasterClient) { _lateJoinQueue.Clear(); break; } int key = _lateJoinQueue.Dequeue(); if (_lateJoinEntries.TryGetValue(key, out var value) && value.State != LateJoinState.Aborted) { if (!IsTargetStillOnline(value.Target) || CurrentSceneName() != value.StartScene) { value.State = LateJoinState.Aborted; continue; } value.State = LateJoinState.Processing; LogInfo("正在同步 " + value.NickName + "。"); ((MonoBehaviour)Instance).StartCoroutine(RunSingleJoinerPipeline(value)); yield return null; } } } finally { _schedulerRunning = false; } } private static IEnumerator RunSingleJoinerPipeline(LateJoinEntry e) { yield return ((MonoBehaviour)Instance).StartCoroutine(EnsurePlayerCorpseObjectsAndSnapshot(e)); if (e.State != LateJoinState.Done && e.State != LateJoinState.Finalizing) { yield break; } while (true) { if (_refreshOldPlayersBusy) { if (IsTargetStillOnline(e.Target) && !(CurrentSceneName() != e.StartScene)) { yield return null; continue; } break; } _refreshOldPlayersBusy = true; try { yield return ((MonoBehaviour)Instance).StartCoroutine(RefreshOldPlayersForNewJoiner(e.Target, e.StartScene)); break; } finally { _refreshOldPlayersBusy = false; } } } private static IEnumerator RefreshOldPlayersForNewJoiner(Player newPlayer, string startScene) { if (_ownershipUpdate == null) { yield break; } int hostActor = PhotonNetwork.LocalPlayer.ActorNumber; int hostVoiceViewID = 0; PlayerAvatar hostAvatar = null; int newPlayerActor = newPlayer.ActorNumber; int newPlayerVoiceViewID = 0; PlayerAvatar newPlayerAvatar = null; try { if (GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player?.photonView == (Object)null) && player.photonView.IsMine) { hostAvatar = player; break; } } } if ((Object)(object)hostAvatar != (Object)null && CurrentSceneName() == startScene) { List<PlayerVoiceChat> list = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null); if (list != null) { foreach (PlayerVoiceChat item in list) { PhotonView val = ((item != null) ? ((Component)item).GetComponent<PhotonView>() : null); int num; if (val == null) { num = 1; } else { Player owner = val.Owner; num = ((((owner != null) ? new int?(owner.ActorNumber) : ((int?)null)) != hostActor) ? 1 : 0); } if (num == 0) { hostVoiceViewID = val.ViewID; try { hostAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", newPlayer, new object[1] { val.ViewID }); } catch { } break; } } } } } catch { } try { if (GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player2 in GameDirector.instance.PlayerList) { if (!((Object)(object)player2?.photonView == (Object)null)) { Player owner2 = player2.photonView.Owner; if (owner2 != null && owner2.ActorNumber == newPlayerActor) { newPlayerAvatar = player2; break; } } } } if ((Object)(object)newPlayerAvatar != (Object)null && CurrentSceneName() == startScene) { List<PlayerVoiceChat> list2 = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null); if (list2 != null) { foreach (PlayerVoiceChat item2 in list2) { PhotonView val2 = ((item2 != null) ? ((Component)item2).GetComponent<PhotonView>() : null); int num2; if (val2 == null) { num2 = 1; } else { Player owner3 = val2.Owner; num2 = ((((owner3 != null) ? new int?(owner3.ActorNumber) : ((int?)null)) != newPlayerActor) ? 1 : 0); } if (num2 == 0) { newPlayerVoiceViewID = val2.ViewID; try { newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", PhotonNetwork.LocalPlayer, new object[1] { val2.ViewID }); } catch { } break; } } } } } catch { } yield return null; int master = PhotonNetwork.LocalPlayer.ActorNumber; bool isCoop = IsCoopActorActive(newPlayer.ActorNumber); int maxAttempts = (isCoop ? 1 : 3); for (int attempt = 0; attempt < maxAttempts; attempt++) { if (newPlayerVoiceViewID == 0 || (Object)(object)newPlayerAvatar == (Object)null) { try { if ((Object)(object)newPlayerAvatar == (Object)null && GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player3 in GameDirector.instance.PlayerList) { if (player3 != null) { PhotonView photonView = player3.photonView; int? obj5; if (photonView == null) { obj5 = null; } else { Player owner4 = photonView.Owner; obj5 = ((owner4 != null) ? new int?(owner4.ActorNumber) : ((int?)null)); } if (obj5 == newPlayerActor) { newPlayerAvatar = player3; break; } } } } if ((Object)(object)newPlayerAvatar != (Object)null && newPlayerVoiceViewID == 0) { List<PlayerVoiceChat> list3 = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null); if (list3 != null) { foreach (PlayerVoiceChat item3 in list3) { PhotonView val3 = ((item3 != null) ? ((Component)item3).GetComponent<PhotonView>() : null); int num3; if (val3 == null) { num3 = 1; } else { Player owner5 = val3.Owner; num3 = ((((owner5 != null) ? new int?(owner5.ActorNumber) : ((int?)null)) != newPlayerActor) ? 1 : 0); } if (num3 == 0) { newPlayerVoiceViewID = val3.ViewID; try { newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", PhotonNetwork.LocalPlayer, new object[1] { val3.ViewID }); } catch { } break; } } } } } catch { } } if (attempt > 0) { yield return _wait25; if (hostVoiceViewID != 0 && (Object)(object)hostAvatar != (Object)null && CurrentSceneName() == startScene) { try { hostAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", newPlayer, new object[1] { hostVoiceViewID }); } catch { } } if (newPlayerVoiceViewID != 0 && (Object)(object)newPlayerAvatar != (Object)null && CurrentSceneName() == startScene) { try { newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", PhotonNetwork.LocalPlayer, new object[1] { newPlayerVoiceViewID }); } catch { } } } if (!IsTargetStillOnline(newPlayer) || CurrentSceneName() != startScene) { yield break; } _oldAvatarsScratch.Clear(); if (GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player4 in GameDirector.instance.PlayerList) { object obj10; if (player4 == null) { obj10 = null; } else { PhotonView photonView2 = player4.photonView; obj10 = ((photonView2 != null) ? photonView2.Owner : null); } if (obj10 != null && player4.photonView.Owner.ActorNumber != newPlayer.ActorNumber && !player4.photonView.IsMine) { int actorNumber = player4.photonView.Owner.ActorNumber; if (!_lateJoinEntries.TryGetValue(actorNumber, out var value) || value.State != LateJoinState.Aborted) { _oldAvatarsScratch.Add((player4, actorNumber)); } } } } if (_oldAvatarsScratch.Count == 0) { continue; } for (int i = 0; i < _oldAvatarsScratch.Count; i++) { var (av, origActor) = _oldAvatarsScratch[i]; if ((Object)(object)av?.photonView == (Object)null || CurrentSceneName() != startScene) { yield break; } Player origPlayer = av.photonView.Owner; if (origPlayer == null) { continue; } int vid = av.photonView.ViewID; int voiceViewID = 0; try { List<PlayerVoiceChat> list4 = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null); if (list4 != null) { foreach (PlayerVoiceChat item4 in list4) { PhotonView val4 = ((item4 != null) ? ((Component)item4).GetComponent<PhotonView>() : null); int num4; if (val4 == null) { num4 = 1; } else { Player owner6 = val4.Owner; num4 = ((((owner6 != null) ? new int?(owner6.ActorNumber) : ((int?)null)) != origActor) ? 1 : 0); } if (num4 == 0) { voiceViewID = val4.ViewID; break; } } } } catch { } if (voiceViewID == 0) { continue; } if (!isCoop && attempt == 0) { if (hostVoiceViewID != 0) { try { _ownershipScratch[0] = hostVoiceViewID; _ownershipScratch[1] = 999; _ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber); } catch { } yield return null; } try { Hashtable val5 = new Hashtable(); val5[(byte)0] = "Voice"; val5[(byte)1] = Vector3.zero; val5[(byte)2] = Quaternion.identity; val5[(byte)3] = (byte)0; val5[(byte)4] = new int[1] { voiceViewID }; val5[(byte)5] = null; val5[(byte)6] = PhotonNetwork.ServerTimestamp; val5[(byte)7] = voiceViewID; val5[(byte)8] = (byte)0; RaiseEventOptions val6 = new RaiseEventOptions(); val6.TargetActors = new int[1] { newPlayer.ActorNumber }; RaiseEventOptions val7 = val6; PhotonNetwork.RaiseEvent((byte)202, (object)val5, val7, SendOptions.SendReliable); } catch { } yield return null; try { _ownershipScratch[0] = voiceViewID; _ownershipScratch[1] = origActor; _ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber); } catch { } yield return null; if (hostVoiceViewID != 0) { try { _ownershipScratch[0] = hostVoiceViewID; _ownershipScratch[1] = hostActor; _ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber); } catch { } yield return null; } } try { _ownershipScratch[0] = vid; _ownershipScratch[1] = master; _ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber); } catch { continue; } yield return null; try { av.photonView.RPC("LoadingLevelAnimationCompletedRPC", newPlayer, Array.Empty<object>()); } catch { } try { av.photonView.RPC("UpdateMyPlayerVoiceChat", newPlayer, new object[1] { voiceViewID }); } catch { } if (!isCoop) { yield return _wait15; } else { yield return null; } try { _ownershipScratch[0] = vid; _ownershipScratch[1] = origActor; _ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber); } catch { } yield return null; if (newPlayerVoiceViewID != 0 && (Object)(object)newPlayerAvatar != (Object)null) { try { _ownershipScratch[0] = newPlayerAvatar.photonView.ViewID; _ownershipScratch[1] = master; _ownershipUpdate(_ownershipScratch, origActor); } catch { continue; } yield return null; try { newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", origPlayer, new object[1] { newPlayerVoiceViewID }); } catch { } if (!isCoop) { yield return _wait15; } else { yield return null; } try { _ownershipScratch[0] = newPlayerAvatar.photonView.ViewID; _ownershipScratch[1] = newPlayerActor; _ownershipUpdate(_ownershipScratch, origActor); } catch { } yield return null; } } } if (!isCoop) { yield return _wait05; } if (CurrentSceneName() == startScene) { BroadcastLoadingAnimationCompletedOnce(newPlayer); } } internal static void ClearLateJoinQueue() { int count = _lateJoinQueue.Count; int count2 = _lateJoinEntries.Count; foreach (LateJoinEntry value in _lateJoinEntries.Values) { value.State = LateJoinState.Aborted; } _lateJoinQueue.Clear(); _lateJoinEntries.Clear(); _coopReadyByActor.Clear(); _coopModuleCountByActor.Clear(); if (count > 0 || count2 > 0) { Verbose($"切关清空同步队列:{count} 待处理,{count2} 条目。"); } } private static IEnumerator EnsurePlayerCorpseObjectsAndSnapshot(LateJoinEntry e) { yield return null; if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target)) { e.State = LateJoinState.Aborted; yield break; } EnsurePlayerCorpseObjects(e.Target); yield return ((MonoBehaviour)Instance).StartCoroutine(SendCatchupSnapshotStaged(e)); } private static IEnumerator SendCatchupSnapshotStaged(LateJoinEntry e) { Player t = e.Target; Verbose("开始同步 " + e.NickName + "。"); float avatarDeadline = Time.unscaledTime + 3f; while (Time.unscaledTime < avatarDeadline) { if (ShouldAbortSnapshot(e, "wait_avatar")) { yield break; } bool flag = false; if (GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { int? obj; if (player == null) { obj = null; } else { PhotonView photonView = player.photonView; if (photonView == null) { obj = null; } else { Player owner = photonView.Owner; obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null)); } } if (obj == e.Target.ActorNumber) { flag = true; break; } } } if (flag) { break; } yield return null; } if (ShouldAbortSnapshot(e, "1")) { yield break; } try { NetworkManager instance = NetworkManager.instance; if (instance != null) { PhotonView photonView2 = ((MonoBehaviourPun)instance).photonView; if (photonView2 != null) { photonView2.RPC("AllPlayerSpawnedRPC", t, Array.Empty<object>()); } } } catch { } yield return null; if (ShouldAbortSnapshot(e, "2")) { yield break; } yield return ((MonoBehaviour)Instance).StartCoroutine(WaitHostGenerated(e)); if (ShouldAbortSnapshot(e, "3")) { yield break; } Module[] allModules = null; PhotonView[] allPVs = null; ItemAttributes[] items = null; try { allModules = Object.FindObjectsOfType<Module>(); } catch { } try { allPVs = Object.FindObjectsOfType<PhotonView>(); } catch { } try { items = Object.FindObjectsOfType<ItemAttributes>(); } catch { } int _syncDone = 0; Action onDone = delegate { _syncDone++; }; ((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendModuleSyncStreamed(t, e, allModules), onDone)); ((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendValuableSyncStreamed(t, e), onDone)); ((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendItemAttributesSyncStreamed(t, e, items), onDone)); ((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendStateSyncStreamed(t, e, allPVs), onDone)); float syncDeadline = Time.unscaledTime + 10f; while (_syncDone < 4 && Time.unscaledTime < syncDeadline) { if (ShouldAbortSnapshot(e, "3_parallel")) { yield break; } yield return null; } if (ShouldAbortSnapshot(e, "3_done")) { yield break; } try { LevelGenerator instance2 = LevelGenerator.Instance; if (instance2 != null) { PhotonView photonView3 = instance2.PhotonView; if (photonView3 != null) { photonView3.RPC("GenerateDone", t, Array.Empty<object>()); } } } catch { } if (IsCoopActorActive(e.Target.ActorNumber)) { ResetCoopReadyFor(e.Target.ActorNumber); yield return ((MonoBehaviour)Instance).StartCoroutine(WaitClientReadyOrTimeout(e, 8f)); if (!ShouldAbortSnapshot(e, "4_coop")) { BroadcastLoadingAnimationCompletedOnce(t); e.State = LateJoinState.Finalizing; ((MonoBehaviour)Instance).StartCoroutine(BackgroundStateSyncCoroutine(e, allModules)); } yield break; } float nonCoopDeadline = Time.unscaledTime + 6f; while (Time.unscaledTime < nonCoopDeadline) { if (ShouldAbortSnapshot(e, "4_wait")) { yield break; } bool flag2 = false; if (_avatarAnimCompleted != null && GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player2 in GameDirector.instance.PlayerList) { int? obj7; if (player2 == null) { obj7 = null; } else { PhotonView photonView4 = player2.photonView; if (photonView4 == null) { obj7 = null; } else { Player owner2 = photonView4.Owner; obj7 = ((owner2 != null) ? new int?(owner2.ActorNumber) : ((int?)null)); } } if (obj7 != t.ActorNumber) { continue; } try { if (_avatarAnimCompleted.Invoke(player2)) { flag2 = true; } } catch { } break; } } if (flag2) { break; } yield return null; } if (!ShouldAbortSnapshot(e, "4")) { BroadcastLoadingAnimationCompletedOnce(t); e.State = LateJoinState.Finalizing; ((MonoBehaviour)Instance).StartCoroutine(BackgroundStateSyncCoroutine(e, allModules)); ((MonoBehaviour)Instance).StartCoroutine(LateRetryAnimationComplete(t)); } } private static IEnumerator LateRetryAnimationComplete(Player target) { yield return _wait15; if (target == null || PhotonNetwork.CurrentRoom == null || !PhotonNetwork.CurrentRoom.Players.ContainsKey(target.ActorNumber)) { yield break; } try { BroadcastLoadingAnimationCompletedOnce(target); } catch { } } private static IEnumerator BackgroundStateSyncCoroutine(LateJoinEntry e, Module[] modules) { Player t = e.Target; if (IsCoopActorActive(e.Target.ActorNumber)) { int num = ((modules != null) ? modules.Length : 0); int coopActorModuleCount = GetCoopActorModuleCount(e.Target.ActorNumber); if (num > 0 && coopActorModuleCount >= num) { Verbose($"客户端 Module 已齐({coopActorModuleCount}/{num}),跳过墙体兜底重发。"); } else { yield return _wait2; if (ShouldAbortSnapshot(e, "bg_coop")) { yield break; } try { SendModuleSyncToLateJoiner(t, modules); } catch (Exception ex) { Verbose("地图同步失败:" + ex.Message); } } if (e.State == LateJoinState.Finalizing) { e.State = LateJoinState.Done; LogInfo(e.NickName + " 同步完成,已进入游戏。"); } yield break; } yield return _wait2; if (ShouldAbortSnapshot(e, "bg_r1")) { yield break; } try { SendModuleSyncToLateJoiner(t, modules); } catch (Exception ex2) { Verbose("地图同步失败:" + ex2.Message); } if (e.State == LateJoinState.Finalizing) { e.State = LateJoinState.Done; LogInfo(e.NickName + " 同步完成,已进入游戏。"); } yield return _wait2; if (!ShouldAbortSnapshot(e, "bg_r2")) { try { SendModuleSyncToLateJoiner(t, modules); } catch (Exception ex3) { Verbose("地图同步失败:" + ex3.Message); } } } private static bool IsTargetStillOnline(Player p) { if (p != null && PhotonNetwork.CurrentRoom != null) { return PhotonNetwork.CurrentRoom.Players.ContainsKey(p.ActorNumber); } return false; } private static string CurrentSceneName() { RunManager instance = RunManager.instance; object obj; if (instance == null) { obj = null; } else { Level levelCurrent = instance.levelCurrent; obj = ((levelCurrent != null) ? ((Object)levelCurrent).name : null); } if (obj == null) { obj = "<null>"; } return (string)obj; } private static bool ShouldAbortSnapshot(LateJoinEntry e, string stage) { if (!IsStaticModEnabled() || !PhotonNetwork.IsMasterClient) { e.State = LateJoinState.Aborted; return true; } if (e.State == LateJoinState.Aborted) { return true; } if (e.State == LateJoinState.Done || e.State == LateJoinState.Finalizing) { return false; } if (!IsTargetStillOnline(e.Target)) { e.State = LateJoinState.Aborted; Verbose(e.NickName + " 已离线,取消同步。"); return true; } if (CurrentSceneName() != e.StartScene) { e.State = LateJoinState.Aborted; Verbose(e.NickName + " 场景已切换,取消同步。"); return true; } return false; } private static bool IsSnapshotNeededForCurrentScene() { try { return (Object)(object)RunManager.instance != (Object)null && (SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel()); } catch { return false; } } private static IEnumerator WaitClientReadyOrTimeout(LateJoinEntry e, float timeoutSeconds) { float deadline = Time.unscaledTime + timeoutSeconds; float started = Time.unscaledTime; while (Time.unscaledTime < deadline) { if (!IsTargetStillOnline(e.Target) || e.State == LateJoinState.Aborted) { yield break; } if (!IsCoopActorActive(e.Target.ActorNumber)) { Verbose("协同状态消失(" + e.NickName + "),中止 ack 等待走非协同 fallback"); yield break; } if (IsCoopActorReady(e.Target.ActorNumber)) { Verbose($"协同就绪:{e.NickName} 用时 {Time.unscaledTime - started:F2}s"); yield break; } yield return null; } Verbose($"协同就绪等待超时({timeoutSeconds}s),按原流程继续。"); } private static IEnumerator WaitHostGenerated(LateJoinEntry e) { float deadline = Time.unscaledTime + 30f; if (LevelGenerator.Instance?.Generated ?? false) { yield break; } while (Time.unscaledTime < deadline) { if (!IsTargetStillOnline(e.Target) || e.State == LateJoinState.Aborted || (LevelGenerator.Instance?.Generated ?? false)) { yield break; } yield return null; } Verbose("等待关卡生成超时。"); } private static IEnumerator RunAndNotify(IEnumerator coroutine, Action onDone) { yield return ((MonoBehaviour)Instance).StartCoroutine(coroutine); onDone?.Invoke(); } private static void EnsurePlayerCorpseObjects(Player target) { if (!PhotonNetwork.IsMasterClient || target == null) { return; } try { LevelGenerator instance = LevelGenerator.Instance; if ((Object)(object)instance?.PlayerDeathHeadPrefab == (Object)null || (Object)(object)instance.PlayerTumblePrefab == (Object)null || GameDirector.instance?.PlayerList == null || SemiFunc.RunIsLobby()) { return; } PlayerAvatar av = null; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { int? obj; if (player == null) { obj = null; } else { PhotonView photonView = player.photonView; if (photonView == null) { obj = null; } else { Player owner = photonView.Owner; obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null)); } } if (obj == target.ActorNumber) { av = player; break; } } if (!((Object)(object)av == (Object)null)) { SpawnCorpseComponent(((Object)instance.PlayerDeathHeadPrefab).name, av, delegate(GameObject go) { PlayerDeathHead component = go.GetComponent<PlayerDeathHead>(); component.playerAvatar = av; _playerDeathHeadRef.Invoke(av) = component; }, (Object)(object)_playerDeathHeadRef.Invoke(av) == (Object)null); SpawnCorpseComponent(((Object)instance.PlayerTumblePrefab).name, av, delegate(GameObject go) { PlayerTumble component = go.GetComponent<PlayerTumble>(); component.playerAvatar = av; _playerTumbleRef.Invoke(av) = component; }, (Object)(object)_playerTumbleRef.Invoke(av) == (Object)null); } } catch (Exception ex) { Verbose("补创组件失败:" + ex.Message); } } private static void SpawnCorpseComponent(string prefabName, PlayerAvatar av, Action<GameObject> setup, bool needed) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (needed) { GameObject obj = PhotonNetwork.Instantiate(prefabName, new Vector3(0f, 3000f, 0f), Quaternion.identity, (byte)0, (object[])null); setup(obj); } } private static void SendModuleSyncToLateJoiner(Player target, Module[] modules = null) { if (_modTop == null || _modBottom == null || _modRight == null || _modLeft == null || _modFirst == null || _modDone == null) { return; } if (modules == null) { try { modules = Object.FindObjectsOfType<Module>(); } catch { } } if (modules == null) { return; } int num = 0; int num2 = 0; Module[] array = modules; foreach (Module val in array) { if ((Object)(object)val == (Object)null) { continue; } PhotonView component = ((Component)val).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null || component.ViewID == 0 || !_modDone.Invoke(val)) { num2++; continue; } try { component.RPC("ModuleConnectionSetRPC", target, new object[5] { _modTop.Invoke(val), _modBottom.Invoke(val), _modRight.Invoke(val), _modLeft.Invoke(val), _modFirst.Invoke(val) }); num++; } catch { num2++; } } Verbose($"地图同步:{num} 个(跳过 {num2})。"); } private static IEnumerator SendModuleSyncStreamed(Player target, LateJoinEntry e, Module[] modules) { if (_modTop == null || _modBottom == null || _modRight == null || _modLeft == null || _modFirst == null || _modDone == null || modules == null || modules.Length == 0) { yield break; } LevelGenerator instance = LevelGenerator.Instance; if (instance == null || !instance.Generated) { float setupDeadline = Time.unscaledTime + 5f; while (Time.unscaledTime < setupDeadline) { bool flag = false; foreach (Module val in modules) { if ((Object)(object)val != (Object)null && !_modDone.Invoke(val)) { flag = true; break; } } if (!flag) { break; } yield return null; if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target)) { yield break; } } } int sent = 0; int skip = 0; int batched = 0; foreach (Module val2 in modules) { if ((Object)(object)val2 == (Object)null) { continue; } PhotonView component = ((Component)val2).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null || component.ViewID == 0 || !_modDone.Invoke(val2)) { skip++; continue; } try { component.RPC("ModuleConnectionSetRPC", target, new object[5] { _modTop.Invoke(val2), _modBottom.Invoke(val2), _modRight.Invoke(val2), _modLeft.Invoke(val2), _modFirst.Invoke(val2) }); sent++; } catch { skip++; } batched++; if (batched >= 8) { batched = 0; yield return null; if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target)) { yield break; } } } Verbose($"地图同步:{sent} 个(跳过 {skip}),分帧完成。"); } private static IEnumerator SendValuableSyncStreamed(Player target, LateJoinEntry e) { if (_valSet == null || _valCurrent == null || _valDisc == null || _valHaul == null || ValuableDirector.instance?.valuableList == null) { yield break; } int vs = 0; int ds = 0; int hs = 0; int batched = 0; foreach (ValuableObject valuable in ValuableDirector.instance.valuableList) { if ((Object)(object)valuable == (Object)null) { continue; } PhotonView component = ((Component)valuable).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null || component.ViewID == 0) { continue; } try { if (_valSet.Invoke(valuable)) { component.RPC("DollarValueSetRPC", target, new object[1] { _valCurrent.Invoke(valuable) }); vs++; } if (_valDisc.Invoke(valuable)) { component.RPC("DiscoverRPC", target, Array.Empty<object>()); ds++; } if (_valHaul.Invoke(valuable)) { component.RPC("AddToDollarHaulListRPC", target, Array.Empty<object>()); hs++; } } catch { } batched++; if (batched >= 6) { batched = 0; yield return null; if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target)) { yield break; } } } Verbose($"物品同步:价值 {vs},发现 {ds},出口 {hs},分帧完成。"); } private static FieldRef<ItemAttributes, int> TryItemValueRef() { try { return (AccessTools.Field(typeof(ItemAttributes), "value") != null) ? AccessTools.FieldRefAccess<ItemAttributes, int>("value") : null; } catch { return null; } } private static IEnumerator SendItemAttributesSyncStreamed(Player target, LateJoinEntry e, ItemAttributes[] items) { if (_itemValueRef == null || items == null || items.Length == 0) { yield break; } int sent = 0; int skipped = 0; int batched = 0; foreach (ItemAttributes val in items) { if ((Object)(object)val == (Object)null) { skipped++; continue; } PhotonView component = ((Component)val).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null || component.ViewID == 0) { skipped++; continue; } try { int num = _itemValueRef.Invoke(val); if (num > 0) { component.RPC("GetValueRPC", target, new object[1] { num }); sent++; } } catch { skipped++; } batched++; if (batched >= 8) { batched = 0; yield return null; if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target)) { yield break; } } } Verbose($"Item 价格同步:sent={sent} skipped={skipped},分帧完成。"); } private static int BroadcastLoadingAnimationCompletedOnce(Player target) { int num = 0; try { IEnumerable<PlayerAvatar> enumerable2; if (GameDirector.instance?.PlayerList == null) { IEnumerable<PlayerAvatar> enumerable = Object.FindObjectsOfType<PlayerAvatar>(); enumerable2 = enumerable; } else { IEnumerable<PlayerAvatar> enumerable = GameDirector.instance.PlayerList; enumerable2 = enumerable; } foreach (PlayerAvatar item in enumerable2) { if ((Object)(object)item?.photonView != (Object)null) { try { item.photonView.RPC("LoadingLevelAnimationCompletedRPC", target, Array.Empty<object>()); num++; } catch { } } } } catch { } return num; } public static void SafeDestroySlowProjectile(Object obj) { if (obj == (Object)null) { Object.Destroy(obj); return; } GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { Object.Destroy(obj); return; } if (!PhotonNetwork.IsMasterClient || PhotonNetwork.LocalPlayer == null || PhotonNetwork.CurrentRoom == null) { Object.Destroy((Object)(object)val); return; } bool flag = false; try { flag = GameManager.Multiplayer(); } catch { } if (!flag) { Object.Destroy((Object)(object)val); return; } PhotonView val2 = null; try { val2 = val.GetComponent<PhotonView>(); } catch { } if ((Object)(object)val2 == (Object)null || val2.ViewID == 0) { Object.Destroy((Object)(object)val); return; } SlowProjectile val3 = null; try { val3 = val.GetComponent<SlowProjectile>(); } catch { } if ((Object)(object)val3 == (Object)null) { Object.Destroy((Object)(object)val); return; } try { if (_ownershipUpdate != null) { _projectileOwnershipScratch[0] = val2.ViewID; _projectileOwnershipScratch[1] = PhotonNetwork.LocalPlayer.ActorNumber; _ownershipUpdate(_projectileOwnershipScratch, PhotonNetwork.LocalPlayer.ActorNumber); } if ((Object)(object)val == (Object)null) { Verbose($"[SlowProjectile] ViewID={val2.ViewID} 在所有权转让后已被销毁,跳过 PhotonNetwork.Destroy。"); return; } PhotonNetwork.Destroy(val); Verbose($"[SlowProjectile] ViewID={val2.ViewID} 已通过 PhotonNetwork.Destroy 清理服务器缓存。"); return; } catch (Exception ex) { Verbose($"[SlowProjectile] PhotonNetwork.Destroy 失败(ViewID={((val2 != null) ? new int?(val2.ViewID) : ((int?)null))}),回退到 Object.Destroy:{ex.Message}"); } try { Object.Destroy((Object)(object)val); } catch { } } private static FieldRef<DataDirector, string> TryFieldRefRoom() { try { return (AccessTools.Field(typeof(DataDirector), "networkServerName") != null) ? AccessTools.FieldRefAccess<DataDirector, string>("networkServerName") : null; } catch { return null; } } private static FieldRef<RunManager, List<PlayerVoiceChat>> TryFieldRefRM() { try { return (AccessTools.Field(typeof(RunManager), "voiceChats") != null) ? AccessTools.FieldRefAccess<RunManager, List<PlayerVoiceChat>>("voiceChats") : null; } catch { return null; } } internal static void HideRoomForChangeLevelCooldown() { if (PhotonNetwork.IsMasterClient && PhotonNetwork.CurrentRoom != null && !((Object)(object)Instance == (Object)null)) { LockRoomNow(); _changeLevelCooldownUntil = Time.unscaledTime + 600f; _steamUnlockEnsureNextAt = 0f; if (!_changeLevelCooldownRunning) { _changeLevelCooldownRunning = true; ((MonoBehaviour)Instance).StartCoroutine(CooldownCoroutine()); } } } private static IEnumerator CooldownCoroutine() { try { float t0 = Time.unscaledTime; LevelGenerator old = LevelGenerator.Instance; bool newSeen = false; while (Time.unscaledTime - t0 < 60f) { if (!IsCooldownStillRelevant()) { yield break; } LevelGenerator instance = LevelGenerator.Instance; if ((Object)(object)instance != (Object)null && instance != old) { newSeen = true; break; } if ((Object)(object)old == (Object)null && (Object)(object)instance != (Object)null) { newSeen = true; break; } yield return _waitGeneratedPoll; } if (newSeen) { while (Time.unscaledTime - t0 < 60f) { if (!IsCooldownStillRelevant()) { yield break; } GameDirector instance2 = GameDirector.instance; if (instance2 != null && (int)instance2.currentState == 2) { break; } yield return _waitGeneratedPoll; } } if (IsCooldownStillRelevant()) { float num = Time.unscaledTime - t0; Verbose($"房间已解锁(等待 {num:F1}s)。"); _changeLevelCooldownUntil = Time.unscaledTime + 0f; } } finally { _changeLevelCooldownRunning = false; } } private static bool IsCooldownStillRelevant() { if (PhotonNetwork.IsMasterClient) { return PhotonNetwork.CurrentRoom != null; } return false; } private static bool LockRoomNow() { if (PhotonNetwork.CurrentRoom == null) { return false; } bool result = false; if (PhotonNetwork.CurrentRoom.IsOpen) { PhotonNetwork.CurrentRoom.IsOpen = false; result = true; } if (PhotonNetwork.CurrentRoom.IsVisible) { PhotonNetwork.CurrentRoom.IsVisible = false; result = true; } if (!_steamLobbyLocked) { try { SteamManager instance = SteamManager.instance; if (instance != null) { instance.LockLobby(); } _steamLobbyLocked = true; result = true; } catch { } } return result; } internal static void EnforceLobbyState() { if (!PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null || (Object)(object)SteamManager.instance == (Object)null) { return; } if (IsInPreGameMenu()) { _lastSceneAllowed = true; return; } try { if (IsInChangeLevelCooldown) { LockRoomNow(); return; } if (IsCurrentSceneAllowed()) { EnforceAllowedSceneState(); _lastSceneAllowed = true; return; } LockRoomNow(); if (_lastSceneAllowed != false) { _lastSceneAllowed = false; Verbose("场景不允许中途加入,房间已锁定。"); } } catch (Exception ex) { LogWarning("房间状态维护失败:" + ex.Message); } } private static bool IsSteamLobbyActuallyLocked() { try { if (_steamPrivateLobbyField == null) { _steamPrivateLobbyField = AccessTools.Field(typeof(SteamManager), "privateLobby"); } if (_steamPrivateLobbyField == null) { return true; } return (bool)_steamPrivateLobbyField.GetValue(SteamManager.instance); } catch { return true; } } private static void EnforceAllowedSceneState() { Room currentRoom = PhotonNetwork.CurrentRoom; bool flag = false; bool flag2 = IsPublicGame(); if (!currentRoom.IsOpen) { currentRoom.IsOpen = true; flag = true; } if (_steamLobbyLocked) { try { SteamManager.instance.UnlockLobby(flag2); _steamLobbyLocked = false; flag = true; } catch { } } else { float unscaledTime = Time.unscaledTime; if (unscaledTime >= _steamUnlockEnsureNextAt) { _steamUnlockEnsureNextAt = unscaledTime + 5f; if (IsSteamLobbyActuallyLocked()) { try { SteamManager.instance.UnlockLobby(flag2); } catch { } } } } if (flag2) { if (!currentRoom.IsVisible) { currentRoom.IsVisible = true; flag = true; } GameManager instance = GameManager.instance; if (instance != null) { instance.SetConnectRandom(true); } } if (flag) { Verbose("房间已开放。"); } } internal static void NetworkConnectOnConnectedToMasterPrefix() { try { if (IsStaticModEnabled() && CompatibilityReport.RuntimeReady && PublicRoomPrefixEnabled && !((Object)(object)DataDirector.instance == (Object)null) && !((Object)(object)GameManager.instance == (Object)null) && _networkServerNameRef != null && IsPublicGame()) { ref string reference = ref _networkServerNameRef.Invoke(DataDirector.instance); if (!string.IsNullOrEmpty(reference) && !HasRoomNamePrefix(reference)) { reference = (UseChinese() ? "[中途加入] " : "[Late Join] ") + reference; LogInfo("房间名称已更新。"); } } } catch (Exception ex) { Verbose("网络连接前置处理失败:" + ex.Message); } } private static bool HasRoomNamePrefix(string name) { if (name != null) { if (!name.StartsWith("[中途加入] ", StringComparison.Ordinal)) { return name.StartsWith("[Late Join] ", StringComparison.Ordinal); } return true; } return false; } private static bool IsPublicGame() { if ((Object)(object)GameManager.instance == (Object)null) { return false; } try { if (CompatibilityReport.GameManagerConnectRandomRef != null) { return CompatibilityReport.GameManagerConnectRandomRef.Invoke(GameManager.instance); } object obj = CompatibilityReport.GameManagerConnectRandomField?.GetValue(GameManager.instance); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } internal static bool ClearInstantiationCacheById(int id) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (id == 0 || !CompatibilityReport.CanCleanPhotonCache) { return false; } try { Hashtable val = new Hashtable(); object value = CompatibilityReport.PhotonKeyByteSeven.GetValue(null); RaiseEventOptions val2 = new RaiseEventOptions(); val[value] = id; val2.CachingOption = (EventCaching)6; CompatibilityReport.PhotonRaiseEventInternal.Invoke(null, new object[4] { (byte)202, val, val2, SendOptions.SendReliable }); return true; } catch (Exception ex) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)("缓存清理失败:" + ex.Message)); } return false; } } internal static void ClearPhotonInstantiationCache(PhotonView pv) { if ((Object)(object)pv != (Object)null) { ClearInstantiationCacheById(pv.InstantiationId); } } internal static void ClearAllScenePhotonCacheWithKeepPublic(HashSet<int> keep) { ClearAllScenePhotonCacheWithKeepPublic(keep, clearPersistent: false); } internal static void ClearAllScenePhotonCacheWithKeepPublic(HashSet<int> keep, bool clearPersistent) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) try { int num = 0; int num2 = 0; int num3 = 0; PhotonView[] array = Object.FindObjectsOfType<PhotonView>(); foreach (PhotonView val in array) { if ((Object)(object)val == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).buildIndex == -1 || keep.Contains(val.ViewID) || (!clearPersistent && IsPlayerPersistentByName(val))) { continue; } if (val.ViewID != 0) { try { PhotonNetwork.RemoveBufferedRPCs(val.ViewID, (string)null, (int[])null); num3++; } catch { } } if (val.InstantiationId == 0) { continue; } if (val.isRuntimeInstantiated && val.IsMine) { t