Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LateJoinSimple v0.3.2
plugins/LateJoinSimple.dll
Decompiled 12 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; 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 Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("LateJoinSimple")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("LateJoinSimple")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4a6b399a-b272-4a3b-89b4-c059afdb7768")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace LateJoinSimple; [BepInPlugin("cn_xc.LateJoinSimple", "Late Join Simple", "0.3.2")] [BepInProcess("REPO.exe")] public class Plugin : BaseUnityPlugin { 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; public Vector3 SpawnPosition; } internal sealed class PeerCoopCallbackListener : IInRoomCallbacks, IOnEventCallback { public void OnPlayerEnteredRoom(Player newPlayer) { UpdateCoopStateFor(newPlayer); } public void OnPlayerLeftRoom(Player otherPlayer) { if (otherPlayer != null) { RemoveCoopActor(otherPlayer.ActorNumber); } } public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) { RecordCoopReadyForListener(targetPlayer); if (targetPlayer != null) { UpdateCoopStateFor(targetPlayer); } } public void OnMasterClientSwitched(Player newMasterClient) { HandleMasterClientSwitched(newMasterClient); } public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) { } public void OnEvent(EventData photonEvent) { if (photonEvent.Code != 171 || PhotonNetwork.IsMasterClient) { return; } try { if (!(photonEvent.CustomData is object[] array) || array.Length < 2) { return; } if (array[0] is string text) { Player masterClient = PhotonNetwork.MasterClient; if (text == "LJS.Rooms" && array[1] is float[] flat && masterClient != null && photonEvent.Sender == masterClient.ActorNumber) { ReceiveExploredRooms(flat); } } else { if (!(array[0] is int)) { return; } int num = (int)array[0]; bool flag = (bool)array[1]; PhotonView val = PhotonView.Find(num); if (!((Object)(object)val == (Object)null)) { ExtractionPoint component = ((Component)val).GetComponent<ExtractionPoint>(); if (!((Object)(object)component == (Object)null)) { component.UpdateLock(flag); Verbose("ExtractionPoint " + num + " 锁定状态同步:" + flag); } } } } catch (Exception ex) { Verbose("ExtractionPoint 锁定同步失败:" + ex.Message); } } } private delegate void OwnershipUpdateDelegate(int[] viewOwnerPairs, int targetActor); private sealed class SwitchSnapshot { internal List<KeyValuePair<ModulePropSwitch, object>> Sides; internal bool[] Flags; } 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; } public const string PluginGuid = "cn_xc.LateJoinSimple"; public const string PluginVersion = "0.3.2"; internal const string CoopVersionKey = "LateJoinSimple.Version"; internal const string CoopProtoKey = "LateJoinSimple.Protocol"; internal const string CoopReadyKey = "LateJoinSimple.Ready"; internal const string CoopModuleCountKey = "LateJoinSimple.ModuleCount"; private Harmony _harmony; private static ConfigEntry<bool> _modEnabled; private static ConfigEntry<bool> _publicRoomPrefix; private static ConfigEntry<bool> _teleportToHostOnJoin; private static ConfigEntry<bool> _coopMode; private static ConfigEntry<bool> _allowJoinInTruck; private static ConfigEntry<bool> _allowJoinInShop; private static ConfigEntry<bool> _allowJoinInLevel; private static ConfigEntry<bool> _hudEnabled; private static ConfigEntry<bool> _showJoinStatus; private static ConfigEntry<bool> _showPlayerCount; private static ConfigEntry<bool> _showRoomType; private static ConfigEntry<bool> _showRoomName; private static ConfigEntry<int> _hudFontSize; private static ConfigEntry<int> _hudOpacity; private static ConfigEntry<bool> _verboseLog; private static ConfigEntry<bool> _showConflictWarning; private static ConfigEntry<string> _modVersionInfo; private static ConfigEntry<bool> _roomConnectionRebuild; private static ConfigEntry<bool> _navMeshRebuild; private static readonly Dictionary<int, LateJoinEntry> _lateJoinEntries = new Dictionary<int, LateJoinEntry>(); private static readonly Queue<int> _lateJoinQueue = new Queue<int>(); private static bool _schedulerRunning; private const float SceneAllowedCacheSeconds = 0.5f; private static float _sceneAllowedCacheUntil; private static bool _sceneAllowedCachedValue; private static float _inGameCacheUntil; private static bool _inGameCachedValue; private float _enforceTimer; private const float EnforceInterval = 1f; private static readonly FieldInfo _roundAllExtractionPointsCompletedField = AccessTools.Field(typeof(RoundDirector), "allExtractionPointsCompleted"); private static bool _conflictWarned; private static bool _conflictCheckedInRoom; private const int CoopProtocolNumber = 1; private static readonly Dictionary<int, bool> _coopByActor = new Dictionary<int, bool>(); private static readonly Dictionary<int, long> _coopReadyByActor = new Dictionary<int, long>(); private static readonly Dictionary<int, int> _coopModuleCountByActor = new Dictionary<int, int>(); 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(); private static float _changeLevelCooldownUntil; private static bool _changeLevelCooldownRunning; private static bool _lastSceneAllowed; private static bool _steamLobbyLocked; private static float _steamUnlockEnsureNextAt; private static FieldInfo _steamPrivateLobbyField; private static readonly FieldInfo _gmConnectRandomField = F(typeof(GameManager), "connectRandom"); private static readonly WaitForSecondsRealtime _waitGeneratedPoll = new WaitForSecondsRealtime(0.5f); private static readonly HashSet<int> _scenePersistentViewIds = new HashSet<int>(64); private static readonly FieldInfo _voiceChatsField = F(typeof(RunManager), "voiceChats"); private static readonly FieldInfo _runManagerPunField = F(typeof(RunManager), "runManagerPUN"); private static readonly FieldInfo _punPhotonViewField = F(typeof(RunManagerPUN), "photonView"); private static readonly List<string> _hudLines = new List<string>(4); private static readonly List<string> _emptyList = new List<string>(0); private static readonly List<string> _lateJoinStatusLines = new List<string>(4); private static readonly List<LateJoinEntry> _hudEntriesScratch = new List<LateJoinEntry>(8); private static readonly Comparison<LateJoinEntry> _entrySortDesc = (LateJoinEntry a, LateJoinEntry b) => b.ActorNumber.CompareTo(a.ActorNumber); private static GUIStyle _hudLabelStyle; private static GUIStyle _hudShadowStyle; private static int _cachedFontSize; private static int _cachedOpacity; 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 float _statusLinesNextRefreshAt; private static int _statusLinesLastEntryCount = -1; private static int _statusLinesLastCoopMask; private static readonly FieldInfo _gmLobbyTypeField = F(typeof(GameManager), "lobbyType"); private static readonly FieldInfo _avatarPlayerNameField = F(typeof(PlayerAvatar), "playerName"); private static readonly WaitForSecondsRealtime _wait02 = new WaitForSecondsRealtime(0.2f); private static bool _clientLateJoinFixesDone; private static bool _clientLateJoinCoroutineRunning; private static FieldInfo _physGrabSpawnedField; private static float _nextSteamLobbyJoinAttempt; private static MethodInfo _steamJoinLobbyMethod; private static FieldInfo _steamIdValueField; private static FieldInfo _steamCurrentLobbyField; private static PropertyInfo _steamLobbyIdProp; private static PropertyInfo _steamLobbyIdIsValidProp; private static readonly FieldInfo _deathHeadField = F(typeof(PlayerAvatar), "playerDeathHead"); private static readonly FieldInfo _tumbleField = F(typeof(PlayerAvatar), "tumble"); private static readonly FieldInfo _networkServerNameField = F(typeof(DataDirector), "networkServerName"); 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 readonly int[] _projectileOwnershipScratch = new int[2]; private static readonly List<KeyValuePair<PlayerAvatar, int>> _oldAvatarsScratch = new List<KeyValuePair<PlayerAvatar, int>>(8); private static readonly WaitForSecondsRealtime _wait05 = new WaitForSecondsRealtime(0.5f); private static readonly WaitForSecondsRealtime _wait25 = new WaitForSecondsRealtime(2.5f); internal const string RoomSyncTag = "LJS.Rooms"; private static readonly FieldInfo _rvExploredField = F(typeof(RoomVolume), "Explored"); private static readonly FieldInfo _rvMapModuleField = F(typeof(RoomVolume), "MapModule"); private static readonly List<Vector3> _pendingRoomReveal = new List<Vector3>(256); private static bool _roomRevealRunning; private const float RoomRevealTolerance = 0.5f; private static readonly FieldInfo _modSetupDoneField = F(typeof(Module), "SetupDone"); private static readonly FieldInfo _modTopField = F(typeof(Module), "ConnectingTop"); private static readonly FieldInfo _modBottomField = F(typeof(Module), "ConnectingBottom"); private static readonly FieldInfo _modRightField = F(typeof(Module), "ConnectingRight"); private static readonly FieldInfo _modLeftField = F(typeof(Module), "ConnectingLeft"); private static readonly FieldInfo _modFirstField = F(typeof(Module), "First"); private static int _connMismatchCount; private static readonly MethodInfo _modConnectionRpcMethod = AccessTools.Method(typeof(Module), "ModuleConnectionSetRPC", (Type[])null, (Type[])null); private static readonly FieldInfo _switchSideField = F(typeof(ModulePropSwitch), "ConnectionSide"); private static readonly FieldInfo _switchConnectedField = F(typeof(ModulePropSwitch), "Connected"); private static readonly Dictionary<Module, bool[]> _estimatedConnections = new Dictionary<Module, bool[]>(); private static readonly Dictionary<Module, List<KeyValuePair<ModulePropSwitch, object>>> _estimatedSwitches = new Dictionary<Module, List<KeyValuePair<ModulePropSwitch, object>>>(); private static bool _applyingEstimate; private static int _connectionMatched; private static int _connectionCorrected; private static LevelGenerator _voidRepairLevel; private static bool _voidRepairRunning; private static readonly Dictionary<Module, SwitchSnapshot> _moduleSwitchSides = new Dictionary<Module, SwitchSnapshot>(); private static int _wallDoorRefreshed; private static readonly string[] ChecklistFields = new string[21] { "wallsInside", "wallsMap", "levelPointsEntrance", "levelPointsWaypoints", "levelPointsRoomVolume", "levelPointsNavmesh", "levelPointsConnected", "lightsMax", "lightsPrefab", "roomVolumeDoors", "roomVolumeHeight", "roomVolumeSpace", "navmeshConnected", "navmeshPitfalls", "valuablesAllTypes", "valuablesMaxed", "valuablesSwitch", "valuablesSwitchNavmesh", "valuablesTest", "ModulePropSwitchSetup", "ModulePropSwitchNavmesh" }; private static int _checklistLogged; private static ConfigEntry<bool> _levelPointRelink; private static ConfigEntry<bool> _wallDoorRefresh; private static int _relinkAdded; private static bool _voiceMixerGuardLogged; 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 readonly FieldInfo _modTop = F(typeof(Module), "ConnectingTop"); private static readonly FieldInfo _modBottom = F(typeof(Module), "ConnectingBottom"); private static readonly FieldInfo _modRight = F(typeof(Module), "ConnectingRight"); private static readonly FieldInfo _modLeft = F(typeof(Module), "ConnectingLeft"); private static readonly FieldInfo _modFirst = F(typeof(Module), "First"); private static readonly FieldInfo _modDone = F(typeof(Module), "SetupDone"); private static readonly FieldInfo _valSet = F(typeof(ValuableObject), "dollarValueSet"); private static readonly FieldInfo _valCurrent = F(typeof(ValuableObject), "dollarValueCurrent"); private static readonly FieldInfo _valDisc = F(typeof(ValuableObject), "discovered"); private static readonly FieldInfo _valHaul = F(typeof(ValuableObject), "inStartRoom"); private static readonly FieldInfo _itemValue = F(typeof(ItemAttributes), "value"); private static readonly FieldInfo _avatarMoving = F(typeof(PlayerAvatar), "isMoving"); private static readonly FieldInfo _avatarInputRaw = F(typeof(PlayerAvatar), "InputDirectionRaw"); private static readonly FieldInfo _avatarAnimDone = F(typeof(PlayerAvatar), "levelAnimationCompleted"); private static FieldInfo _epIsLockedField; private static readonly WaitForSecondsRealtime _wait2 = new WaitForSecondsRealtime(2f); private static readonly WaitForSecondsRealtime _wait15 = new WaitForSecondsRealtime(1.5f); private static bool _refreshOldPlayersBusy; private static readonly Dictionary<int, bool> _playerJumpedFlags = new Dictionary<int, bool>(); public static Plugin Instance { get; private set; } internal static Dictionary<int, LateJoinEntry>.ValueCollection LateJoinEntryValues => _lateJoinEntries.Values; internal static int PendingLateJoinerCount => _lateJoinEntries.Count; internal static bool CoopModeEnabled { get { if (_coopMode != null) { return _coopMode.Value; } return false; } } internal static bool IsInChangeLevelCooldown => Time.unscaledTime < _changeLevelCooldownUntil; internal static float CooldownLeft => Mathf.Max(0f, _changeLevelCooldownUntil - Time.unscaledTime); internal static FieldInfo DeathHeadField => _deathHeadField; internal static FieldInfo TumbleField => _tumbleField; public static bool TeleportToHostOnJoinEnabled { get { if (_teleportToHostOnJoin != null) { return _teleportToHostOnJoin.Value; } return false; } } internal static bool HudEnabled { get { if (_hudEnabled != null) { return _hudEnabled.Value; } return false; } } internal static int HudFontSize { get { if (_hudFontSize == null) { return 16; } return _hudFontSize.Value; } } internal static int HudOpacityPercent { get { if (_hudOpacity == null) { return 80; } return _hudOpacity.Value; } } internal static bool HudShowPlayerCount { get { if (_showPlayerCount != null) { return _showPlayerCount.Value; } return false; } } internal static bool HudShowRoomType { get { if (_showRoomType != null) { return _showRoomType.Value; } return false; } } internal static bool HudShowRoomName { get { if (_showRoomName != null) { return _showRoomName.Value; } return false; } } internal static bool ShowJoinStatusEnabled { get { if (_showJoinStatus != null) { return _showJoinStatus.Value; } return false; } } internal static bool PublicRoomPrefixEnabled { get { if (_publicRoomPrefix != null) { return _publicRoomPrefix.Value; } return false; } } private void Awake() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown DetachFromManager(); Instance = this; ConfigVersionResetter.ResetIfVersionChanged(((BaseUnityPlugin)this).Config, "0.3.2", ((BaseUnityPlugin)this).Logger); BindConfig(); if (!_modEnabled.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"LateJoinSimple v0.3.2 已禁用。"); return; } CompatibilityReport.Build(); try { _harmony = new Harmony("cn_xc.LateJoinSimple"); InstallPatchesSafely(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LateJoinSimple v0.3.2 已加载。"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("LateJoinSimple 启动失败:" + ex.Message)); } if (CompatibilityReport.HasIssues) { ((MonoBehaviour)this).StartCoroutine(WaitAndShowCompatPopup()); } CheckConflicts("启动"); ((MonoBehaviour)this).StartCoroutine(DeferredConflictCheck()); } internal static void CheckConflicts(string phase) { if (_showConflictWarning == null || !_showConflictWarning.Value) { return; } ConflictDetect.Detect(); int count = ConflictDetect.DetectedConflictMods.Count; LogInfo("[冲突检测/" + phase + "] 扫描 " + ConflictDetect.ScannedCount + " 个插件,命中 " + count + " 个" + ((count > 0) ? (":" + string.Join("、", ConflictDetect.DetectedConflictMods)) : "")); if (count > 0 && !_conflictWarned) { _conflictWarned = true; LogWarning("检测到同类模组:" + string.Join("、", ConflictDetect.DetectedConflictMods) + "(建议只保留一个中途加入模组)"); if ((Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StartCoroutine(Instance.WaitAndShowConflictPopup()); } } } private IEnumerator DeferredConflictCheck() { yield return (object)new WaitForSecondsRealtime(5f); CheckConflicts("延迟复检"); } private void Update() { if (!IsHookActive()) { return; } if (PhotonNetwork.IsMasterClient) { _enforceTimer -= Time.unscaledDeltaTime; if (!(_enforceTimer > 0f)) { _enforceTimer = 1f; EnforceLobbyState(); } } else { TryJoinSteamLobbyFromPhotonRoom(); } } private void BindConfig() { //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Expected O, but got Unknown //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Expected O, but got Unknown _modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("全局设置", "ModEnabled", true, "关闭后整个模组全部功能失效。"); _publicRoomPrefix = ((BaseUnityPlugin)this).Config.Bind<bool>("全局设置", "PublicRoomNamePrefix", true, "创建公开房时自动加 [中途加入] 前缀。"); _teleportToHostOnJoin = ((BaseUnityPlugin)this).Config.Bind<bool>("全局设置", "TeleportToHostOnJoin", false, "开启时新加入者直接传送到房主位置;若房主死亡,则传送到最近一个活着的玩家位置。"); _coopMode = ((BaseUnityPlugin)this).Config.Bind<bool>("全局设置", "CoopMode", true, "双端都装同版本模组时启用协同优化。关闭后所有客户端按单端模式跑(仅调试用)。"); _allowJoinInTruck = ((BaseUnityPlugin)this).Config.Bind<bool>("场景白名单", "AllowJoinInTruck", true, "卡车阶段允许中途加入。"); _allowJoinInShop = ((BaseUnityPlugin)this).Config.Bind<bool>("场景白名单", "AllowJoinInShop", true, "商店阶段允许中途加入。"); _allowJoinInLevel = ((BaseUnityPlugin)this).Config.Bind<bool>("场景白名单", "AllowJoinInLevel", true, "关卡进行中允许中途加入。"); _hudEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("房间信息显示", "HudEnabled", true, "左下角显示房间信息框。"); _showJoinStatus = ((BaseUnityPlugin)this).Config.Bind<bool>("房间信息显示", "ShowJoinStatus", true, "显示中途加入玩家的进度。"); _showPlayerCount = ((BaseUnityPlugin)this).Config.Bind<bool>("房间信息显示", "ShowPlayerCount", true, "显示当前人数/最大人数。"); _showRoomType = ((BaseUnityPlugin)this).Config.Bind<bool>("房间信息显示", "ShowRoomType", true, "显示公开/私人/单人。"); _showRoomName = ((BaseUnityPlugin)this).Config.Bind<bool>("房间信息显示", "ShowRoomName", true, "显示房间名称。"); _hudFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("房间信息显示", "HudFontSize", 16, new ConfigDescription("HUD 字体大小。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 28), Array.Empty<object>())); _hudOpacity = ((BaseUnityPlugin)this).Config.Bind<int>("房间信息显示", "HudOpacity", 80, new ConfigDescription("HUD 透明度。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 100), Array.Empty<object>())); _verboseLog = ((BaseUnityPlugin)this).Config.Bind<bool>("调试", "VerboseLog", false, "输出详细调试日志。"); _showConflictWarning = ((BaseUnityPlugin)this).Config.Bind<bool>("调试", "ShowConflictWarning", true, "检测到同类模组时弹警告。"); _modVersionInfo = ((BaseUnityPlugin)this).Config.Bind<string>("模组信息", "模组版本号", "0.3.2", "只读:用于配置升级时自动重置。"); _navMeshRebuild = ((BaseUnityPlugin)this).Config.Bind<bool>("虚空修复", "NavMeshRebuildOnJoin", false, "补建过房间连接后,在本地重建一次导航网格(仅本机执行,不广播)。房主的导航网格设置由游戏自身同步,一般无需开启。"); _roomConnectionRebuild = ((BaseUnityPlugin)this).Config.Bind<bool>("虚空修复", "RoomConnectionRebuild", false, "【实验】对连接缺失的房间,按关卡布局本地推算墙/门连接并补建(会构造一条「来自房主」的本地消息以通过游戏的 master 校验,不发网络包)。若出现墙体异常请关掉。"); _levelPointRelink = ((BaseUnityPlugin)this).Config.Bind<bool>("虚空修复", "LevelPointRelink", false, "【实验,默认关】按几何补链接关卡连接点。注意:可能给本该不通行的位置补上连接,请逐关观察后再决定是否常开。"); _wallDoorRefresh = ((BaseUnityPlugin)this).Config.Bind<bool>("虚空修复", "WallDoorRefresh", false, "【实验,默认关】只在房主连接确实发生变化时,重算该模块的墙体 / 门道具。绝不在连接未变时翻动开关(避免把本来正确的通路封死)。"); } 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(PhysGrabber), "Update", (Type[])null, (Type[])null); if (!(methodInfo == null)) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "PhysGrabberUpdateTranspiler", (Type[])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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(SlowProjectile), "DestroyRPC", (Type[])null, (Type[])null); if (!(methodInfo == null)) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Hooks), "SlowProjectileDestroyRPCTranspiler", (Type[])null), (HarmonyMethod)null); } }); Pre("PhysGrabObject.DestroyRPC Pre", typeof(PhysGrabObject), "DestroyPhysGrabObjectRPC", "PhysGrabObjectDestroyPhysGrabObjectRPCPrefix"); Pre("PlayerAvatar.JumpRPC Pre", typeof(PlayerAvatar), "JumpRPC", "PlayerAvatarJumpRPCPrefix"); Final("LightManager.Update Final", typeof(LightManager), "Update", "LightManagerUpdateFinalizer"); Final("LightManager.LogicUpdate Final", typeof(LightManager), "LogicUpdate", "LightManagerLogicUpdateFinalizer"); Final("LightManager.Setup Final", typeof(LightManager), "Setup", "LightManagerSetupFinalizer"); Final("AmbientLightLogic Final", typeof(EnvironmentDirector), "AmbientLightLogic", "AmbientLightLogicFinalizer"); Post("ModuleConnectionSetRPC Post", typeof(Module), "ModuleConnectionSetRPC", "ModuleConnectionSetRPCAppliedPostfix"); Pre("ModuleConnectionSetRPC IncomingPre", typeof(Module), "ModuleConnectionSetRPC", "ModuleConnectionSetRPCIncomingPrefix"); TryPatch("LevelPoint.Start Post", delegate { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(LevelPoint), "Start", (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"未找到 LevelPoint.Start,跳过关卡连接点重链接。"); } else { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "LevelPointStartPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); } }); Pre("PlayerHealth.Update Pre", typeof(PlayerHealth), "Update", "PlayerHealthUpdatePrefix"); Pre("AmbientLightLogic Pre", typeof(EnvironmentDirector), "AmbientLightLogic", "EnvLightLogicPrefix"); Pre("FogLogic Pre", typeof(EnvironmentDirector), "FogLogic", "EnvLightLogicPrefix"); Pre("EnemyOnScreen.GetOnScreen Pre", typeof(EnemyOnScreen), "GetOnScreen", "EnemyOnScreenGetOnScreenPrefix"); Final("PlayerDeathHead.UpdateParticleColors Final", typeof(PlayerDeathHead), "UpdateParticleColors", "DeathHeadColorFinalizer"); Final("LightManager.HandleLightActivation Final", typeof(LightManager), "HandleLightActivation", "LightManagerHandleLightFinalizer"); Final("LightManager.HandleEmissionActivation Final", typeof(LightManager), "HandleEmissionActivation", "LightManagerHandleEmissionFinalizer"); TryPatch("VoiceChat.ToggleMixer Final", delegate { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(PlayerVoiceChat), "ToggleMixer", (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"未找到 PlayerVoiceChat.ToggleMixer,跳过语音混音守卫。"); } else { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "VoiceMixerFinalizer", (Type[])null)); } }); ((BaseUnityPlugin)this).Logger.LogInfo((object)("补丁注册完成(失败 " + CompatibilityReport.AffectedFeatures.Count + " 项)。")); } private void Pre(string label, Type target, string methodName, string hookName) { TryPatch(label, delegate { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(target, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("未找到 " + target.Name + "." + methodName + ",跳过:" + label)); } else { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Hooks), hookName, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } }); } private void Post(string label, Type target, string methodName, string hookName) { TryPatch(label, delegate { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(target, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("未找到 " + target.Name + "." + methodName + ",跳过:" + label)); } else { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Hooks), hookName, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); } }); } private void Final(string label, Type target, string methodName, string hookName) { TryPatch(label, delegate { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(target, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("未找到 " + target.Name + "." + methodName + ",跳过:" + label)); } else { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Hooks), hookName, (Type[])null)); } }); } private void TryPatch(string label, Action action) { try { action(); } catch (Exception ex) { CompatibilityReport.AffectedFeatures.Add("补丁失败:" + label); CompatibilityReport.Details.Add(label + " → " + ex.Message); ((BaseUnityPlugin)this).Logger.LogWarning((object)("补丁安装失败:" + label)); } } private void PatchPostfix(Type target, string methodName, string postfixName) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(target, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("未找到 " + target.Name + "." + methodName + ",跳过该补丁。")); } else { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), postfixName, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); Verbose("已注册 " + target.Name + "." + methodName + " Postfix。"); } } public static bool IsStaticModEnabled() { if (_modEnabled != null) { return _modEnabled.Value; } return false; } internal static bool IsHookActive() { if (IsStaticModEnabled()) { return CompatibilityReport.RuntimeReady; } return false; } public static bool IsCurrentSceneAllowed() { float unscaledTime = Time.unscaledTime; if (unscaledTime < _sceneAllowedCacheUntil) { return _sceneAllowedCachedValue; } _sceneAllowedCacheUntil = unscaledTime + 0.5f; _sceneAllowedCachedValue = ComputeSceneAllowed(); return _sceneAllowedCachedValue; } 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 _allowJoinInTruck.Value; } if (SemiFunc.RunIsShop()) { return _allowJoinInShop.Value; } if (SemiFunc.RunIsArena()) { return false; } if (!SemiFunc.RunIsLevel()) { return false; } if (!_allowJoinInLevel.Value) { return false; } if ((Object)(object)RoundDirector.instance != (Object)null && _roundAllExtractionPointsCompletedField != null) { try { if ((bool)_roundAllExtractionPointsCompletedField.GetValue(RoundDirector.instance)) { return false; } } catch { } } return true; } catch (Exception ex) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("场景判定失败:" + ex.Message)); } } return false; } 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; } public static bool IsInPreGameMenu() { try { if ((Object)(object)RunManager.instance == (Object)null) { return false; } return !SemiFunc.RunIsLobby() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsLevel() && !SemiFunc.RunIsArena(); } catch { 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 string CurrentSceneName() { RunManager instance = RunManager.instance; return (((Object)(object)instance != (Object)null && (Object)(object)instance.levelCurrent != (Object)null) ? ((Object)instance.levelCurrent).name : null) ?? "<null>"; } private static bool IsTargetStillOnline(Player p) { if (p != null && PhotonNetwork.CurrentRoom != null) { return PhotonNetwork.CurrentRoom.Players.ContainsKey(p.ActorNumber); } return false; } internal static bool TryGetLateJoinEntryByActor(int actor, out LateJoinEntry entry) { return _lateJoinEntries.TryGetValue(actor, out entry); } 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; } 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 + " 中途加入,等待同步。"); _sceneAllowedCacheUntil = 0f; EnforceLobbyState(); } } internal static void NetworkManagerOnPlayerLeftRoomPostfix(Player p) { if (p != null) { if (_lateJoinEntries.TryGetValue(p.ActorNumber, out var value)) { value.State = LateJoinState.Aborted; Verbose(p.NickName + " 离开房间,同步取消。"); } _lateJoinEntries.Remove(p.ActorNumber); if (PhotonNetwork.IsMasterClient) { _sceneAllowedCacheUntil = 0f; EnforceLobbyState(); } } } 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 (_refreshOldPlayersBusy) { if (!IsTargetStillOnline(e.Target) || CurrentSceneName() != e.StartScene) { yield break; } yield return null; } _refreshOldPlayersBusy = true; yield return ((MonoBehaviour)Instance).StartCoroutine(RefreshOldPlayersForNewJoiner(e.Target, e.StartScene)); ((MonoBehaviour)Instance).StartCoroutine(SendExploredRoomsToPlayer(e.Target)); } internal static void ClearLateJoinQueue() { _lateJoinQueue.Clear(); foreach (LateJoinEntry value in _lateJoinEntries.Values) { value.State = LateJoinState.Aborted; } _lateJoinEntries.Clear(); _schedulerRunning = false; _sceneAllowedCacheUntil = 0f; } 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) { _lastSceneAllowed = false; Verbose("场景不允许中途加入,房间已锁定。"); } } catch (Exception ex) { LogWarning("房间状态维护失败:" + ex.Message); } } internal static void AnnouncePresenceAndRescan() { try { if (PhotonNetwork.LocalPlayer == null) { return; } ((Dictionary<object, object>)(object)_presencePropsScratch).Clear(); _presencePropsScratch[(object)"LateJoinSimple.Version"] = "0.3.2"; _presencePropsScratch[(object)"LateJoinSimple.Protocol"] = 1; PhotonNetwork.LocalPlayer.SetCustomProperties(_presencePropsScratch, (Hashtable)null, (WebFlags)null); Verbose("宣告协同版本:0.3.2 / proto=" + 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); } } internal static void AnnounceCoopReady(int receivedModuleCount = 0) { try { if (PhotonNetwork.LocalPlayer != null) { long ticks = DateTime.UtcNow.Ticks; ((Dictionary<object, object>)(object)_readyPropsScratch).Clear(); _readyPropsScratch[(object)"LateJoinSimple.Ready"] = ticks; _readyPropsScratch[(object)"LateJoinSimple.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, object>)(object)_clearPropsScratch).Clear(); _clearPropsScratch[(object)"LateJoinSimple.Version"] = null; _clearPropsScratch[(object)"LateJoinSimple.Protocol"] = null; _clearPropsScratch[(object)"LateJoinSimple.Ready"] = null; _clearPropsScratch[(object)"LateJoinSimple.ModuleCount"] = null; PhotonNetwork.LocalPlayer.SetCustomProperties(_clearPropsScratch, (Hashtable)null, (WebFlags)null); Verbose("已清除本机 Coop CustomProperties"); } } catch (Exception ex) { Verbose("清 Coop properties 失败:" + ex.Message); } } 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 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 + ")。"); ResetCoopState(); if (PhotonNetwork.IsMasterClient) { if (PhotonNetwork.CurrentRoom == null) { return; } { foreach (Player value in PhotonNetwork.CurrentRoom.Players.Values) { if (value != null) { UpdateCoopStateFor(value); } } return; } } UpdateCoopStateFor(newMasterClient); } 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, object>)(object)p.CustomProperties).TryGetValue((object)"LateJoinSimple.Ready", out object value) && value is long num && num > 0) { _coopReadyByActor[p.ActorNumber] = num; } if (((Dictionary<object, object>)(object)p.CustomProperties).TryGetValue((object)"LateJoinSimple.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, object>)(object)p.CustomProperties).TryGetValue((object)"LateJoinSimple.Version", out object value)) { return false; } if (!(value is string text) || text != "0.3.2") { return false; } if (!((Dictionary<object, object>)(object)p.CustomProperties).TryGetValue((object)"LateJoinSimple.Protocol", out object value2)) { return false; } return Convert.ToInt32(value2) == 1; } catch { return false; } } 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; } } private static IEnumerator WaitClientReadyOrTimeout(LateJoinEntry e, float timeoutSeconds) { float deadline = Time.unscaledTime + timeoutSeconds; while (Time.unscaledTime < deadline) { if (!IsTargetStillOnline(e.Target) || e.State == LateJoinState.Aborted || IsCoopActorReady(e.Target.ActorNumber)) { yield break; } yield return null; } Verbose("等待客户端就绪超时(" + timeoutSeconds + "s)。"); } private static bool IsPublicGame() { GameManager instance = GameManager.instance; if ((Object)(object)instance == (Object)null) { return false; } try { return _gmConnectRandomField != null && (bool)_gmConnectRandomField.GetValue(instance); } catch { return false; } } 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 && (Object)(object)instance != (Object)(object)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 ((Object)(object)instance2 != (Object)null && ReadGameDirectorState(instance2) == 2) { break; } yield return _waitGeneratedPoll; } } if (IsCooldownStillReferenced()) { Verbose("房间已解锁(等待 " + (Time.unscaledTime - t0).ToString("F1") + "s)。"); _changeLevelCooldownUntil = Time.unscaledTime; } } finally { _changeLevelCooldownRunning = false; } } private static bool IsCooldownStillReferenced() { return IsCooldownStillRelevant(); } private static int ReadGameDirectorState(GameDirector gd) { try { FieldInfo fieldInfo = F(typeof(GameDirector), "currentState"); if (fieldInfo == null) { return -1; } object value = fieldInfo.GetValue(gd); return (value == null) ? (-1) : Convert.ToInt32(value); } catch { return -1; } } 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 ((Object)(object)instance != (Object)null) { instance.LockLobby(); } _steamLobbyLocked = true; result = true; } catch { } } return result; } private static bool IsSteamLobbyActuallyLocked() { try { if (_steamPrivateLobbyField == null) { _steamPrivateLobbyField = F(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 ((Object)(object)instance != (Object)null) { instance.SetConnectRandom(true); } } if (flag) { Verbose("房间已开放。"); } } 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)) { LogInfo("[门跳过] " + ((Object)((Component)p).gameObject).name + "(带 hinge,不再销毁)"); 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 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 { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("无法显示 " + label + " 弹窗。")); } } } private IEnumerator WaitForMenuReady(float deadline, Action<MenuManager> onReady) { float until = Time.unscaledTime + deadline; bool shown = false; 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); shown = true; break; } } yield return null; } if (!shown) { LogWarning("菜单在 " + deadline + " 秒内未就绪,未能显示弹窗(冲突仍已在日志中警告)。"); } } private static bool IsPopupSlotBusy(MenuManager mm) { if ((Object)(object)mm == (Object)null || !CompatibilityReport.CanShowMenuPopup) { return true; } try { if (CompatibilityReport.MenuCurrentMenuPageField.GetValue(mm) == null) { return true; } object value = CompatibilityReport.MenuPagePopUpScheduledField.GetValue(mm); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return true; } object value2 = CompatibilityReport.MenuCurrentMenuPageIndexField.GetValue(mm); int num2 = ((value2 == null) ? (-1) : Convert.ToInt32(value2)); return num2 == 9 || num2 == 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 "LateJoinSimple Compatibility"; } return "LateJoinSimple 兼容模式"; } 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("<size=85%>"); foreach (string affectedFeature in CompatibilityReport.AffectedFeatures) { stringBuilder.Append(" • ").AppendLine(affectedFeature); } stringBuilder.AppendLine("</size>").AppendLine(); stringBuilder.Append("<size=90%><i>").Append(UseChinese() ? "如需使用这些功能,请等待作者适配最新版本。" : "Please wait for the mod to be updated for the latest game version.").Append("</i></size>"); return stringBuilder.ToString(); } private IEnumerator WaitAndShowConflictPopup() { return WaitForMenuReady(60f, 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 "LateJoinSimple Conflict Warning"; } return "LateJoinSimple 冲突警告"; } private static string BuildConflictBody() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(UseChinese() ? "检测到本地启用了其他「中途加入」类模组:" : "Detected other late-join style mods alongside LateJoinSimple:"); stringBuilder.AppendLine().AppendLine("<size=85%>"); foreach (string detectedConflictMod in ConflictDetect.DetectedConflictMods) { stringBuilder.Append(" • ").AppendLine(detectedConflictMod); } stringBuilder.AppendLine("</size>").AppendLine(); stringBuilder.Append("<b>").Append(UseChinese() ? "建议在模组管理器中禁用上述模组,仅保留 LateJoinSimple。" : "Please disable those mods and keep only LateJoinSimple.").AppendLine("</b>"); stringBuilder.AppendLine(); stringBuilder.Append("<size=85%><i>").Append(UseChinese() ? "同时启用会出现状态污染、卡加载等问题。" : "Running them together causes state corruption and loading hangs.").Append("</i></size>"); return stringBuilder.ToString(); } 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) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("缓存清理失败:" + ex.Message)); } return false; } } internal static void ClearPhotonInstantiationCache(PhotonView pv) { if ((Object)(object)pv != (Object)null) { ClearInstantiationCacheById(pv.InstantiationId); } } internal static void ClearAllScenePhotonCacheWithKeepPublic(HashSet<int> keep) { ClearAllScenePhotonCacheWithKeepPublic(keep, clearPersistent: false); } internal static void ClearAllScenePhotonCacheWithKeepPublic(HashSet<int> keep, bool clearPersistent) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) try { int num = 0; int num2 = 0; int num3 = 0; PhotonView[] array = Object.FindObjectsOfType<PhotonView>(); foreach (PhotonView val in array) { if ((Object)(object)val == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).buildIndex == -1 || keep.Contains(val.ViewID) || (!clearPersistent && IsPlayerPersistentByName(val))) { continue; } if (val.ViewID != 0) { try { PhotonNetwork.RemoveBufferedRPCs(val.ViewID, (string)null, (int[])null); num3++; } catch { } } if (val.InstantiationId == 0) { continue; } if (val.isRuntimeInstantiated && val.IsMine) { 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<int> 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<PhotonView>(); foreach (PhotonView val in array) { if ((Object)(object)val == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).buildIndex == -1 || keep.Contains(val.ViewID) || 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<int> BuildPlayerPersistentViewIdSet() { _scenePersistentViewIds.Clear(); try { if ((Object)(object)GameDirector.instance != (Object)null && 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); } try { PlayerTumble val = (PlayerTumble)((_tumbleField != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent<PhotonView>() != (Object)null) { _scenePersistentViewIds.Add(((Component)val).GetComponent<PhotonView>().ViewID); } } catch { } } } } catch (Exception ex) { Verbose("收集玩家视图失败:" + ex.Message); } try { if (_voiceChatsField != null && (Object)(object)RunManager.instance != (Object)null && _voiceChatsField.GetValue(RunManager.instance) is List<PlayerVoiceChat> list) { foreach (PlayerVoiceChat item in list) { PhotonView val2 = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<PhotonView>() : 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) { string text = (((Object)(object)v != (Object)null && (Object)(object)((Component)v).gameObject != (Object)null) ? ((Object)((Component)v).gameObject).name : null); 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 = (RunManagerPUN)((_runManagerPunField != null) ? /*isinst with value type is only supported in some contexts*/: null); PhotonView val2 = (PhotonView)(((Object)(object)val != (Object)null && _punPhotonViewField != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val2 != (Object)null) { PhotonNetwork.RemoveBufferedRPCs(val2.ViewID, (string)null, (int[])null); Verbose("缓冲指令清理完成。"); } } catch (Exception ex) { Verbose("缓冲指令清理失败:" + ex.Message); } } 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; } bool flag = false; try { flag = GameManager.Multiplayer(); } catch { } if (!PhotonNetwork.IsMasterClient || PhotonNetwork.LocalPlayer == null || PhotonNetwork.CurrentRoom == null || !flag) { Object.Destroy((Object)(object)val); return; } PhotonView val2 = null; try { val2 = val.GetComponent<PhotonView>(); } catch { } if ((Object)(object)val2 == (Object)null || val2.ViewID == 0 || (Object)(object)val.GetComponent<SlowProjectile>() == (Object)null) { Object.Destroy((Object)(object)val); return; } try { if (_ownershipUpdate != null && PhotonNetwork.LocalPlayer != null) { int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; _projectileOwnershipScratch[0] = val2.ViewID; _projectileOwnershipScratch[1] = actorNumber; _ownershipUpdate(_projectileOwnershipScratch, actorNumber); } if ((Object)(object)val == (Object)null) { Verbose("[SlowProjectile] ViewID=" + val2.ViewID + " 在所有权转让后已被销毁,跳过 Destroy。"); return; } PhotonNetwork.Destroy(val); Verbose("[SlowProjectile] ViewID=" + val2.ViewID + " 已通过 PhotonNetwork.Destroy 清理服务器缓存。"); } catch (Exception ex) { Verbose("[SlowProjectile] PhotonNetwork.Destroy 失败,回退本地销毁:" + ex.Message); Object.Destroy((Object)(object)val); } } public static bool UseChinese() { return true; } private void OnGUI() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Clamp(HudFontSize, 10, 28); int num2 = Mathf.Clamp(HudOpacityPercent, 20, 100); if (_hudLabelStyle == null || _cachedFontSize != num || _cachedOpacity != num2) { float num3 = (float)num2 / 100f; _hudLabelStyle = new GUIStyle { fontSize = num, richText = true }; _hudLabelStyle.normal.textColor = new Color(1f, 1f, 1f, num3); _hudShadowStyle = new GUIStyle { fontSize = num, richText = true }; _hudShadowStyle.normal.textColor = new Color(0f, 0f, 0f, num3 * 0.7f); _cachedFontSize = num; _cachedOpacity = num2; } } private static void RefreshHudLinesIfNeeded() { //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime < _hudNextRefreshAt) { return; } _hudNextRefreshAt = Time.unscaledTime + 1f; bool flag = UseChinese(); bool hudShowPlayerCount = HudShowPlayerCount; bool hudShowRoomType = HudShowRoomType; bool hudShowRoomName = HudShowRoomName; int num = 0; int num2 = 0; bool flag2 = false; try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { num = currentRoom.PlayerCount; num2 = currentRoom.MaxPlayers; flag2 = currentRoom.IsOpen && (num2 == 0 || num < num2); } } catch { } LobbyTypes? lobbyTypeOrNull = GetLobbyTypeOrNull(); string text = ReadServerName(); bool multi = false; try { multi = SemiFunc.IsMultiplayer(); } catch { } if (flag != _hudCacheUseChinese || hudShowPlayerCount != _hudCacheShowPlayerCount || hudShowRoomType != _hudCacheShowRoomType || hudShowRoomName != _hudCacheShowRoomName || num != _hudCachePlayerCount || num2 != _hudCacheMaxPlayers || lobbyTypeOrNull.HasValue != _hudCacheLobbyTypeKnown || (lobbyTypeOrNull.HasValue && lobbyTypeOrNull.Value != _hudCacheLobbyType) || text != _hudCacheServerName || flag2 != _hudCacheRoomOpen) { _hudCacheUseChinese = flag; _hudCacheShowPlayerCount = hudShowPlayerCount; _hudCacheShowRoomType = hudShowRoomType; _hudCacheShowRoomName = hudShowRoomName; _hudCachePlayerCount = num; _hudCacheMaxPlayers = num2; _hudCacheLobbyTypeKnown = lobbyTypeOrNull.HasValue; _hudCacheLobbyType = lobbyTypeOrNull.GetValueOrDefault(); _hudCacheServerName = text; _hudCacheRoomOpen = flag2; _hudLines.Clear(); if (hudShowPlayerCount) { _hudLines.Add((flag ? "房间人数:" : "Players: ") + GetPlayerCountText(num, num2, multi, flag)); } if (hudShowRoomType) { _hudLines.Add((flag ? "房间类型:" : "Room: ") + GetRoomTypeText(lobbyTypeOrNull, multi, flag, flag2)); } if (hudShowRoomName) { _hudLines.Add((flag ? "房间名称:" : "Name: ") + GetRoomNameText(lobbyTypeOrNull, text, multi, flag)); } } } private static void DrawHud() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) float num = (float)((_cachedFontSize > 0) ? _cachedFontSize : 16) * 1.35f; List<string> list = (ShowJoinStatusEnabled ? BuildLateJoinStatusLines() : _emptyList); int num2 = list.Count + _hudLines.Count; if (num2 != 0) { float num3 = 12f; float num4 = (float)Screen.height - num * (float)num2 - 12f; for (int i = 0; i < list.Count; i++) { float num5 = num4 + (float)i * num; GUI.Label(new Rect(num3 + 1f, num5 + 1f, 480f, num), list[i], _hudShadowStyle); GUI.Label(new Rect(num3, num5, 480f, num), list[i], _hudLabelStyle); } for (int j = 0; j < _hudLines.Count; j++) { float num6 = num4 + (float)(list.Count + j) * num; GUI.Label(new Rect(num3 + 1f, num6 + 1f, 480f, num), _hudLines[j], _hudShadowStyle); GUI.Label(new Rect(num3, num6, 480f, num), _hudLines[j], _hudLabelStyle); } } } private static List<string> BuildLateJoinStatusLines() { int pendingLateJoinerCount = PendingLateJoinerCount; int num = ComputeCoopMaskForHud(); float unscaledTime = Time.unscaledTime; if (pendingLateJoinerCount == _statusLinesLastEntryCount && num == _statusLinesLastCoopMask && unscaledTime < _statusLinesNextRefreshAt) { return _lateJoinStatusLines; } _statusLinesNextRefreshAt = unscaledTime + 0.25f; _statusLinesLastEntryCount = pendingLateJoinerCount; _statusLinesLastCoopMask = num; _lateJoinStatusLines.Clear(); if (pendingLateJoinerCount == 0) { return _lateJoinStatusLines; } _hudEntriesScratch.Clear(); foreach (LateJoinEntry lateJoinEntryValue in LateJoinEntryValues) { _hudEntriesScratch.Add(lateJoinEntryValue); } _hudEntriesScratch.Sort(_entrySortDesc); bool flag = UseChinese(); for (int i = 0; i < _hudEntriesScratch.Count; i++) { LateJoinEntry lateJoinEntry = _hudEntriesScratch[i]; string text; switch (lateJoinEntry.State) { case LateJoinState.Pending: text = (flag ? "等待连接" : "Connecting"); break; case LateJoinState.Queued: text = (flag ? "排队中" : "Queued"); break; case LateJoinState.Processing: case LateJoinState.Finalizing: text = (flag ? "加载中" : "Loading"); break; case LateJoinState.Done: text = (flag ? "已中途加入" : "Late Joined"); break; default: continue; } if (lateJoinEntry.State == LateJoinState.Done && IsCoopActorActive(lateJoinEntry.ActorNumber)) { text += (flag ? "(优化)" : " (Optimized)"); } _lateJoinStatusLines.Add(ResolveDisplayName(lateJoinEntry.ActorNumber, lateJoinEntry.NickName) + ": " + text); } return _lateJoinStatusLines; } private static int ComputeCoopMaskForHud() { int num = 0; foreach (LateJoinEntry lateJoinEntryValue in LateJoinEntryValues) { if (lateJoinEntryValue.State == LateJoinState.Done) { int num2 = (IsCoopActorActive(lateJoinEntryValue.ActorNumber) ? 1 : 0); num = num * 31 + (lateJoinEntryValue.ActorNumber << 1) + num2; } } return num; } private static LobbyTypes? GetLobbyTypeOrNull() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameManager.instance == (Object)null) { return null; } try { object obj = ((_gmLobbyTypeField != null) ? _gmLobbyTypeField.GetValue(GameManager.instance) : null); if (obj is LobbyTypes) { return (LobbyTypes)obj; } } catch { } return null; } private static string ReadServerName() { try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom == null) { return null; } string text = null; Hashtable customProperties = ((RoomInfo)currentRoom).CustomProperties; if (customProperties != null && ((Dictionary<object, object>)(object)customProperties).TryGetValue((object)"server_name", out object value)) { text = value as string; } if (string.IsNullOrWhiteSpace(text)) { text = currentRoom.Name; } return string.IsNullOrEmpty(text) ? null : text; } catch { return null; } } private static string GetRoomTypeText(LobbyTypes? lt, bool multi, bool cn, bool roomOpen) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected I4, but got Unknown if (!multi) { if (!cn) { return "Singleplayer"; } return "单人模式"; } string text; if (lt.HasValue) { text = (int)lt.Value switch { 1 => cn ? "公开服务器" : "Public", 0 => cn ? "私人服务器" : "Private", 2 => cn ? "匹配房间" : "Matchmaking", _ => cn ? "未知" : "Unknown", }; } else { try { text = ((PhotonNetwork.CurrentRoom != null) ? (PhotonNetwork.CurrentRoom.IsVisible ? (cn ? "公开服务器" : "Public") : (cn ? "私人服务器" : "Private")) : (cn ? "未知" : "Unknown")); } catch { text = (cn ? "未知" : "Unknown"); } } string text2 = (roomOpen ? (cn ? "可加入" : "Open") : (cn ? "禁止加入" : "Closed")); return text + " - " + text2; } private static string GetRoomNameText(LobbyTypes? lt, string serverName, bool multi, bool cn) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if (!multi) { if (!cn) { return "N/A"; } return "无"; } if (lt.HasValue ? ((int)lt.Value == 0) : (PhotonNetwork.CurrentRoom != null && !PhotonNetwork.CurrentRoom.IsVisible)) { if (!cn) { return "Friends Only"; } return "仅限好友加入"; } 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 "未知"; } internal static string ResolveDisplayName(int actorNumber, string fallback) { try { if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || (Object)(object)player.photonView == (Object)null || player.photonView.Owner == null || player.photonView.Owner.ActorNumber != actorNumber) { continue; } if (_avatarPlayerNameField != null) { string text = _avatarPlayerNameField.GetValue(player) as string; if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } string nickName = player.photonView.Owner.NickName; if (!string.IsNullOrWhiteSpace(nickName)) { return nickName.Trim(); } break; } } } catch { } if (!UseChinese()) { return "Name Fetching..."; } return "名字获取中"; } private void PatchPrefix(Type target, string methodName, string prefixName) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(target, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("未找到 " + target.Name + "." + methodName + ",跳过该补丁。")); } else { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), prefixName, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Verbose("已注册 " + target.Name + "." + methodName + " Prefix。"); } } internal static void PlayerAvatarStartPostfix(PlayerAvatar __instance) { if (IsHookActive() && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.photonView == (Object)null) && !PhotonNetwork.IsMasterClient && __instance.photonView.IsMine) { TryRunClientLateJoinFixes(__instance); } } 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 levelDeadline = Time.unscaledTime + 30f; while (Time.unscaledTime < levelDeadline && ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)) { yield return null; } if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { TryAnnounceCoopReady(0, "level-not-generated"); yield break; } } float coopDeadline = Time.unscaledTime + 1f; while (Time.unscaledTime < coopDeadline && !IsClientCoopActive()) { yield return null; } if (!IsClientCoopActive()) { TryAnnounceCoopReady(0, "host-not-coop"); yield break; } _clientLateJoinFixesDone = true; yield return null; if (!IsClientCoopActive() || PhotonNetwork.CurrentRoom == null) { TryAnnounceCoopReady(0, "coop-deactivated"); yield break; } int num = 0; int num2 = 0; bool flag2 = false; try { flag2 = SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel(); } catch { } List<PlayerCosmetics> cosmeticsList = new List<PlayerCosmetics>(8); try { if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || (Object)(object)player.photonView == (Object)null || player.photonView.IsMine) { continue; } try { PlayerCosmetics componentInChildren = ((Component)player).GetComponentInChildren<PlayerCosmetics>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.SetupCosmetics(false, true, (List<int>)null); componentInChildren.SetupColors(false, (int[])null); num++; cosmeticsList.Add(componentInChildren); } } catch (Exception ex) { Verbose("客户端外观刷新失败:" + ex.Message); } if (!flag2) { continue; } try { PlayerVoiceChat componentInChildren2 = ((Component)player).GetComponentInChildren<PlayerVoiceChat>(); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.ToggleMixer(false, false); num2++; } } catch (Exception ex2) { Verbose("客户端语音刷新失败:" + ex2.Message); } } } } catch (Exception ex3) { Verbose("远程玩家批量刷新异常:" + ex3.Message); } LogInfo("客户端协同补丁:外观刷新 " + num + " 人,语音刷新 " + num2 + " 人。"); yield return _wait02; if (IsClientCoopActive() && PhotonNetwork.CurrentRoom != null) { foreach (PlayerCosmetics item in cosmeticsList) { if (!((Object)(object)item == (Object)null)) { try { item.SetupCosmetics(false, true, (List<int>)null); item.SetupColors(false, (int[])null); } catch { } } } } bool flag3 = false; try { flag3 = SemiFunc.RunIsLevel(); } catch { } if (flag3) { int num3 = TryForceSpawnAllPhysGrabObjects(); if (num3 > 0) { LogInfo("客户端协同补丁:强制 " + num3 + " 个物品标记为已生成。"); } } int receivedModules = 0; if (flag3) { try { Module[] array = Object.FindObjectsOfType<Module>(); if (array != null) { receivedModules = array.Length; } } catch { } try { PlayerAvatar val = (((Object)(object)PlayerController.instance != (Object)null) ? PlayerController.instance.playerAvatarScript : null); if ((Object)(object)val != (Object)null && (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; } private static int TryForceSpawnAllPhysGrabObjects() { FieldInfo physGrabSpawnedField = GetPhysGrabSpawnedField(); if (physGrabSpawnedField == null) { return 0; } int num = 0; int num2 = 0; try { PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>(); foreach (PhysGrabObject val in array) { if ((Object)(object)val == (Object)null) { continue; } if (HasHinge(val)) { num2++; 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) { LogInfo("跳过 " + num2 + " 个带 hinge 的门(不再销毁,避免门框没门)。"); } return num; } private static FieldInfo GetPhysGrabSpawnedField() { if (_physGrabSpawnedField == null) { _physGrabSpawnedField = F(typeof(PhysGrabObject), "spawned"); } return _physGrabSpawnedField; } private static bool HasHinge(PhysGrabObject p) { try { return (Object)(object)p != (Object)null && (Object)(object)((Component)p).GetComponent<PhysGrabHinge>() != (Object)null; } catch { return false; } } internal static void NetworkConnectOnJoinedRoomPostfix() { if (!_conflictCheckedInRoom) { _conflictCheckedInRoom = true; CheckConflicts("加入房间"); } try { AnnouncePresenceAndRescan(); } catch { } TryJoinSteamLobbyFromPhotonRoom(); } internal static void NetworkManagerOnDisconnectedPostfix() { try { ResetCoopState(); } catch { } } internal static void TryJoinSteamLobbyFromPhotonRoom() { if (PhotonNetwork.IsMasterClient || (Object)(object)SteamManager.instance == (Object)null || IsSteamLobbyValid()) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextSteamLobbyJoinAttempt) { return; } _nextSteamLobbyJoinAttempt = unscaledTime + 4f; try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom == null || !ulong.TryParse(currentRoom.Name, out var result) || result == 0L) { 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 }); Verbose("尝试连入 Steam 大厅 " + result + "..."); } } } catch (Exception ex) { Verbose("TryJoinSteamLobbyFromPhotonRoom 失败:" + ex.Message); } } private static bool IsSteamLobbyValid() { try { if ((Object)(object)SteamManager.instance == (Object)null) { return false; } if (_steamCurrentLobbyField == null) { _steamCurrentLobbyField = F(typeof(SteamManager), "currentLobby"); } if (_steamCurrentLobbyField == null) { return false; } object value = _steamCurrentLobbyField.GetValue(SteamManager.instance); if (value == null) { return false; } if (_steamLobbyIdProp == null) { _steamLobbyIdProp = value.GetType().GetProperty("Id"); } if (_steamLobbyIdProp == null) { return false; } object value2 = _steamLobbyIdProp.GetValue(value); if (value2 == null) { return false; } if (_steamLobbyIdIsValidProp == null) { _steamLobbyIdIsValidProp = value2.GetType().GetProperty("IsValid"); } if (_steamLobbyIdIsValidProp != null && _steamLobbyIdIsValidProp.GetValue(value2) is bool result) { return result; } } catch { } return false; } internal static void NetworkConnectOnConnectedToMasterPrefix() { try { if (IsStaticModEnabled() && PublicRoomPrefixEnabled && !((Object)(object)DataDirector.instance == (Object)null) && !((Object)(object)GameManager.instance == (Object)null) && !(_networkServerNameField == null) && IsPublicGame()) { string text = _networkServerNameField.GetValue(DataDirector.instance) as string; if (!string.IsNullOrEmpty(text) && !HasRoomNamePrefix(text)) { _networkServerNameField.SetValue(DataDirector.instance, (UseChinese() ? "[中途加入-小菜] " : "[latejoin-xc] ") + text); LogInfo("房间名称已更新。"); } } } catch (Exception ex) { Verbose("网络连接前置处理失败:" + ex.Message); } } private static bool HasRoomNamePrefix(string name) { if (name == null) { return false; } if (!name.StartsWith("[中途加入-小菜] ", StringComparison.Ordinal) && !name.StartsWith("[latejoin-xc] ", StringComparison.Ordinal) && !name.StartsWith("[中途加入] ", StringComparison.Ordinal)) { return name.StartsWith("[Late Join] ", StringComparison.Ordinal); } return true; } 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 { try { if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player != (Object)null && (Object)(object)player.photonView != (Object)null && player.photonView.IsMine) { hostAvatar = player; break; } } } if ((Object)(object)hostAvatar != (Object)null && CurrentSceneName() == startScene) { foreach (PlayerVoiceChat voiceChat in GetVoiceChats()) { PhotonView val = (((Object)(object)voiceChat != (Object)null) ? ((Component)voiceChat).GetComponent<PhotonView>() : null); if ((Object)(object)val != (Object)null && val.Owner != null && val.Owner.ActorNumber == hostActor) { hostVoiceViewID = val.ViewID; try { hostAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", newPlayer, new object[1] { val.ViewID }); } catch { } break; } } } } catch { } try { if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null) { foreach (PlayerAvatar player2 in GameDirector.instance.PlayerList) { if ((Object)(object)player2 != (Object)null && (Object)(object)player2.photonView != (Object)null && player2.photonView.Owner != null && player2.photonView.Owner.ActorNumber == newPlayerActor) { newPlayerAvatar = player2; break; } } } if ((Object)(object)newPlayerAvatar != (Object)null && CurrentSceneName() == startScene) { foreach (PlayerVoiceChat voiceChat2 in GetVoiceChats()) { PhotonView val2 = (((Object)(object)voiceChat2 != (Object)null) ? ((Component)voiceChat2).GetComponent<PhotonView>() : null); if ((Object)(object)val2 != (Object)null && val2.Owner != null && val2.Owner.ActorNumber == newPlayerActor) { 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) { newPlayerAvatar = FindAvatarByActor(newPlayerActor); } if ((Object)(object)newPlayerAvatar != (Object)null && newPlayerVoiceViewID == 0) { foreach (PlayerVoiceChat voiceChat3 in GetVoiceChats()) { PhotonView val3 = (((Object)(object)voiceChat3 != (Object)null) ? ((Component)voiceChat3).GetComponent<PhotonView>() : null); if ((Object)(object)val3 != (Object)null && val3.Owner != null && val3.Owner.ActorNumber == newPlayerActor) { 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 ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null) { foreach (PlayerAvatar player3 in GameDirector.instance.PlayerList) { if (!((Object)(object)player3 == (Object)null) && !((Object)(object)player3.photonView == (Object)null) && player3.photonView.Owner != null && player3.photonView.Owner.ActorNumber != newPlayerActor && !player3.photonView.IsMine) { int actorNumber = player3.photonView.Owner.ActorNumber; if (!_lateJoinEntries.TryGetValue(actorNumber, out var value) || value.State != LateJoinState.Aborted) { _oldAvatarsScratch.Add(new KeyValuePair<PlayerAvatar, int>(player3, actorNumber)); } } } } if (_oldAvatarsScratch.Count == 0) { continue; } for (int i = 0; i < _oldAvatarsScratch.Count; i++) { PlayerAvatar av = _oldAvatarsScratch[i].Key; int origActor = _oldAvatarsScratch[i].Value; if ((Object)(object)av == (Object)null || (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 { foreach (PlayerVoiceChat voiceChat4 in GetVoiceChats()) { PhotonView val4 = (((Object)(object)voiceChat4 != (Object)null) ? ((Component)voiceChat4).GetComponent<PhotonView>() : null); if ((Object)(object)val4 != (Object)null && val4.Owner != null && val4.Owner.ActorNumber == origActor) { 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