Decompiled source of LateJoinSimple v0.3.2

plugins/LateJoinSimple.dll

Decompiled 12 hours ago
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