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 _cfgModEnabled; private static ConfigEntry _cfgPublicRoomPrefix; private static ConfigEntry _cfgTeleportToHostOnJoin; private static ConfigEntry _cfgCoopMode; private static ConfigEntry _cfgAllowLobby; private static ConfigEntry _cfgAllowShop; private static ConfigEntry _cfgAllowLevel; private static ConfigEntry _cfgHudEnabled; private static ConfigEntry _cfgShowJoinStatus; private static ConfigEntry _cfgHudShowRoomType; private static ConfigEntry _cfgHudShowRoomName; private static ConfigEntry _cfgHudShowPlayerCount; private static ConfigEntry _cfgHudFontSize; private static ConfigEntry _cfgHudOpacity; private static ConfigEntry _cfgVerboseLog; private static ConfigEntry _cfgShowConflictWarning; private float _enforceTimer; private const float EnforceInterval = 1f; private static readonly FieldRef _roundAllExtractionPointsCompletedRef = TryFieldRef("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 _hudLines = new List(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 _emptyList = new List(0); private static readonly List _lateJoinStatusLines = new List(4); private static readonly List _hudEntriesScratch = new List(8); private static readonly Comparison _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 _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 _lateJoinEntries = new Dictionary(); private static readonly Queue _lateJoinQueue = new Queue(); private static bool _schedulerRunning; private static readonly FieldRef _playerDeathHeadRef = AccessTools.FieldRefAccess("playerDeathHead"); private static readonly FieldRef _playerTumbleRef = AccessTools.FieldRefAccess("tumble"); private static readonly FieldRef _avatarAnimCompleted = TryAvatarAnimCompletedRef(); private static readonly FieldRef _modTop = AccessTools.FieldRefAccess("ConnectingTop"); private static readonly FieldRef _modBottom = AccessTools.FieldRefAccess("ConnectingBottom"); private static readonly FieldRef _modRight = AccessTools.FieldRefAccess("ConnectingRight"); private static readonly FieldRef _modLeft = AccessTools.FieldRefAccess("ConnectingLeft"); private static readonly FieldRef _modFirst = AccessTools.FieldRefAccess("First"); private static readonly FieldRef _modDone = AccessTools.FieldRefAccess("SetupDone"); private static readonly FieldRef _valSet = AccessTools.FieldRefAccess("dollarValueSet"); private static readonly FieldRef _valCurrent = AccessTools.FieldRefAccess("dollarValueCurrent"); private static readonly FieldRef _valDisc = AccessTools.FieldRefAccess("discovered"); private static readonly FieldRef _valHaul = AccessTools.FieldRefAccess("inStartRoom"); private static readonly FieldRef> _menuLobbyJoiningPlayersRef = TryMenuLobbyListRef("joiningPlayers"); private static readonly FieldRef _menuLobbyJoiningTimerRef = TryMenuLobbyFloatRef("joiningPlayersTimer"); private static readonly FieldRef _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 _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 _networkServerNameRef = TryFieldRefRoom(); private static readonly FieldRef> _runManagerVoiceChatsRef = TryFieldRefRM(); private static readonly HashSet _scenePersistentViewIds = new HashSet(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 _coopReadyByActor = new Dictionary(8); private static readonly Dictionary _coopModuleCountByActor = new Dictionary(8); private static readonly Dictionary _coopByActor = new Dictionary(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.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 TryFieldRef(string name) { try { return (AccessTools.Field(typeof(TInst), name) != null) ? AccessTools.FieldRefAccess(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(10, 28)); _cfgHudOpacity = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Opacity/%", "文字透明度/%", "HUD opacity.", "HUD 透明度。", 80, (AcceptableValueBase)(object)new AcceptableValueRange(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 cosmeticsList = new List(8); try { List 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(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.SetupCosmetics(false, true, (List)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(); 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)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(); 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(); 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 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 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)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 TryAvatarPlayerNameRef() { try { return (AccessTools.Field(typeof(PlayerAvatar), "playerName") != null) ? AccessTools.FieldRefAccess("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(); } 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 TryAvatarAnimCompletedRef() { try { return (AccessTools.Field(typeof(PlayerAvatar), "levelAnimationCompleted") != null) ? AccessTools.FieldRefAccess("levelAnimationCompleted") : null; } catch { return null; } } private static FieldRef> TryMenuLobbyListRef(string name) { try { return (AccessTools.Field(typeof(MenuPageLobby), name) != null) ? AccessTools.FieldRefAccess>(name) : null; } catch { return null; } } private static FieldRef TryMenuLobbyFloatRef(string name) { try { return (AccessTools.Field(typeof(MenuPageLobby), name) != null) ? AccessTools.FieldRefAccess(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 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(); if (!((Object)(object)componentInChildren == (Object)null)) { PhotonView component = ((Component)componentInChildren).GetComponent(); 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(); } 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(); 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 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() : 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 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() : 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 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() : 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 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() : 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()); } 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()); } } } 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(); } catch { } try { allPVs = Object.FindObjectsOfType(); } catch { } try { items = Object.FindObjectsOfType(); } 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()); } } } 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 = ""; } 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(); 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(); 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 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(); } 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(); 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(); 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(); 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()); ds++; } if (_valHaul.Invoke(valuable)) { component.RPC("AddToDollarHaulListRPC", target, Array.Empty()); 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 TryItemValueRef() { try { return (AccessTools.Field(typeof(ItemAttributes), "value") != null) ? AccessTools.FieldRefAccess("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(); 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 enumerable2; if (GameDirector.instance?.PlayerList == null) { IEnumerable enumerable = Object.FindObjectsOfType(); enumerable2 = enumerable; } else { IEnumerable 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()); 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(); } catch { } if ((Object)(object)val2 == (Object)null || val2.ViewID == 0) { Object.Destroy((Object)(object)val); return; } SlowProjectile val3 = null; try { val3 = val.GetComponent(); } 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 TryFieldRefRoom() { try { return (AccessTools.Field(typeof(DataDirector), "networkServerName") != null) ? AccessTools.FieldRefAccess("networkServerName") : null; } catch { return null; } } private static FieldRef> TryFieldRefRM() { try { return (AccessTools.Field(typeof(RunManager), "voiceChats") != null) ? AccessTools.FieldRefAccess>("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 keep) { ClearAllScenePhotonCacheWithKeepPublic(keep, clearPersistent: false); } internal static void ClearAllScenePhotonCacheWithKeepPublic(HashSet 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(); 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) { try { PhotonNetwork.Destroy(((Component)val).gameObject); num++; } catch { goto IL_00ab; } continue; } goto IL_00ab; IL_00ab: ClearPhotonInstantiationCache(val); num2++; } Verbose($"场景缓存清理:Destroy {num} 个,反射清理 {num2} 个,RPC清理 {num3} 个。"); } catch (Exception ex) { Verbose("场景缓存清理失败:" + ex.Message); } } internal static void DestroyAllSceneValuablesAndItemsWithKeepPublic(HashSet keep) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient) { return; } try { int num = 0; int num2 = 0; PhotonView[] array = Object.FindObjectsOfType(); 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) || IsPlayerPersistentByName(val) || val.InstantiationId == 0 || !val.isRuntimeInstantiated) { continue; } if (val.IsMine || val.Owner == null) { try { PhotonNetwork.Destroy(((Component)val).gameObject); num++; } catch { ClearPhotonInstantiationCache(val); num2++; } } else { ClearPhotonInstantiationCache(val); num2++; } } if (num > 0 || num2 > 0) { LogInfo($"切关清理:销毁 {num} 个场景物品,清理缓存 {num2} 个(含其他玩家持有的物品)。"); } } catch (Exception ex) { Verbose("切关物品清理失败:" + ex.Message); } } internal static HashSet BuildPlayerPersistentViewIdSet() { HashSet scenePersistentViewIds = _scenePersistentViewIds; scenePersistentViewIds.Clear(); try { if (GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null) { continue; } if ((Object)(object)player.photonView != (Object)null) { scenePersistentViewIds.Add(player.photonView.ViewID); } if (_playerTumbleRef == null) { continue; } try { PlayerTumble obj = _playerTumbleRef.Invoke(player); PhotonView val = ((obj != null) ? ((Component)obj).GetComponent() : null); if ((Object)(object)val != (Object)null) { scenePersistentViewIds.Add(val.ViewID); } } catch { } } } } catch (Exception ex) { Verbose("收集玩家视图失败:" + ex.Message); } try { if (_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) { List list = _runManagerVoiceChatsRef.Invoke(RunManager.instance); if (list != null) { foreach (PlayerVoiceChat item in list) { PhotonView val2 = ((item != null) ? ((Component)item).GetComponent() : null); if ((Object)(object)val2 != (Object)null) { scenePersistentViewIds.Add(val2.ViewID); } } } } } catch (Exception ex2) { Verbose("收集语音视图失败:" + ex2.Message); } return scenePersistentViewIds; } internal static bool IsPlayerPersistentByName(PhotonView v) { object obj; if (v == null) { obj = null; } else { GameObject gameObject = ((Component)v).gameObject; obj = ((gameObject != null) ? ((Object)gameObject).name : null); } string text = (string)obj; if (string.IsNullOrEmpty(text)) { return false; } if (!text.StartsWith("Voice", StringComparison.Ordinal) && !text.StartsWith("Player Avatar", StringComparison.Ordinal)) { return text.StartsWith("Player Tumble", StringComparison.Ordinal); } return true; } internal static void ClearRunManagerPunBufferedRpcs() { if (!CompatibilityReport.CanReadRunManagerPun || (Object)(object)RunManager.instance == (Object)null) { return; } try { RunManagerPUN val = CompatibilityReport.RunManagerPunRef.Invoke(RunManager.instance); PhotonView val2 = (((Object)(object)val != (Object)null) ? CompatibilityReport.RunManagerPunPhotonViewRef.Invoke(val) : null); if (!((Object)(object)val2 == (Object)null)) { PhotonNetwork.RemoveBufferedRPCs(val2.ViewID, (string)null, (int[])null); Verbose("缓冲指令清理完成。"); } } catch (Exception ex) { Verbose("缓冲指令清理失败:" + ex.Message); } } internal static void TryJoinSteamLobbyFromPhotonRoom() { try { if (PhotonNetwork.IsMasterClient || (Object)(object)SteamManager.instance == (Object)null) { return; } Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom == null || !ulong.TryParse(currentRoom.Name, out var result) || result == 0L) { return; } if (_steamCurrentLobbyField == null) { _steamCurrentLobbyField = AccessTools.Field(typeof(SteamManager), "currentLobby"); } if (_steamCurrentLobbyField == null) { return; } object value = _steamCurrentLobbyField.GetValue(SteamManager.instance); if (value == null) { return; } if (_steamLobbyIdProp == null) { _steamLobbyIdProp = value.GetType().GetProperty("Id"); } if (_steamLobbyIdProp == null) { return; } object value2 = _steamLobbyIdProp.GetValue(value); if (value2 == null) { return; } if (_steamLobbyIdIsValidProp == null) { _steamLobbyIdIsValidProp = value2.GetType().GetProperty("IsValid"); } if (_steamLobbyIdIsValidProp != null) { object value3 = _steamLobbyIdIsValidProp.GetValue(value2); bool flag = default(bool); int num; if (value3 is bool) { flag = (bool)value3; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { Verbose("Steam 大厅已有效,跳过加入。"); return; } } if (_steamJoinLobbyMethod == null) { _steamJoinLobbyMethod = AccessTools.Method(typeof(SteamManager), "JoinLobby", (Type[])null, (Type[])null); } if (!(_steamJoinLobbyMethod == null)) { Type parameterType = _steamJoinLobbyMethod.GetParameters()[0].ParameterType; if (_steamIdValueField == null) { _steamIdValueField = AccessTools.Field(parameterType, "Value"); } if (!(_steamIdValueField == null)) { object obj = Activator.CreateInstance(parameterType); _steamIdValueField.SetValue(obj, result); _steamJoinLobbyMethod.Invoke(SteamManager.instance, new object[1] { obj }); LogInfo($"客户端已加入 Steam 大厅 {result},邀请好友功能已恢复。"); } } } catch (Exception ex) { Verbose("TryJoinSteamLobbyFromPhotonRoom 失败:" + ex.Message); } } private void InstallPatchesSafely() { Pre("ChangeLevel Pre", typeof(RunManager), "ChangeLevel", "RunManagerChangeLevelPrefix"); Post("ChangeLevel Post", typeof(RunManager), "ChangeLevel", "RunManagerChangeLevelPostfix"); Pre("RestartScene Pre", typeof(RunManager), "RestartScene", "RunManagerRestartScenePrefix"); Pre("LevelGenerator.Start Pre", typeof(LevelGenerator), "Start", "LevelGeneratorStartPrefix"); Post("PlayerAvatar.Start Post", typeof(PlayerAvatar), "Start", "PlayerAvatarStartPostfix"); Pre("LoadingUI.AnimStart Pre", typeof(LoadingUI), "LevelAnimationStart", "LoadingUILevelAnimationStartPrefix"); Pre("LoadingAnimCompleted Pre", typeof(PlayerAvatar), "LoadingLevelAnimationCompletedRPC", "PlayerAvatarLoadingLevelAnimationCompletedRPCPrefix"); Pre("OwnerOnlyRPC Pre", typeof(SemiFunc), "OwnerOnlyRPC", "SemiFuncOwnerOnlyRPCPrefix"); Post("OnPlayerEntered Post", typeof(NetworkManager), "OnPlayerEnteredRoom", "NetworkManagerOnPlayerEnteredRoomPostfix"); Post("OnPlayerLeft Post", typeof(NetworkManager), "OnPlayerLeftRoom", "NetworkManagerOnPlayerLeftRoomPostfix"); Pre("OnConnectedToMaster Pre", typeof(NetworkConnect), "OnConnectedToMaster", "NetworkConnectOnConnectedToMasterPrefix"); Post("ExtractionPoint.UpdateLock Post", typeof(ExtractionPoint), "UpdateLock", "ExtractionPointUpdateLockPostfix"); Post("OnJoinedRoom Post", typeof(NetworkConnect), "OnJoinedRoom", "NetworkConnectOnJoinedRoomPostfix"); Post("OnDisconnected Post", typeof(NetworkManager), "OnDisconnected", "NetworkManagerOnDisconnectedPostfix"); Post("PlayerSpawnedRPC Post", typeof(NetworkManager), "PlayerSpawnedRPC", "NetworkManagerPlayerSpawnedRPCPostfix"); Pre("GumballHypnosis Pre", typeof(GumballValuable), "UpdateHypnosisLinesList", "GumballValuableUpdateHypnosisLinesListPrefix"); Pre("GumballEffectsLoop Pre", typeof(GumballValuable), "ClientEffectsLoop", "GumballValuableClientEffectsLoopPrefix"); Post("EnemyOnScreen.Awake Post", typeof(EnemyOnScreen), "Awake", "EnemyOnScreenAwakePostfix"); Post("Module.Awake Post", typeof(Module), "Awake", "ModuleAwakePostfix"); Post("PhysGrabObj.Awake Post", typeof(PhysGrabObject), "Awake", "PhysGrabObjectAwakePostfix"); Post("ItemAttributes.Awake Post", typeof(ItemAttributes), "Awake", "ItemAttributesAwakePostfix"); Post("ValuableObject.Awake Post", typeof(ValuableObject), "Awake", "ValuableObjectAwakePostfix"); Post("PlayerAvatar.Awake Post", typeof(PlayerAvatar), "Awake", "PlayerAvatarAwakeSetOwnershipPostfix"); Post("PlayerAvatar.Destroy Post", typeof(PlayerAvatar), "OnDestroy", "PlayerAvatarOnDestroyPostfix"); Pre("LevelGen.PlayerSpawn Pre", typeof(LevelGenerator), "PlayerSpawn", "LevelGeneratorPlayerSpawnPrefix"); Post("VoiceChat.Start Post", typeof(PlayerVoiceChat), "Start", "PlayerVoiceChatStartPostfix"); Pre("EnergyUI.Update Pre", typeof(EnergyUI), "Update", "EnergyUIUpdatePrefix"); Pre("PlayerAvatarVisuals.Update Pre", typeof(PlayerAvatarVisuals), "Update", "PlayerAvatarVisualsUpdatePrefix"); Pre("PlayerExpression.Update Pre", typeof(PlayerExpression), "Update", "PlayerExpressionUpdatePrefix"); Pre("PlayerAvatarTalkAnim.Update Pre", typeof(PlayerAvatarTalkAnimation), "Update", "PlayerAvatarTalkAnimationUpdatePrefix"); Pre("PlayerLocalCamera.GetOverride Pre", typeof(PlayerLocalCamera), "GetOverrideTransform", "PlayerLocalCameraGetOverrideTransformPrefix"); Pre("PlayerAvatarOvercharge.Update Pre", typeof(PlayerAvatarOverchargeVisuals), "Update", "PlayerAvatarOverchargeVisualsUpdatePrefix"); Pre("PlayerAvatarGrabDis.Update Pre", typeof(PlayerAvatarGrabDisableVisuals), "Update", "PlayerAvatarGrabDisableVisualsUpdatePrefix"); Pre("PhysGrabber.Update Pre", typeof(PhysGrabber), "Update", "PhysGrabberUpdatePrefix"); TryPatch("PhysGrabber.Update Trans", delegate { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(PhysGrabber), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(LateJoinNowHooks), "PhysGrabberUpdateTranspiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); }); Pre("PlayerAvatarLeftArm.Update Pre", typeof(PlayerAvatarLeftArm), "Update", "PlayerAvatarLeftArmUpdatePrefix"); Pre("ItemEquippable.UpdateState Pre", typeof(ItemEquippable), "RPC_UpdateItemState", "ItemEquippableUpdateItemStatePrefix"); Pre("UpdatePlayerVoiceChat Pre", typeof(PlayerAvatar), "UpdateMyPlayerVoiceChat", "PlayerAvatarUpdateMyPlayerVoiceChatPrefix"); TryPatch("SlowProjectile.DestroyRPC Trans", delegate { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(SlowProjectile), "DestroyRPC", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(LateJoinNowHooks), "SlowProjectileDestroyRPCTranspiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); }); Pre("PhysGrabObject.DestroyRPC Pre", typeof(PhysGrabObject), "DestroyPhysGrabObjectRPC", "PhysGrabObjectDestroyPhysGrabObjectRPCPrefix"); void Post(string l, Type t, string m, string h) { TryPatch(l, delegate { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(t, m, (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(LateJoinNowHooks), h, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); }); } void Pre(string l, Type t, string m, string h) { TryPatch(l, delegate { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(t, m, (Type[])null, (Type[])null), new HarmonyMethod(typeof(LateJoinNowHooks), h, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); }); } } private void TryPatch(string label, Action action) { try { action(); } catch (Exception ex) { CompatibilityReport.AffectedFeatures.Add(UseChinese() ? ("补丁失败:" + label) : ("Patch failed: " + label)); CompatibilityReport.Details.Add(label + " → " + ex.Message); ((BaseUnityPlugin)this).Logger.LogWarning((object)("补丁安装失败:" + label)); } } public static bool IsCoopActorActive(int actor) { bool value = default(bool); return CoopModeEnabled && PhotonNetwork.IsMasterClient && _coopByActor.TryGetValue(actor, out value) && value; } public static bool IsClientCoopActive() { if (IsStaticModEnabled() && CoopModeEnabled && !PhotonNetwork.IsMasterClient) { return _hostCoopCompatible; } return false; } internal static void AnnouncePresenceAndRescan() { try { if (PhotonNetwork.LocalPlayer == null) { return; } ((Dictionary)(object)_presencePropsScratch).Clear(); _presencePropsScratch[(object)"LateJoinNow.Version"] = "1.0.9"; _presencePropsScratch[(object)"LateJoinNow.Protocol"] = 1; PhotonNetwork.LocalPlayer.SetCustomProperties(_presencePropsScratch, (Hashtable)null, (WebFlags)null); Verbose(string.Format("宣告协同版本:{0} / proto={1}", "1.0.9", 1)); EnsureCoopListenerRegistered(); if (PhotonNetwork.CurrentRoom == null) { return; } foreach (Player value in PhotonNetwork.CurrentRoom.Players.Values) { if (value != null) { UpdateCoopStateFor(value); } } } catch (Exception ex) { Verbose("AnnouncePresence 失败:" + ex.Message); } } private static void EnsureCoopListenerRegistered() { if (_coopListener != null) { return; } try { _coopListener = new PeerCoopCallbackListener(); PhotonNetwork.AddCallbackTarget((object)_coopListener); Verbose("注册 PeerCoop Photon 回调监听。"); } catch (Exception ex) { Verbose("注册回调失败:" + ex.Message); } } private static void UnregisterCoopListener() { if (_coopListener != null) { try { PhotonNetwork.RemoveCallbackTarget((object)_coopListener); } catch { } _coopListener = null; } } internal static void ResetCoopState() { _coopByActor.Clear(); _coopReadyByActor.Clear(); _coopModuleCountByActor.Clear(); _hostCoopCompatible = false; } internal static void HandleMasterClientSwitched(Player newMasterClient) { if (newMasterClient == null) { return; } LogInfo($"主机切换为 {newMasterClient.NickName}(actor {newMasterClient.ActorNumber})。"); _coopByActor.Clear(); _coopReadyByActor.Clear(); _coopModuleCountByActor.Clear(); _hostCoopCompatible = false; if (PhotonNetwork.IsMasterClient) { if (PhotonNetwork.CurrentRoom == null) { return; } { foreach (Player value in PhotonNetwork.CurrentRoom.Players.Values) { if (value != null) { UpdateCoopStateFor(value); } } return; } } ResetClientCoopState(); UpdateCoopStateFor(newMasterClient); } internal static void UpdateCoopStateFor(Player p) { if (p == null || PhotonNetwork.CurrentRoom == null) { return; } try { bool flag = ReadPeerCompatible(p); if (PhotonNetwork.IsMasterClient) { if (p.ActorNumber != PhotonNetwork.LocalPlayer.ActorNumber) { bool value; bool num = _coopByActor.TryGetValue(p.ActorNumber, out value) && value; _coopByActor[p.ActorNumber] = flag; if (num != flag) { LogInfo("玩家 " + (p.NickName ?? p.ActorNumber.ToString()) + " 协同状态:" + (flag ? "兼容" : "不兼容")); } } } else if (p.IsMasterClient) { bool hostCoopCompatible = _hostCoopCompatible; _hostCoopCompatible = flag; if (hostCoopCompatible != flag) { LogInfo("主机协同状态:" + (flag ? "兼容(启用客户端协同补丁)" : "不兼容(客户端 mod 休眠)")); } } } catch (Exception ex) { Verbose("UpdateCoopStateFor 失败:" + ex.Message); } } internal static void RemoveCoopActor(int actor) { if (_coopByActor.Remove(actor)) { Verbose($"清理协同表:actor {actor}"); } _coopReadyByActor.Remove(actor); _coopModuleCountByActor.Remove(actor); } internal static void AnnounceCoopReady(int receivedModuleCount = 0) { try { if (PhotonNetwork.LocalPlayer != null) { long ticks = DateTime.UtcNow.Ticks; ((Dictionary)(object)_readyPropsScratch).Clear(); _readyPropsScratch[(object)"LateJoinNow.Ready"] = ticks; _readyPropsScratch[(object)"LateJoinNow.ModuleCount"] = receivedModuleCount; PhotonNetwork.LocalPlayer.SetCustomProperties(_readyPropsScratch, (Hashtable)null, (WebFlags)null); Verbose($"宣告客户端协同就绪 ts={ticks} modules={receivedModuleCount}"); } } catch (Exception ex) { Verbose("AnnounceCoopReady 失败:" + ex.Message); } } internal static void ClearOwnCoopProperties() { try { if (PhotonNetwork.LocalPlayer != null) { ((Dictionary)(object)_clearPropsScratch).Clear(); _clearPropsScratch[(object)"LateJoinNow.Version"] = null; _clearPropsScratch[(object)"LateJoinNow.Protocol"] = null; _clearPropsScratch[(object)"LateJoinNow.Ready"] = null; _clearPropsScratch[(object)"LateJoinNow.ModuleCount"] = null; PhotonNetwork.LocalPlayer.SetCustomProperties(_clearPropsScratch, (Hashtable)null, (WebFlags)null); Verbose("已清除本机 Coop CustomProperties"); } } catch (Exception ex) { Verbose("清 Coop properties 失败:" + ex.Message); } } internal static bool IsCoopActorReady(int actor) { if (_coopReadyByActor.TryGetValue(actor, out var value)) { return value > 0; } return false; } internal static int GetCoopActorModuleCount(int actor) { if (!_coopModuleCountByActor.TryGetValue(actor, out var value)) { return 0; } return value; } internal static void ResetCoopReadyFor(int actor) { _coopReadyByActor.Remove(actor); _coopModuleCountByActor.Remove(actor); } private static void RecordCoopReadyIfPresent(Player p) { if (p != null && p.CustomProperties != null) { if (((Dictionary)(object)p.CustomProperties).TryGetValue((object)"LateJoinNow.Ready", out object value) && value is long num && num > 0) { _coopReadyByActor[p.ActorNumber] = num; } if (((Dictionary)(object)p.CustomProperties).TryGetValue((object)"LateJoinNow.ModuleCount", out object value2) && value2 is int num2 && num2 >= 0) { _coopModuleCountByActor[p.ActorNumber] = num2; } if (_coopReadyByActor.ContainsKey(p.ActorNumber)) { Verbose($"收到客户端就绪 ack:{p.NickName} modules={GetCoopActorModuleCount(p.ActorNumber)}"); } } } internal static void RecordCoopReadyForListener(Player p) { RecordCoopReadyIfPresent(p); } private static bool ReadPeerCompatible(Player p) { if (p == null || p.CustomProperties == null) { return false; } try { if (!((Dictionary)(object)p.CustomProperties).TryGetValue((object)"LateJoinNow.Version", out object value)) { return false; } if (!(value is string text) || text != "1.0.9") { return false; } if (!((Dictionary)(object)p.CustomProperties).TryGetValue((object)"LateJoinNow.Protocol", out object value2)) { return false; } if (!(value2 is int num) || num != 1) { return false; } return true; } catch { return false; } } private static void TryShowMenuPopup(MenuManager mm, string title, Color color, string body, string label) { //IL_0002: 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) try { mm.PagePopUp(title, color, body, "OK", true); return; } catch { } try { mm.PagePopUpScheduled(title, color, body, "OK", true); } catch { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)("无法显示 " + label + " 弹窗。")); } } } private IEnumerator WaitForMenuReady(float deadline, Action onReady) { float until = Time.unscaledTime + deadline; while (Time.unscaledTime < until) { MenuManager instance = MenuManager.instance; if ((Object)(object)instance != (Object)null && !IsPopupSlotBusy(instance) && CompatibilityReport.CanShowMenuPopup) { yield return null; instance = MenuManager.instance; if ((Object)(object)instance != (Object)null && !IsPopupSlotBusy(instance)) { onReady?.Invoke(instance); break; } } yield return null; } } private static bool IsPopupSlotBusy(MenuManager mm) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 if ((Object)(object)mm == (Object)null || !CompatibilityReport.CanShowMenuPopup) { return true; } try { if ((Object)(object)CompatibilityReport.MenuCurrentMenuPageRef.Invoke(mm) == (Object)null) { return true; } if (CompatibilityReport.MenuPagePopUpScheduledRef.Invoke(mm)) { return true; } MenuPageIndex val = CompatibilityReport.MenuCurrentMenuPageIndexRef.Invoke(mm); return (int)val == 9 || (int)val == 7; } catch { return true; } } private IEnumerator WaitAndShowCompatPopup() { return WaitForMenuReady(15f, delegate(MenuManager mm) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) TryShowMenuPopup(mm, BuildCompatTitle(), new Color(1f, 0.78f, 0.2f), BuildCompatBody(), "兼容提示"); }); } private static string BuildCompatTitle() { if (!UseChinese()) { return "LateJoinNow Compatibility"; } return "LateJoinNow 兼容模式"; } private static string BuildCompatBody() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(UseChinese() ? "检测到游戏接口可能已变化,以下功能进入兼容模式:" : "Some game APIs may have changed. The following features may be unavailable:"); stringBuilder.AppendLine().AppendLine(""); foreach (string affectedFeature in CompatibilityReport.AffectedFeatures) { stringBuilder.Append(" • ").AppendLine(affectedFeature); } stringBuilder.AppendLine("").AppendLine(); stringBuilder.Append("").Append(UseChinese() ? "如需使用这些功能,请等待作者适配最新版本。" : "Please wait for the mod to be updated for the latest game version.").Append(""); return stringBuilder.ToString(); } private IEnumerator WaitAndShowConflictPopup() { return WaitForMenuReady(15f, delegate(MenuManager mm) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) TryShowMenuPopup(mm, BuildConflictTitle(), new Color(1f, 0.35f, 0.35f), BuildConflictBody(), "冲突警告"); }); } private static string BuildConflictTitle() { if (!UseChinese()) { return "LateJoinNow Conflict Warning"; } return "LateJoinNow 冲突警告"; } private static string BuildConflictBody() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(UseChinese() ? "检测到本地启用了其他「中途加入」类模组:" : "Detected other late-join style mods alongside LateJoinNow:"); stringBuilder.AppendLine().AppendLine(""); foreach (string detectedConflictMod in ConflictDetect.DetectedConflictMods) { stringBuilder.Append(" • ").AppendLine(detectedConflictMod); } stringBuilder.AppendLine("").AppendLine(); stringBuilder.Append("").Append(UseChinese() ? "建议在模组管理器中禁用上述模组,仅保留 LateJoinNow。" : "Please disable those mods and keep only LateJoinNow.").AppendLine(""); stringBuilder.AppendLine(); stringBuilder.Append("").Append(UseChinese() ? "同时启用会出现状态污染、卡加载等问题。" : "Running them together causes state corruption and loading hangs.").Append(""); return stringBuilder.ToString(); } } internal static class CompatibilityReport { public static FieldInfo PhotonRemoveFilter; public static FieldInfo PhotonKeyByteSeven; public static FieldInfo PhotonServerCleanOptions; public static MethodInfo PhotonRaiseEventInternal; public static FieldInfo GameManagerConnectRandomField; public static FieldInfo GameManagerLobbyTypeField; public static FieldRef GameManagerLobbyTypeRef; public static FieldRef GameManagerConnectRandomRef; public static FieldRef RunManagerPunRef; public static FieldRef RunManagerPunPhotonViewRef; public static FieldRef PlayerAvatarSpawnedRef; public static FieldRef MenuPagePopUpScheduledRef; public static FieldRef MenuCurrentMenuPageIndexRef; public static FieldRef MenuCurrentMenuPageRef; public static bool CanCleanPhotonCache; public static bool CanRefreshLevelGenerator; public static bool CanCatchupLateJoiner; public static bool CanShowMenuPopup; public static bool CanReadRunManagerPun; public static bool CanReadGameManagerLobbyState; public static bool CanAddRoomNamePrefix; public static bool CanFixGumballOverflow; public static bool CanGuardLoadingAnimation; public static bool CanEnsurePlayerCorpse; public static bool CanSyncModuleState; public static bool CanSyncValuableObjectState; private static readonly BindingFlags NPS = BindingFlags.Static | BindingFlags.NonPublic; private static readonly BindingFlags NPI = BindingFlags.Instance | BindingFlags.NonPublic; private static readonly BindingFlags ALL = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public static bool HasIssues { get; private set; } public static bool RuntimeReady { get; private set; } public static List AffectedFeatures { get; } = new List(); public static List Details { get; } = new List(); public static void Build() { HasIssues = false; RuntimeReady = false; AffectedFeatures.Clear(); Details.Clear(); bool flag = Plugin.UseChinese(); CanCleanPhotonCache = TryAll(() => RF(typeof(PhotonNetwork), "removeFilter", NPS, ref PhotonRemoveFilter), () => RF(typeof(PhotonNetwork), "keyByteSeven", NPS, ref PhotonKeyByteSeven), () => RF(typeof(PhotonNetwork), "ServerCleanOptions", NPS, ref PhotonServerCleanOptions), () => RM(typeof(PhotonNetwork), "RaiseEventInternal", new Type[4] { typeof(byte), typeof(object), typeof(RaiseEventOptions), typeof(SendOptions) }, NPS, ref PhotonRaiseEventInternal)); if (!CanCleanPhotonCache) { AffectedFeatures.Add(flag ? "清理 Photon 实例化缓存" : "Photon instantiation cache cleanup"); } RF(typeof(GameManager), "connectRandom", NPI, ref GameManagerConnectRandomField); RF(typeof(GameManager), "lobbyType", NPI, ref GameManagerLobbyTypeField); try { if (GameManagerLobbyTypeField != null) { GameManagerLobbyTypeRef = AccessTools.FieldRefAccess("lobbyType"); } if (GameManagerConnectRandomField != null) { GameManagerConnectRandomRef = AccessTools.FieldRefAccess("connectRandom"); } } catch (Exception ex) { Details.Add("GameManager FieldRef 失败:" + ex.Message); } CanReadRunManagerPun = TryAll(() => RFR("runManagerPUN", out RunManagerPunRef), () => RFR("photonView", out RunManagerPunPhotonViewRef)); if (!CanReadRunManagerPun) { AffectedFeatures.Add(flag ? "清理 RunManager 缓存" : "RunManager buffered RPC cleanup"); } CanRefreshLevelGenerator = typeof(LevelGenerator) != null; CanCatchupLateJoiner = TryAll(() => RFR("spawned", out PlayerAvatarSpawnedRef), () => RME(typeof(PlayerAvatar), "LoadingLevelAnimationCompletedRPC")); if (!CanCatchupLateJoiner) { AffectedFeatures.Add(flag ? "晚加入玩家跳过加载动画" : "Late joiner load-animation catch-up"); } CanShowMenuPopup = TryAll(() => RFR("pagePopUpScheduled", out MenuPagePopUpScheduledRef), () => RFR("currentMenuPageIndex", out MenuCurrentMenuPageIndexRef), () => RFR("currentMenuPage", out MenuCurrentMenuPageRef)); if (!CanShowMenuPopup) { AffectedFeatures.Add(flag ? "菜单弹窗提示" : "Menu popup notifications"); } CanReadGameManagerLobbyState = GameManagerConnectRandomField != null && GameManagerLobbyTypeField != null; if (!CanReadGameManagerLobbyState) { AffectedFeatures.Add(flag ? "房间类型判定" : "Room type detection"); } CanAddRoomNamePrefix = Check(typeof(DataDirector), "networkServerName", flag ? "公开房名加[中途加入]前缀" : "Public room name [Late Join] prefix"); CanFixGumballOverflow = HasField(typeof(GumballValuable), "hypnosisLines") && HasField(typeof(GumballValuable), "hypnosisLine"); if (!CanFixGumballOverflow) { AffectedFeatures.Add(flag ? "修复 GumballValuable 越界崩溃" : "GumballValuable overflow fix"); } CanGuardLoadingAnimation = HasField(typeof(LoadingUI), "levelAnimationStarted") && HasField(typeof(LoadingUI), "levelAnimationCompleted") && HasField(typeof(PlayerAvatar), "levelAnimationCompleted"); if (!CanGuardLoadingAnimation) { AffectedFeatures.Add(flag ? "晚加入加载界面守门" : "Late join loading screen guard"); } CanEnsurePlayerCorpse = HasField(typeof(PlayerAvatar), "playerDeathHead") && HasField(typeof(PlayerAvatar), "tumble"); if (!CanEnsurePlayerCorpse) { AffectedFeatures.Add(flag ? "晚加入者尸体组件补创" : "Late joiner death head / tumble fallback"); } CanSyncModuleState = HasField(typeof(Module), "ConnectingTop") && HasField(typeof(Module), "ConnectingBottom") && HasField(typeof(Module), "ConnectingRight") && HasField(typeof(Module), "ConnectingLeft") && HasField(typeof(Module), "First") && HasField(typeof(Module), "SetupDone"); if (!CanSyncModuleState) { AffectedFeatures.Add(flag ? "晚加入者墙体/内饰同步" : "Late joiner walls / room interior sync"); } CanSyncValuableObjectState = HasField(typeof(ValuableObject), "dollarValueSet") && HasField(typeof(ValuableObject), "dollarValueCurrent") && HasField(typeof(ValuableObject), "discovered") && HasField(typeof(ValuableObject), "inStartRoom"); if (!CanSyncValuableObjectState) { AffectedFeatures.Add(flag ? "晚加入者宝物状态同步" : "Late joiner valuable state sync"); } HasIssues = AffectedFeatures.Count > 0; RuntimeReady = true; } private static bool HasField(Type t, string name) { return AccessTools.Field(t, name) != null; } private static bool Check(Type t, string name, string feature) { if (HasField(t, name)) { return true; } Details.Add("缺失字段:" + t.Name + "." + name); AffectedFeatures.Add(feature); return false; } private static bool TryAll(params Func[] checks) { bool result = true; foreach (Func func in checks) { try { if (!func()) { result = false; } } catch (Exception ex) { Details.Add(ex.Message); result = false; } } return result; } private static bool RF(Type t, string name, BindingFlags flags, ref FieldInfo into) { into = t.GetField(name, flags); if (into != null) { return true; } Details.Add("缺失字段:" + t.Name + "." + name); return false; } private static bool RM(Type t, string name, Type[] pTypes, BindingFlags flags, ref MethodInfo into) { into = t.GetMethod(name, flags, null, pTypes, null); if (into != null) { return true; } Details.Add("缺失方法:" + t.Name + "." + name); return false; } private static bool RME(Type t, string name) { if (t.GetMethod(name, ALL) != null) { return true; } Details.Add("缺失方法:" + t.Name + "." + name); return false; } private static bool RFR(string name, out FieldRef fieldRef) { fieldRef = null; if (!HasField(typeof(TType), name)) { Details.Add("缺失字段:" + typeof(TType).Name + "." + name); return false; } try { fieldRef = AccessTools.FieldRefAccess(name); return true; } catch (Exception ex) { Details.Add("FieldRef 绑定失败 " + typeof(TType).Name + "." + name + ":" + ex.Message); return false; } } } internal static class ConflictDetect { internal static readonly List DetectedConflictMods = new List(); private static readonly string[] ConflictKeywords = new string[2] { "late", "join" }; private static readonly string[] SelfKeywords = new string[2] { "latejoinnow", "zichenlatejoinnow" }; internal static bool HasConflict => DetectedConflictMods.Count > 0; internal static void Detect() { DetectedConflictMods.Clear(); try { foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { string text = pluginInfo.Key ?? string.Empty; PluginInfo value = pluginInfo.Value; object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata != null) ? metadata.Name : null); } if (obj == null) { obj = string.Empty; } string text2 = (string)obj; if (!string.Equals(text, "zichen.latejoinnow", StringComparison.OrdinalIgnoreCase) && !IsSelf(text) && !IsSelf(text2) && (IsConflict(text) || IsConflict(text2))) { string text3 = ((!string.IsNullOrWhiteSpace(text2)) ? text2 : text); if (!DetectedConflictMods.Contains(text3)) { DetectedConflictMods.Add(text3); Plugin.LogWarning("检测到同类模组:" + text3); } } } } catch (Exception ex) { Plugin.Verbose("模组冲突检测失败:" + ex.Message); } } private static string Normalize(string v) { if (!string.IsNullOrWhiteSpace(v)) { return v.Replace("_", "").Replace("-", "").Replace(" ", "") .Replace(".", "") .ToLowerInvariant(); } return string.Empty; } private static bool IsSelf(string v) { string text = Normalize(v); string[] selfKeywords = SelfKeywords; foreach (string value in selfKeywords) { if (text.IndexOf(value, StringComparison.Ordinal) >= 0) { return true; } } return false; } private static bool IsConflict(string v) { string text = Normalize(v); string[] conflictKeywords = ConflictKeywords; foreach (string value in conflictKeywords) { if (text.IndexOf(value, StringComparison.Ordinal) >= 0) { return true; } } return false; } } internal static class LateJoinNowHooks { private static bool _changeLevelInProgress; private static readonly Dictionary _trackedItems = new Dictionary(); private static readonly FieldRef _avDisabledRef = AccessTools.FieldRefAccess("isDisabled"); private static readonly FieldRef _avDeadSetRef = AccessTools.FieldRefAccess("deadSet"); private static readonly FieldRef> _gumLines = AccessTools.FieldRefAccess>("hypnosisLines"); private static readonly FieldRef _gumPrefab = TFR("hypnosisLine"); private static readonly FieldRef _gumParent = TFR("HypnosisLinesParenTransform"); private static readonly FieldRef> _gumPlayerInSight = TFR>("playerInSight"); private static readonly FieldRef> _gumPreviouslySeen = TFR>("previouslySeenList"); private static readonly FieldRef _gumPlayerCount = TFR("playerCount"); private static readonly FieldRef _animCompleted = AccessTools.FieldRefAccess("levelAnimationCompleted"); private static readonly FieldRef _uiStarted = TFR("levelAnimationStarted"); private static readonly FieldRef _uiCompleted = TFR("levelAnimationCompleted"); private static readonly FieldRef _talkAnimVisualsRef = TFR("playerAvatarVisuals"); private static readonly FieldRef _overchargePlayerAvatarRef = TFR("playerAvatar"); private static readonly FieldRef _overchargeBeamOriginRef = TFR("physGrabBeamOrigin"); private static readonly FieldRef _grabDisablePlayerAvatarRef = TFR("playerAvatar"); private static readonly FieldRef _grabDisableBeamOriginRef = TFR("physGrabBeamOrigin"); private static readonly FieldRef _leftArmVisualsRef = TFR("playerAvatarVisuals"); private static readonly FieldRef _inventorySpotsFeched = TFR("spotsFeched"); private static MethodInfo _rpcUpdateItemStateMethod; private static readonly Dictionary _epLastLockedState = new Dictionary(8); private static readonly FieldRef _epIsLockedRef = TFR("isLocked"); private static float _restartSceneTimer = 0f; private static readonly FieldRef _rmRestartingRef = TFR("restarting"); private static readonly FieldRef _rmRestartingDoneRef = TFR("restartingDone"); private static readonly FieldRef _rmGameOverRef = TFR("gameOver"); private static readonly FieldRef _rmLobbyJoinRef = TFR("lobbyJoin"); private static readonly FieldRef _rmWaitToChangeSceneRef = TFR("waitToChangeScene"); private static readonly FieldRef _avatarOutroDoneRef = TFR("outroDone"); private static bool IsMainState() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 GameDirector instance = GameDirector.instance; if (instance == null) { return false; } return (int)instance.currentState == 2; } internal static void ClearTrackedItemInstantiationCaches() { if (_trackedItems.Count == 0) { return; } int num = 0; List list = new List(_trackedItems.Values); _trackedItems.Clear(); foreach (int item in list) { if (Plugin.ClearInstantiationCacheById(item)) { num++; } } Plugin.Verbose($"切关清理 {num} 个物品缓存。"); } internal static void RunManagerChangeLevelPrefix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType) { if (!Plugin.IsHookActive() || _changeLevelInProgress || !PhotonNetwork.IsMasterClient) { return; } _changeLevelInProgress = true; try { if (_animCompleted != null && GameDirector.instance?.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null)) { try { _animCompleted.Invoke(player) = true; } catch { } } } } Plugin.ClearRunManagerPunBufferedRpcs(); HashSet keep = Plugin.BuildPlayerPersistentViewIdSet(); Plugin.DestroyAllSceneValuablesAndItemsWithKeepPublic(keep); Plugin.ClearAllScenePhotonCacheWithKeepPublic(keep, clearPersistent: true); ClearTrackedItemInstantiationCaches(); Plugin.ClearLateJoinQueue(); Plugin.HideRoomForChangeLevelCooldown(); _epLastLockedState.Clear(); } catch { } finally { _changeLevelInProgress = false; } } internal static void RunManagerChangeLevelPostfix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType) { if (!Plugin.IsHookActive() || !PhotonNetwork.IsMasterClient) { return; } try { Plugin.EnforceLobbyState(); } catch { } } internal static void LevelGeneratorStartPrefix(LevelGenerator __instance) { if (!PhotonNetwork.IsMasterClient) { try { Plugin.ResetClientCoopState(); return; } catch { return; } } if (!Plugin.IsHookActive() || !CompatibilityReport.CanRefreshLevelGenerator) { return; } bool flag = false; try { flag = SemiFunc.RunIsLobby(); } catch { } if (!flag || (Object)(object)__instance?.PhotonView == (Object)null) { return; } try { PhotonNetwork.RemoveBufferedRPCs(__instance.PhotonView.ViewID, (string)null, (int[])null); } catch { } } internal static void PlayerAvatarStartPostfix(PlayerAvatar __instance) { if (!Plugin.IsHookActive() || !CompatibilityReport.CanCatchupLateJoiner || (Object)(object)__instance?.photonView == (Object)null || !IsMainState()) { return; } if (!PhotonNetwork.IsMasterClient) { if (__instance.photonView.IsMine) { Plugin.TryRunClientLateJoinFixes(__instance); } return; } if (_animCompleted != null) { try { _animCompleted.Invoke(__instance) = true; } catch { } } try { __instance.photonView.RPC("LoadingLevelAnimationCompletedRPC", (RpcTarget)0, Array.Empty()); } catch { } try { int viewID = __instance.photonView.ViewID; EnemyOnScreen[] array = Object.FindObjectsOfType(); foreach (EnemyOnScreen val in array) { if ((Object)(object)val != (Object)null) { val.PlayerAdded(viewID); } } } catch { } } internal static bool LevelGeneratorPlayerSpawnPrefix(LevelGenerator __instance) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00de: 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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null) { return true; } if (!PhotonNetwork.IsMasterClient) { return true; } if (!__instance.Generated) { return true; } try { if (GameDirector.instance?.PlayerList == null) { return false; } bool teleportToHostOnJoinEnabled = Plugin.TeleportToHostOnJoinEnabled; Vector3 val = Vector3.zero; Quaternion val2 = Quaternion.identity; bool flag = false; if (teleportToHostOnJoinEnabled) { PlayerAvatar val3 = PickAliveAnchorAvatar(); if ((Object)(object)val3 != (Object)null) { Transform val4 = (((Object)(object)val3.playerTransform != (Object)null) ? val3.playerTransform : ((Component)val3).transform); if ((Object)(object)val4 != (Object)null) { Vector3 forward = val4.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { forward = Vector3.forward; } else { ((Vector3)(ref forward)).Normalize(); } val = val4.position + forward * 3f; val2 = Quaternion.LookRotation(-forward); flag = true; } } } SpawnPoint[] array = null; int num = 0; int num2 = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { object obj; if (player == null) { obj = null; } else { PhotonView photonView = player.photonView; obj = ((photonView != null) ? photonView.Owner : null); } if (obj == null || !Plugin.TryGetLateJoinEntryByActor(player.photonView.Owner.ActorNumber, out var entry)) { continue; } Plugin.LateJoinState state = entry.State; if (state != Plugin.LateJoinState.Pending && state != Plugin.LateJoinState.Queued && state != Plugin.LateJoinState.Processing) { continue; } Vector3 val5; Quaternion val6; if (flag) { val5 = val; val6 = val2; } else { if (array == null) { array = Object.FindObjectsOfType(); } if (array.Length == 0) { return false; } SpawnPoint obj2 = array[num % array.Length]; val5 = ((Component)obj2).transform.position; val6 = ((Component)obj2).transform.rotation; num++; } try { player.Spawn(val5, val6); num2++; } catch { } } Plugin.Verbose(string.Format("中途加入者 spawn:{0} 人,{1}。", num2, flag ? "传送到锚点" : "出生点")); } catch { } return false; } private static bool IsAvatarAlive(PlayerAvatar av) { if ((Object)(object)av == (Object)null) { return false; } try { if (_avDeadSetRef != null && _avDeadSetRef.Invoke(av)) { return false; } if (_avDisabledRef != null && _avDisabledRef.Invoke(av)) { return false; } } catch { } return true; } private static PlayerAvatar PickAliveAnchorAvatar() { if (GameDirector.instance?.PlayerList == null) { return null; } Player masterClient = PhotonNetwork.MasterClient; int num = ((masterClient != null) ? masterClient.ActorNumber : (-1)); PlayerAvatar val = null; PlayerAvatar val2 = null; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { object obj; if (player == null) { obj = null; } else { PhotonView photonView = player.photonView; obj = ((photonView != null) ? photonView.Owner : null); } if (obj != null && IsAvatarAlive(player)) { if (player.photonView.Owner.ActorNumber == num) { val = player; } else if ((Object)(object)val2 == (Object)null) { val2 = player; } } } return val ?? val2; } internal static void NetworkManagerOnPlayerEnteredRoomPostfix(Player newPlayer) { Plugin.NetworkManagerOnPlayerEnteredRoomPostfix(newPlayer); } internal static void NetworkManagerOnPlayerLeftRoomPostfix(Player otherPlayer) { Plugin.NetworkManagerOnPlayerLeftRoomPostfix(otherPlayer); } internal static void NetworkConnectOnConnectedToMasterPrefix() { Plugin.NetworkConnectOnConnectedToMasterPrefix(); } internal static void NetworkManagerPlayerSpawnedRPCPostfix(PhotonMessageInfo _info) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Plugin.NetworkManagerPlayerSpawnedRPCPostfix(_info); } internal static void NetworkConnectOnJoinedRoomPostfix() { try { Plugin.AnnouncePresenceAndRescan(); } catch { } Plugin.TryJoinSteamLobbyFromPhotonRoom(); } internal static void NetworkManagerOnDisconnectedPostfix() { try { Plugin.ResetCoopState(); } catch { } } private static FieldRef TFR(string name) { try { return (AccessTools.Field(typeof(T), name) != null) ? AccessTools.FieldRefAccess(name) : null; } catch { return null; } } internal static bool GumballValuableUpdateHypnosisLinesListPrefix(GumballValuable __instance) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || _gumLines == null || _gumPrefab == null) { return true; } try { List list = _gumLines.Invoke(__instance); List list2 = SemiFunc.PlayerGetList(); if (list == null || list2 == null) { return true; } int count = list2.Count; int count2 = list.Count; if (count <= count2) { return true; } GameObject val = _gumPrefab.Invoke(__instance); if ((Object)(object)val == (Object)null) { return false; } Transform val2 = ((_gumParent != null) ? _gumParent.Invoke(__instance) : null); for (int i = count2; i < count; i++) { GameObject val3 = Object.Instantiate(val, ((Component)__instance).transform.position, Quaternion.identity); if ((Object)(object)val2 != (Object)null) { val3.transform.SetParent(val2); } val3.SetActive(false); list.Add(val3); } if (_gumPlayerInSight != null) { List list3 = _gumPlayerInSight.Invoke(__instance); if (list3 != null) { while (list3.Count < count) { list3.Add(item: false); } } } if (_gumPreviouslySeen != null) { List list4 = _gumPreviouslySeen.Invoke(__instance); if (list4 != null) { while (list4.Count < count) { list4.Add(item: false); } } } } catch { } return true; } internal static bool GumballValuableClientEffectsLoopPrefix(GumballValuable __instance) { if ((Object)(object)__instance == (Object)null) { return true; } try { if (_gumLines == null || _gumPlayerCount == null) { return true; } List list = _gumLines.Invoke(__instance); int num = _gumPlayerCount.Invoke(__instance); if (list != null && num > list.Count) { return false; } if (_gumPlayerInSight != null) { List list2 = _gumPlayerInSight.Invoke(__instance); if (list2 != null && num > list2.Count) { return false; } } } catch { } return true; } internal static bool SemiFuncOwnerOnlyRPCPrefix(PhotonMessageInfo _info, PhotonView _photonView, ref bool __result) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsHookActive()) { return true; } try { Player sender = _info.Sender; if (sender != null && sender.IsMasterClient) { __result = true; return false; } } catch { } return true; } internal static bool PlayerAvatarLoadingLevelAnimationCompletedRPCPrefix(PlayerAvatar __instance) { if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || _animCompleted == null) { return true; } if (!PhotonNetwork.IsMasterClient) { return true; } if (!IsMainState()) { return true; } try { if (!_animCompleted.Invoke(__instance)) { _animCompleted.Invoke(__instance) = true; } } catch { } return false; } internal static bool LoadingUILevelAnimationStartPrefix(LoadingUI __instance) { if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || (Object)(object)LevelGenerator.Instance?.Level != (Object)null) { return true; } if (PhotonNetwork.CurrentRoom == null) { return true; } try { if (_uiStarted != null) { _uiStarted.Invoke(__instance) = true; } if (_uiCompleted != null) { _uiCompleted.Invoke(__instance) = true; } try { PlayerController instance = PlayerController.instance; if (instance != null) { PlayerAvatar playerAvatarScript = instance.playerAvatarScript; if (playerAvatarScript != null) { playerAvatarScript.LoadingLevelAnimationCompleted(); } } } catch { } return false; } catch { } return true; } internal static void PlayerAvatarAwakeSetOwnershipPostfix(PlayerAvatar __instance) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance?.photonView == (Object)null || !PhotonNetwork.IsMasterClient) { return; } try { __instance.photonView.OwnershipTransfer = (OwnershipOption)1; } catch { } } internal static void PlayerAvatarOnDestroyPostfix(PlayerAvatar __instance) { try { if ((Object)(object)PlayerAvatar.instance == (Object)(object)__instance) { PlayerAvatar.instance = null; } } catch { } } internal static bool EnergyUIUpdatePrefix() { if ((Object)(object)PlayerController.instance != (Object)null) { return (Object)(object)PlayerAvatar.instance != (Object)null; } return false; } internal static bool PlayerAvatarVisualsUpdatePrefix(PlayerAvatarVisuals __instance) { if ((Object)(object)__instance == (Object)null) { return false; } if (!__instance.isMenuAvatar && (Object)(object)LevelGenerator.Instance == (Object)null) { return false; } if (__instance.isMenuAvatar && (Object)(object)__instance.playerAvatar == (Object)null && (Object)(object)PlayerAvatar.instance == (Object)null) { return false; } if (!__instance.isMenuAvatar && (Object)(object)__instance.playerAvatar == (Object)null) { return false; } PlayerAvatar val = (((Object)(object)__instance.playerAvatar != (Object)null) ? __instance.playerAvatar : PlayerAvatar.instance); if ((Object)(object)val != (Object)null && (Object)(object)val.localCamera == (Object)null) { return false; } return true; } internal static bool PlayerExpressionUpdatePrefix(PlayerExpression __instance) { if ((Object)(object)__instance == (Object)null) { return false; } if ((Object)(object)LevelGenerator.Instance == (Object)null) { bool flag = false; try { flag = SemiFunc.MenuLevel(); } catch { } if (!flag) { return false; } } if (!Object.op_Implicit((Object)(object)__instance.playerAvatar) && (Object)(object)PlayerAvatar.instance == (Object)null) { return false; } return true; } internal static bool PlayerAvatarTalkAnimationUpdatePrefix(PlayerAvatarTalkAnimation __instance) { if ((Object)(object)__instance == (Object)null) { return false; } if ((Object)(object)__instance.playerAvatar == (Object)null && (Object)(object)PlayerAvatar.instance == (Object)null) { return false; } if (_talkAnimVisualsRef != null) { try { if ((Object)(object)_talkAnimVisualsRef.Invoke(__instance) == (Object)null) { return false; } } catch { return false; } } return true; } internal static bool PlayerLocalCameraGetOverrideTransformPrefix(PlayerLocalCamera __instance, ref Transform __result) { if ((Object)(object)__instance == (Object)null) { __result = null; return false; } if ((Object)(object)__instance.playerAvatar == (Object)null) { __result = ((Component)__instance).transform; return false; } if ((Object)(object)CameraPosition.instance == (Object)null) { __result = ((Component)__instance).transform; return false; } if ((Object)(object)__instance.playerAvatar.PlayerVisionTarget == (Object)null) { __result = ((Component)__instance).transform; return false; } return true; } internal static bool PlayerAvatarOverchargeVisualsUpdatePrefix(PlayerAvatarOverchargeVisuals __instance) { if ((Object)(object)__instance == (Object)null) { return false; } try { if (_overchargePlayerAvatarRef != null && (Object)(object)_overchargePlayerAvatarRef.Invoke(__instance) == (Object)null) { return false; } if (_overchargeBeamOriginRef != null && (Object)(object)_overchargeBeamOriginRef.Invoke(__instance) == (Object)null) { return false; } } catch { return false; } return true; } internal static bool PlayerAvatarGrabDisableVisualsUpdatePrefix(PlayerAvatarGrabDisableVisuals __instance) { if ((Object)(object)__instance == (Object)null) { return false; } try { if (_grabDisablePlayerAvatarRef != null && (Object)(object)_grabDisablePlayerAvatarRef.Invoke(__instance) == (Object)null) { return false; } if (_grabDisableBeamOriginRef != null && (Object)(object)_grabDisableBeamOriginRef.Invoke(__instance) == (Object)null) { return false; } } catch { return false; } return true; } internal static bool PhysGrabberUpdatePrefix(PhysGrabber __instance) { if ((Object)(object)__instance == (Object)null) { return false; } if ((Object)(object)__instance.playerAvatar == (Object)null) { return false; } if ((Object)(object)LevelGenerator.Instance == (Object)null) { return false; } if ((Object)(object)__instance.playerAvatar.healthGrab == (Object)null) { return false; } if ((Object)(object)__instance.playerAvatar.localCamera == (Object)null) { return false; } if ((Object)(object)PlayerController.instance == (Object)null) { return false; } return true; } internal static IEnumerable PhysGrabberUpdateTranspiler(IEnumerable instructions, ILGenerator il) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown List list = new List(instructions); MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(PlayerController), "instance"); if (methodInfo == null) { return list; } bool flag = false; for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Call && list[i].operand is MethodInfo methodInfo2 && methodInfo2 == methodInfo) { Label label = il.DefineLabel(); List list2 = new List { list[i], new CodeInstruction(OpCodes.Dup, (object)null), new CodeInstruction(OpCodes.Brtrue_S, (object)label), new CodeInstruction(OpCodes.Pop, (object)null), new CodeInstruction(OpCodes.Ret, (object)null), new CodeInstruction(OpCodes.Nop, (object)null) { labels = new List