Decompiled source of LateJoinNow v1.0.9

Zichen-LateJoinNow-1.0.9.dll

Decompiled 11 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.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using Zichen_LateJoinNow.Common;
using Zichen_LateJoinNow.I18N;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Zichen-LateJoinNow-1.0.9")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1e059071d3b2bcf585769b06a3b726d514a70e6a")]
[assembly: AssemblyProduct("Zichen-LateJoinNow-1.0.9")]
[assembly: AssemblyTitle("Zichen-LateJoinNow-1.0.9")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Zichen_LateJoinNow
{
	[BepInPlugin("zichen.latejoinnow", "LateJoinNow", "1.0.9")]
	public class Plugin : BaseUnityPlugin
	{
		private sealed class StateSyncEntry
		{
			public string TypeName;

			public string FieldName;

			public string RpcName;

			public bool CastToInt;

			public Type ResolvedType;

			public FieldInfo ResolvedField;

			public bool ResolveAttempted;
		}

		internal enum LateJoinState
		{
			Pending,
			Queued,
			Processing,
			Finalizing,
			Done,
			Aborted
		}

		internal sealed class LateJoinEntry
		{
			public Player Target;

			public int ActorNumber;

			public string NickName;

			public LateJoinState State;

			public string StartScene;
		}

		private delegate void OwnershipUpdateDelegate(int[] viewOwnerPairs, int targetActor);

		public const string PluginGuid = "zichen.latejoinnow";

		public const string PluginName = "LateJoinNow";

		public const string PluginVersion = "1.0.9";

		internal static Plugin Instance;

		internal static Harmony harmony;

		private I18NRegistry _i18n;

		private I18NSwitcher _i18nSwitcher;

		private static ConfigEntry<bool> _cfgModEnabled;

		private static ConfigEntry<bool> _cfgPublicRoomPrefix;

		private static ConfigEntry<bool> _cfgTeleportToHostOnJoin;

		private static ConfigEntry<bool> _cfgCoopMode;

		private static ConfigEntry<bool> _cfgAllowLobby;

		private static ConfigEntry<bool> _cfgAllowShop;

		private static ConfigEntry<bool> _cfgAllowLevel;

		private static ConfigEntry<bool> _cfgHudEnabled;

		private static ConfigEntry<bool> _cfgShowJoinStatus;

		private static ConfigEntry<bool> _cfgHudShowRoomType;

		private static ConfigEntry<bool> _cfgHudShowRoomName;

		private static ConfigEntry<bool> _cfgHudShowPlayerCount;

		private static ConfigEntry<int> _cfgHudFontSize;

		private static ConfigEntry<int> _cfgHudOpacity;

		private static ConfigEntry<bool> _cfgVerboseLog;

		private static ConfigEntry<bool> _cfgShowConflictWarning;

		private float _enforceTimer;

		private const float EnforceInterval = 1f;

		private static readonly FieldRef<RoundDirector, bool> _roundAllExtractionPointsCompletedRef = TryFieldRef<RoundDirector, bool>("allExtractionPointsCompleted");

		private const float SceneAllowedCacheSeconds = 0.5f;

		private static float _sceneAllowedCacheUntil;

		private static bool _sceneAllowedCachedValue;

		private static float _inGameCacheUntil;

		private static bool _inGameCachedValue;

		private static bool _clientLateJoinFixesDone;

		private static bool _clientLateJoinCoroutineRunning;

		private static FieldInfo _physGrabSpawnedField;

		private static Type _physGrabHingeType;

		private static GUIStyle _hudLabelStyle;

		private static GUIStyle _hudShadowStyle;

		private static int _cachedFontSize = -1;

		private static int _cachedOpacity = -1;

		private static readonly List<string> _hudLines = new List<string>(3);

		private const float HudRefreshInterval = 1f;

		private static float _hudNextRefreshAt;

		private static bool _hudCacheUseChinese;

		private static bool _hudCacheShowPlayerCount;

		private static bool _hudCacheShowRoomType;

		private static bool _hudCacheShowRoomName;

		private static int _hudCachePlayerCount;

		private static int _hudCacheMaxPlayers;

		private static bool _hudCacheLobbyTypeKnown;

		private static LobbyTypes _hudCacheLobbyType;

		private static string _hudCacheServerName;

		private static bool _hudCacheRoomOpen;

		private static readonly List<string> _emptyList = new List<string>(0);

		private static readonly List<string> _lateJoinStatusLines = new List<string>(4);

		private static readonly List<LateJoinEntry> _hudEntriesScratch = new List<LateJoinEntry>(8);

		private static readonly Comparison<LateJoinEntry> _entrySortDesc = (LateJoinEntry a, LateJoinEntry b) => b.ActorNumber.CompareTo(a.ActorNumber);

		private const float StatusLinesRefreshSeconds = 0.25f;

		private static float _statusLinesNextRefreshAt;

		private static int _statusLinesLastEntryCount = -1;

		private static int _statusLinesLastCoopMask;

		private static readonly FieldRef<PlayerAvatar, string> _avatarPlayerNameRef = TryAvatarPlayerNameRef();

		private static readonly StateSyncEntry[] _stateSyncEntries = new StateSyncEntry[22]
		{
			S("ExtractionPoint", "StateSetRPC", castToInt: false),
			S("ShopKeycardDoor", "StateSetRPC"),
			S("UpgradeStand", "StateSetRPC"),
			S("ItemValuableBox", "StateSetRPC"),
			S("ItemMine", "StateSetRPC", castToInt: true, "state"),
			S("ItemMelee", "StateSetRPC"),
			S("ItemGun", "StateSetRPC", castToInt: true, "stateCurrent"),
			S("ItemDrone", "StateSetRPC"),
			S("ItemVehicle", "SetStateRPC"),
			S("ItemCartCannonMain", "StateSetRPC", castToInt: true, "stateCurrent"),
			S("FanTrap", "SetStateRPC", castToInt: false),
			S("CrystalBallValuable", "SetStateRPC", castToInt: false),
			S("BlenderValuable", "SetStateRPC", castToInt: false),
			S("FlamethrowerValuable", "SetStateRPC", castToInt: false),
			S("IceSawValuable", "SetStateRPC", castToInt: false),
			S("FireExtinguisherValuable", "SetStateRPC", castToInt: false),
			S("ScreamDollValuable", "SetStateRPC", castToInt: false),
			S("JackhammerValuable", "SetStateRPC", castToInt: false),
			S("TrafficLightValuable", "SetStateRPC", castToInt: false),
			S("ValuableWizardTimeGlass", "SetStateRPC", castToInt: false),
			S("ValuableArcticSnowBike", "SetStateRPC"),
			S("ValuableEgg", "SetStateRPC", castToInt: false)
		};

		private static FieldInfo _epIsLockedFieldInfo;

		private static readonly Dictionary<int, LateJoinEntry> _lateJoinEntries = new Dictionary<int, LateJoinEntry>();

		private static readonly Queue<int> _lateJoinQueue = new Queue<int>();

		private static bool _schedulerRunning;

		private static readonly FieldRef<PlayerAvatar, PlayerDeathHead> _playerDeathHeadRef = AccessTools.FieldRefAccess<PlayerAvatar, PlayerDeathHead>("playerDeathHead");

		private static readonly FieldRef<PlayerAvatar, PlayerTumble> _playerTumbleRef = AccessTools.FieldRefAccess<PlayerAvatar, PlayerTumble>("tumble");

		private static readonly FieldRef<PlayerAvatar, bool> _avatarAnimCompleted = TryAvatarAnimCompletedRef();

		private static readonly FieldRef<Module, bool> _modTop = AccessTools.FieldRefAccess<Module, bool>("ConnectingTop");

		private static readonly FieldRef<Module, bool> _modBottom = AccessTools.FieldRefAccess<Module, bool>("ConnectingBottom");

		private static readonly FieldRef<Module, bool> _modRight = AccessTools.FieldRefAccess<Module, bool>("ConnectingRight");

		private static readonly FieldRef<Module, bool> _modLeft = AccessTools.FieldRefAccess<Module, bool>("ConnectingLeft");

		private static readonly FieldRef<Module, bool> _modFirst = AccessTools.FieldRefAccess<Module, bool>("First");

		private static readonly FieldRef<Module, bool> _modDone = AccessTools.FieldRefAccess<Module, bool>("SetupDone");

		private static readonly FieldRef<ValuableObject, bool> _valSet = AccessTools.FieldRefAccess<ValuableObject, bool>("dollarValueSet");

		private static readonly FieldRef<ValuableObject, float> _valCurrent = AccessTools.FieldRefAccess<ValuableObject, float>("dollarValueCurrent");

		private static readonly FieldRef<ValuableObject, bool> _valDisc = AccessTools.FieldRefAccess<ValuableObject, bool>("discovered");

		private static readonly FieldRef<ValuableObject, bool> _valHaul = AccessTools.FieldRefAccess<ValuableObject, bool>("inStartRoom");

		private static readonly FieldRef<MenuPageLobby, List<string>> _menuLobbyJoiningPlayersRef = TryMenuLobbyListRef("joiningPlayers");

		private static readonly FieldRef<MenuPageLobby, float> _menuLobbyJoiningTimerRef = TryMenuLobbyFloatRef("joiningPlayersTimer");

		private static readonly FieldRef<MenuPageLobby, float> _menuLobbyJoiningEndTimerRef = TryMenuLobbyFloatRef("joiningPlayersEndTimer");

		private static readonly MethodInfo _ownershipUpdateMethod = typeof(PhotonNetwork).GetMethod("OwnershipUpdate", BindingFlags.Static | BindingFlags.NonPublic);

		private static readonly OwnershipUpdateDelegate _ownershipUpdate = ((_ownershipUpdateMethod != null) ? ((OwnershipUpdateDelegate)Delegate.CreateDelegate(typeof(OwnershipUpdateDelegate), _ownershipUpdateMethod)) : null);

		private static readonly int[] _ownershipScratch = new int[2];

		private static bool _refreshOldPlayersBusy;

		private static readonly List<(PlayerAvatar av, int ownerActor)> _oldAvatarsScratch = new List<(PlayerAvatar, int)>(8);

		private static readonly WaitForSecondsRealtime _wait02 = new WaitForSecondsRealtime(0.2f);

		private static readonly WaitForSecondsRealtime _wait05 = new WaitForSecondsRealtime(0.5f);

		private static readonly WaitForSecondsRealtime _wait1 = new WaitForSecondsRealtime(1f);

		private static readonly WaitForSecondsRealtime _wait2 = new WaitForSecondsRealtime(2f);

		private static readonly WaitForSecondsRealtime _wait25 = new WaitForSecondsRealtime(2.5f);

		private static readonly WaitForSecondsRealtime _wait15 = new WaitForSecondsRealtime(1.5f);

		private const int ModuleBatchSize = 8;

		private const int ValuableBatchSize = 6;

		private static readonly FieldRef<ItemAttributes, int> _itemValueRef = TryItemValueRef();

		private const int ItemBatchSize = 8;

		private static readonly int[] _projectileOwnershipScratch = new int[2];

		private const float ChangeLevelCooldownSeconds = 0f;

		private const float WaitGeneratedTimeoutSeconds = 60f;

		private const float WaitGeneratedPollInterval = 0.2f;

		private const float CooldownLockedSentinelSeconds = 600f;

		private const string RoomNamePrefixCN = "[中途加入] ";

		private const string RoomNamePrefixEN = "[Late Join] ";

		private static readonly FieldRef<DataDirector, string> _networkServerNameRef = TryFieldRefRoom();

		private static readonly FieldRef<RunManager, List<PlayerVoiceChat>> _runManagerVoiceChatsRef = TryFieldRefRM();

		private static readonly HashSet<int> _scenePersistentViewIds = new HashSet<int>(64);

		private static float _changeLevelCooldownUntil;

		private static bool _changeLevelCooldownRunning;

		private static bool? _lastSceneAllowed;

		private static readonly WaitForSecondsRealtime _waitGeneratedPoll = new WaitForSecondsRealtime(0.2f);

		private static bool _steamLobbyLocked;

		private static float _steamUnlockEnsureNextAt;

		private const float SteamUnlockEnsureInterval = 5f;

		private static FieldInfo _steamPrivateLobbyField;

		private static FieldInfo _steamCurrentLobbyField;

		private static PropertyInfo _steamLobbyIdProp;

		private static PropertyInfo _steamLobbyIdIsValidProp;

		private static MethodInfo _steamJoinLobbyMethod;

		private static FieldInfo _steamIdValueField;

		internal const int CoopProtocolVersion = 1;

		internal const string CoopVersionKey = "LateJoinNow.Version";

		internal const string CoopProtoKey = "LateJoinNow.Protocol";

		internal const string CoopReadyKey = "LateJoinNow.Ready";

		internal const string CoopModuleCountKey = "LateJoinNow.ModuleCount";

		private static readonly Dictionary<int, long> _coopReadyByActor = new Dictionary<int, long>(8);

		private static readonly Dictionary<int, int> _coopModuleCountByActor = new Dictionary<int, int>(8);

		private static readonly Dictionary<int, bool> _coopByActor = new Dictionary<int, bool>(8);

		private static bool _hostCoopCompatible;

		private static PeerCoopCallbackListener _coopListener;

		private static readonly Hashtable _presencePropsScratch = new Hashtable();

		private static readonly Hashtable _readyPropsScratch = new Hashtable();

		private static readonly Hashtable _clearPropsScratch = new Hashtable();

		public static bool PublicRoomPrefixEnabled
		{
			get
			{
				if (_cfgPublicRoomPrefix != null)
				{
					return _cfgPublicRoomPrefix.Value;
				}
				return true;
			}
		}

		public static bool TeleportToHostOnJoinEnabled
		{
			get
			{
				if (_cfgTeleportToHostOnJoin != null)
				{
					return _cfgTeleportToHostOnJoin.Value;
				}
				return false;
			}
		}

		public static bool CoopModeEnabled
		{
			get
			{
				if (_cfgCoopMode != null)
				{
					return _cfgCoopMode.Value;
				}
				return true;
			}
		}

		public static bool AllowLobby
		{
			get
			{
				if (_cfgAllowLobby != null)
				{
					return _cfgAllowLobby.Value;
				}
				return true;
			}
		}

		public static bool AllowShop
		{
			get
			{
				if (_cfgAllowShop != null)
				{
					return _cfgAllowShop.Value;
				}
				return true;
			}
		}

		public static bool AllowLevel
		{
			get
			{
				if (_cfgAllowLevel != null)
				{
					return _cfgAllowLevel.Value;
				}
				return true;
			}
		}

		public static bool HudEnabled
		{
			get
			{
				if (_cfgHudEnabled != null)
				{
					return _cfgHudEnabled.Value;
				}
				return true;
			}
		}

		public static bool ShowJoinStatusEnabled
		{
			get
			{
				if (_cfgShowJoinStatus != null)
				{
					return _cfgShowJoinStatus.Value;
				}
				return true;
			}
		}

		public static bool HudShowRoomType
		{
			get
			{
				if (_cfgHudShowRoomType != null)
				{
					return _cfgHudShowRoomType.Value;
				}
				return true;
			}
		}

		public static bool HudShowRoomName
		{
			get
			{
				if (_cfgHudShowRoomName != null)
				{
					return _cfgHudShowRoomName.Value;
				}
				return true;
			}
		}

		public static bool HudShowPlayerCount
		{
			get
			{
				if (_cfgHudShowPlayerCount != null)
				{
					return _cfgHudShowPlayerCount.Value;
				}
				return true;
			}
		}

		public static int HudFontSize
		{
			get
			{
				if (_cfgHudFontSize != null)
				{
					return _cfgHudFontSize.Value;
				}
				return 16;
			}
		}

		public static int HudOpacityPercent
		{
			get
			{
				if (_cfgHudOpacity != null)
				{
					return _cfgHudOpacity.Value;
				}
				return 80;
			}
		}

		public static bool VerboseLogEnabled
		{
			get
			{
				if (_cfgVerboseLog != null)
				{
					return _cfgVerboseLog.Value;
				}
				return false;
			}
		}

		public static bool ShowConflictWarningEnabled
		{
			get
			{
				if (_cfgShowConflictWarning != null)
				{
					return _cfgShowConflictWarning.Value;
				}
				return true;
			}
		}

		internal static Dictionary<int, LateJoinEntry>.ValueCollection LateJoinEntryValues => _lateJoinEntries.Values;

		internal static int PendingLateJoinerCount => _lateJoinEntries.Count;

		internal static bool IsInChangeLevelCooldown => Time.unscaledTime < _changeLevelCooldownUntil;

		private static float CooldownLeft => Mathf.Max(0f, _changeLevelCooldownUntil - Time.unscaledTime);

		public static bool IsStaticModEnabled()
		{
			if (_cfgModEnabled != null)
			{
				return _cfgModEnabled.Value;
			}
			return false;
		}

		public static bool UseChinese()
		{
			return Instance?._i18n?.UseChinese == true;
		}

		internal static void Verbose(string msg)
		{
			if (VerboseLogEnabled)
			{
				Plugin instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogInfo((object)("[Verbose] " + msg));
				}
			}
		}

		internal static void LogInfo(string msg)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)msg);
			}
		}

		internal static void LogWarning(string msg)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)msg);
			}
		}

		internal static void LogError(string msg)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogError((object)msg);
			}
		}

		internal static bool IsHookActive()
		{
			if (IsStaticModEnabled())
			{
				return CompatibilityReport.RuntimeReady;
			}
			return false;
		}

		private static FieldRef<TInst, TFld> TryFieldRef<TInst, TFld>(string name)
		{
			try
			{
				return (AccessTools.Field(typeof(TInst), name) != null) ? AccessTools.FieldRefAccess<TInst, TFld>(name) : null;
			}
			catch
			{
				return null;
			}
		}

		public static bool IsCurrentSceneAllowed()
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < _sceneAllowedCacheUntil)
			{
				return _sceneAllowedCachedValue;
			}
			_sceneAllowedCacheUntil = unscaledTime + 0.5f;
			_sceneAllowedCachedValue = ComputeSceneAllowed();
			return _sceneAllowedCachedValue;
		}

		private static bool IsInGame()
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < _inGameCacheUntil)
			{
				return _inGameCachedValue;
			}
			_inGameCacheUntil = unscaledTime + 0.5f;
			try
			{
				_inGameCachedValue = SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel();
			}
			catch
			{
				_inGameCachedValue = false;
			}
			return _inGameCachedValue;
		}

		private static bool ComputeSceneAllowed()
		{
			if (!IsStaticModEnabled() || !PhotonNetwork.IsMasterClient || (Object)(object)RunManager.instance == (Object)null)
			{
				return false;
			}
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom != null && currentRoom.MaxPlayers > 0 && currentRoom.PlayerCount >= currentRoom.MaxPlayers)
				{
					return false;
				}
				if (SemiFunc.RunIsLobby())
				{
					return AllowLobby;
				}
				if (SemiFunc.RunIsShop())
				{
					return AllowShop;
				}
				if (SemiFunc.RunIsArena())
				{
					return false;
				}
				if (!SemiFunc.RunIsLevel())
				{
					return false;
				}
				if (!AllowLevel)
				{
					return false;
				}
				if ((Object)(object)RoundDirector.instance != (Object)null && _roundAllExtractionPointsCompletedRef != null)
				{
					try
					{
						if (_roundAllExtractionPointsCompletedRef.Invoke(RoundDirector.instance))
						{
							return false;
						}
					}
					catch
					{
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				Plugin instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)("场景判定失败:" + ex.Message));
				}
			}
			return false;
		}

		public static bool IsInPreGameMenu()
		{
			try
			{
				if ((Object)(object)RunManager.instance == (Object)null)
				{
					return false;
				}
				if (SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel() || SemiFunc.RunIsArena())
				{
					return false;
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private void Awake()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			DetachFromManager();
			Instance = this;
			ConfigVersionResetter.ResetIfVersionChanged(((BaseUnityPlugin)this).Config, "1.0.9", ((BaseUnityPlugin)this).Logger);
			BindConfig();
			if (!IsStaticModEnabled())
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"LateJoinNow v1.0.9 已禁用。");
				return;
			}
			CompatibilityReport.Build();
			try
			{
				harmony = new Harmony("zichen.latejoinnow");
				InstallPatchesSafely();
				_i18nSwitcher = new I18NSwitcher(_i18n, harmony, "LateJoinNow", ((BaseUnityPlugin)this).Logger);
				_i18nSwitcher.InstallRepoConfigHook();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"LateJoinNow v1.0.9 已加载。");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("LateJoinNow 启动失败:" + ex.Message));
			}
			if (CompatibilityReport.HasIssues)
			{
				((MonoBehaviour)this).StartCoroutine(WaitAndShowCompatPopup());
			}
			if (ShowConflictWarningEnabled)
			{
				ConflictDetect.Detect();
				if (ConflictDetect.HasConflict)
				{
					((MonoBehaviour)this).StartCoroutine(WaitAndShowConflictPopup());
				}
			}
		}

		private void Update()
		{
			if (IsHookActive() && PhotonNetwork.IsMasterClient)
			{
				_enforceTimer -= Time.unscaledDeltaTime;
				if (!(_enforceTimer > 0f))
				{
					_enforceTimer = 1f;
					EnforceLobbyState();
				}
			}
		}

		private void BindConfig()
		{
			bool defaultChinese = LanguageDetector.DetectDefault();
			_i18n = new I18NRegistry(((BaseUnityPlugin)this).Config);
			_i18n.BindLanguage(defaultChinese);
			_i18n.BindReadOnly("Mod Info", "模组信息", "Mod Name", "模组名称", "Late Join Now", "中途立即加入");
			_i18n.BindReadOnly("Mod Info", "模组信息", "Mod Version", "模组版本号", "1.0.9", "1.0.9");
			_cfgModEnabled = _i18n.Bind("A.Global", "A.全局设置", "Mod Enabled", "模组启用", "Disable to turn off the whole mod.", "关闭后整个模组全部功能失效。", defaultValue: true);
			_cfgPublicRoomPrefix = _i18n.Bind("A.Global", "A.全局设置", "Public Room Name Prefix", "公开房名加前缀", "Auto add [Late Join] prefix to public room names.", "创建公开房时自动加 [中途加入] 前缀。", defaultValue: true);
			_cfgTeleportToHostOnJoin = _i18n.Bind("A.Global", "A.全局设置", "Teleport To Host On Join", "加入后传送到房主位置", "When enabled, late joiners are teleported to the host; if the host is dead, the closest alive player is used.", "开启时新加入者直接传送到房主位置;若房主死亡,则传送到最近一个活着的玩家位置。", defaultValue: false);
			_cfgCoopMode = _i18n.Bind("A.Global", "A.全局设置", "Coop Mode", "双端协同优化", "Enable optimized late join when both host and client have this mod (same version). Disable to fall back to legacy single-end mode (debug only).", "双端都装同版本模组时启用协同优化(更快加载、自动刷新外观/语音)。关闭后所有客户端按单端模式跑(仅调试用)。", defaultValue: true);
			_cfgAllowLobby = _i18n.Bind("B.Scene Whitelist", "B.场景白名单", "Allow Joining In Truck", "允许在卡车阶段加入", "Allow late joiners during the truck stage.", "卡车阶段允许中途加入。", defaultValue: true);
			_cfgAllowShop = _i18n.Bind("B.Scene Whitelist", "B.场景白名单", "Allow Joining In Shop", "允许在商店阶段加入", "Allow late joiners during the shop stage.", "商店阶段允许中途加入。", defaultValue: true);
			_cfgAllowLevel = _i18n.Bind("B.Scene Whitelist", "B.场景白名单", "Allow Joining In Level", "允许在关卡进行中加入", "Allow late joiners while a level is in progress.", "关卡进行中允许中途加入。", defaultValue: true);
			_cfgHudEnabled = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Enabled", "启用", "Show the room info HUD at the bottom-left.", "左下角显示房间信息框。", defaultValue: true);
			_cfgShowJoinStatus = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Join Status", "显示加入状态", "Show join progress of late joiners.", "显示中途加入玩家的进度。", defaultValue: true);
			_cfgHudShowPlayerCount = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Player Count", "显示房间人数", "Show current/max player count.", "显示当前人数/最大人数。", defaultValue: true);
			_cfgHudShowRoomType = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Room Type", "显示房间类型", "Show public/private/singleplayer.", "显示公开/私人/单人。", defaultValue: true);
			_cfgHudShowRoomName = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Show Room Name", "显示房间名称", "Show room name.", "显示房间名称。", defaultValue: true);
			_cfgHudFontSize = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Font Size", "字体大小", "HUD font size.", "HUD 字体大小。", 16, (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 28));
			_cfgHudOpacity = _i18n.Bind("C.Room Info HUD", "C.房间信息显示", "Opacity/%", "文字透明度/%", "HUD opacity.", "HUD 透明度。", 80, (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 100));
			_cfgVerboseLog = _i18n.Bind("D.Debug", "D.调试", "Verbose Log", "详细日志", "Output detailed debug log.", "输出详细调试日志。", defaultValue: false);
			_cfgShowConflictWarning = _i18n.Bind("D.Debug", "D.调试", "Conflict Mod Warning", "同类模组冲突警告", "Show warning when same-kind mods are detected.", "检测到同类 mod 时弹警告。", defaultValue: true);
			if (_cfgCoopMode != null)
			{
				_cfgCoopMode.SettingChanged += OnCoopModeChanged;
			}
		}

		private static void OnCoopModeChanged(object _, EventArgs __)
		{
			try
			{
				if (CoopModeEnabled)
				{
					AnnouncePresenceAndRescan();
					LogInfo("协同模式已开启:重扫房间内对端协同状态。");
				}
				else
				{
					ClearOwnCoopProperties();
					ResetCoopState();
					LogInfo("协同模式已关闭:清理协同状态,所有客户端按单端模式跑。");
				}
			}
			catch (Exception ex)
			{
				Verbose("OnCoopModeChanged 失败:" + ex.Message);
			}
		}

		private void DetachFromManager()
		{
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)52;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}

		internal static void TryRunClientLateJoinFixes(PlayerAvatar localAvatar)
		{
			if ((Object)(object)localAvatar == (Object)null || (Object)(object)Instance == (Object)null || !IsStaticModEnabled() || PhotonNetwork.IsMasterClient || _clientLateJoinFixesDone || _clientLateJoinCoroutineRunning)
			{
				return;
			}
			try
			{
				if (!SemiFunc.IsMultiplayer())
				{
					return;
				}
			}
			catch
			{
				return;
			}
			try
			{
				if (!SemiFunc.RunIsLobby() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsLevel())
				{
					return;
				}
			}
			catch
			{
				return;
			}
			_clientLateJoinCoroutineRunning = true;
			((MonoBehaviour)Instance).StartCoroutine(ClientLateJoinFixesCoroutine());
		}

		private static IEnumerator ClientLateJoinFixesCoroutine()
		{
			try
			{
				bool flag = false;
				try
				{
					flag = SemiFunc.RunIsLevel();
				}
				catch
				{
				}
				if (flag)
				{
					float deadline = Time.unscaledTime + 30f;
					while (Time.unscaledTime < deadline && (!((Object)(object)LevelGenerator.Instance != (Object)null) || !LevelGenerator.Instance.Generated))
					{
						yield return null;
					}
					if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
					{
						TryAnnounceCoopReady(0, "level-not-generated");
						yield break;
					}
				}
				float coopDeadline = Time.unscaledTime + 1f;
				while (Time.unscaledTime < coopDeadline && !IsClientCoopActive())
				{
					yield return null;
				}
				if (!IsClientCoopActive())
				{
					TryAnnounceCoopReady(0, "host-not-coop");
					yield break;
				}
				_clientLateJoinFixesDone = true;
				yield return null;
				if (!IsClientCoopActive() || PhotonNetwork.CurrentRoom == null)
				{
					TryAnnounceCoopReady(0, "coop-deactivated");
					yield break;
				}
				int num = 0;
				int num2 = 0;
				bool flag2 = false;
				try
				{
					flag2 = SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel();
				}
				catch
				{
				}
				List<PlayerCosmetics> cosmeticsList = new List<PlayerCosmetics>(8);
				try
				{
					List<PlayerAvatar> list = GameDirector.instance?.PlayerList;
					if (list != null)
					{
						foreach (PlayerAvatar item in list)
						{
							if ((Object)(object)item?.photonView == (Object)null || item.photonView.IsMine)
							{
								continue;
							}
							try
							{
								PlayerCosmetics componentInChildren = ((Component)item).GetComponentInChildren<PlayerCosmetics>();
								if ((Object)(object)componentInChildren != (Object)null)
								{
									componentInChildren.SetupCosmetics(false, true, (List<int>)null);
									componentInChildren.SetupColors(false, (int[])null);
									num++;
									cosmeticsList.Add(componentInChildren);
								}
							}
							catch (Exception ex)
							{
								Verbose("客户端外观刷新失败:" + ex.Message);
							}
							if (!flag2)
							{
								continue;
							}
							try
							{
								PlayerVoiceChat componentInChildren2 = ((Component)item).GetComponentInChildren<PlayerVoiceChat>();
								if ((Object)(object)componentInChildren2 != (Object)null)
								{
									componentInChildren2.ToggleMixer(false, false);
									num2++;
								}
							}
							catch (Exception ex2)
							{
								Verbose("客户端语音刷新失败:" + ex2.Message);
							}
						}
					}
				}
				catch (Exception ex3)
				{
					Verbose("远程玩家批量刷新异常:" + ex3.Message);
				}
				LogInfo($"客户端协同补丁:外观刷新 {num} 人,语音刷新 {num2} 人。");
				yield return _wait02;
				if (IsClientCoopActive() && PhotonNetwork.CurrentRoom != null)
				{
					try
					{
						foreach (PlayerCosmetics item2 in cosmeticsList)
						{
							if (!((Object)(object)item2 == (Object)null))
							{
								try
								{
									item2.SetupCosmetics(false, true, (List<int>)null);
									item2.SetupColors(false, (int[])null);
								}
								catch
								{
								}
							}
						}
					}
					catch
					{
					}
				}
				bool flag3 = false;
				try
				{
					flag3 = SemiFunc.RunIsLevel();
				}
				catch
				{
				}
				if (flag3)
				{
					int num3 = TryForceSpawnAllPhysGrabObjects();
					if (num3 > 0)
					{
						LogInfo($"客户端协同补丁:强制 {num3} 个物品标记为已生成。");
					}
				}
				int receivedModules = 0;
				if (flag3)
				{
					try
					{
						Module[] array = Object.FindObjectsOfType<Module>();
						if (array != null)
						{
							receivedModules = array.Length;
						}
					}
					catch
					{
					}
				}
				if (flag3)
				{
					try
					{
						PlayerAvatar val = PlayerController.instance?.playerAvatarScript;
						if ((Object)(object)val?.photonView != (Object)null && val.photonView.IsMine)
						{
							val.LoadingLevelAnimationCompleted();
						}
					}
					catch (Exception ex4)
					{
						Verbose("发送 LoadingLevelAnimationCompleted 失败:" + ex4.Message);
					}
				}
				TryAnnounceCoopReady(receivedModules, "fixes-complete");
			}
			finally
			{
				_clientLateJoinCoroutineRunning = false;
			}
		}

		private static void TryAnnounceCoopReady(int receivedModules = 0, string reason = null)
		{
			try
			{
				AnnounceCoopReady(receivedModules);
				if (!string.IsNullOrEmpty(reason))
				{
					Verbose("协同 ack(" + reason + ")");
				}
			}
			catch (Exception ex)
			{
				Verbose("发协同 ack 失败:" + ex.Message);
			}
		}

		internal static void ResetClientCoopState()
		{
			_clientLateJoinFixesDone = false;
			_clientLateJoinCoroutineRunning = false;
		}

		internal static void TryForceSpawnSingle(PhysGrabObject p)
		{
			if ((Object)(object)p == (Object)null || PhotonNetwork.IsMasterClient || !IsClientCoopActive() || !_clientLateJoinFixesDone)
			{
				return;
			}
			try
			{
				if (!SemiFunc.RunIsLevel())
				{
					return;
				}
			}
			catch
			{
				return;
			}
			if (HasHinge(p))
			{
				try
				{
					Object.Destroy((Object)(object)((Component)p).gameObject);
					return;
				}
				catch
				{
					return;
				}
			}
			FieldInfo physGrabSpawnedField = GetPhysGrabSpawnedField();
			if (physGrabSpawnedField == null)
			{
				return;
			}
			try
			{
				physGrabSpawnedField.SetValue(p, true);
				if ((Object)(object)p.rb != (Object)null && p.rb.isKinematic)
				{
					p.rb.isKinematic = false;
				}
			}
			catch
			{
			}
		}

		private static FieldInfo GetPhysGrabSpawnedField()
		{
			if (_physGrabSpawnedField != null)
			{
				return _physGrabSpawnedField;
			}
			try
			{
				_physGrabSpawnedField = AccessTools.Field(typeof(PhysGrabObject), "spawned");
			}
			catch
			{
			}
			return _physGrabSpawnedField;
		}

		private static Type GetPhysGrabHingeType()
		{
			if (_physGrabHingeType != null)
			{
				return _physGrabHingeType;
			}
			try
			{
				_physGrabHingeType = AccessTools.TypeByName("PhysGrabHinge");
			}
			catch
			{
			}
			return _physGrabHingeType;
		}

		private static bool HasHinge(PhysGrabObject p)
		{
			Type physGrabHingeType = GetPhysGrabHingeType();
			if (physGrabHingeType == null)
			{
				return false;
			}
			try
			{
				return (Object)(object)((Component)p).GetComponent(physGrabHingeType) != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		private static int TryForceSpawnAllPhysGrabObjects()
		{
			FieldInfo physGrabSpawnedField = GetPhysGrabSpawnedField();
			if (physGrabSpawnedField == null)
			{
				return 0;
			}
			int num = 0;
			int num2 = 0;
			try
			{
				PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
				foreach (PhysGrabObject val in array)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					if (HasHinge(val))
					{
						try
						{
							Object.Destroy((Object)(object)((Component)val).gameObject);
							num2++;
						}
						catch
						{
						}
						continue;
					}
					try
					{
						physGrabSpawnedField.SetValue(val, true);
						if ((Object)(object)val.rb != (Object)null && val.rb.isKinematic)
						{
							val.rb.isKinematic = false;
						}
						num++;
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				Verbose("强制 spawned 失败:" + ex.Message);
			}
			if (num2 > 0)
			{
				Verbose($"本地销毁 {num2} 个门,避免卡门。");
			}
			return num;
		}

		private void OnGUI()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			if (!IsHookActive() || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			Event current = Event.current;
			if (current == null || (int)current.type != 7)
			{
				return;
			}
			bool flag = IsInGame();
			bool flag2 = HudEnabled && flag && PhotonNetwork.CurrentRoom != null;
			bool flag3 = ShowJoinStatusEnabled && PendingLateJoinerCount > 0;
			if (flag2 || flag3)
			{
				EnsureHudStyles();
				if (flag2)
				{
					RefreshHudLinesIfNeeded();
				}
				else
				{
					_hudLines.Clear();
				}
				DrawHud();
			}
		}

		private static void EnsureHudStyles()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			int hudFontSize = HudFontSize;
			int hudOpacityPercent = HudOpacityPercent;
			if (_hudLabelStyle == null || _cachedFontSize != hudFontSize || _cachedOpacity != hudOpacityPercent)
			{
				hudFontSize = Mathf.Clamp(hudFontSize, 10, 28);
				hudOpacityPercent = Mathf.Clamp(hudOpacityPercent, 20, 100);
				if (_hudLabelStyle == null || _cachedFontSize != hudFontSize || _cachedOpacity != hudOpacityPercent)
				{
					float num = (float)hudOpacityPercent / 100f;
					_hudLabelStyle = new GUIStyle
					{
						fontSize = hudFontSize,
						richText = true
					};
					_hudLabelStyle.normal.textColor = new Color(1f, 1f, 1f, num);
					_hudShadowStyle = new GUIStyle
					{
						fontSize = hudFontSize,
						richText = true
					};
					_hudShadowStyle.normal.textColor = new Color(0f, 0f, 0f, num * 0.7f);
					_cachedFontSize = hudFontSize;
					_cachedOpacity = hudOpacityPercent;
				}
			}
		}

		private static void RefreshHudLinesIfNeeded()
		{
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime < _hudNextRefreshAt)
			{
				return;
			}
			_hudNextRefreshAt = Time.unscaledTime + 1f;
			bool flag = UseChinese();
			bool hudShowPlayerCount = HudShowPlayerCount;
			bool hudShowRoomType = HudShowRoomType;
			bool hudShowRoomName = HudShowRoomName;
			int num = 0;
			int num2 = 0;
			bool flag2 = false;
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom != null)
				{
					num = currentRoom.PlayerCount;
					num2 = currentRoom.MaxPlayers;
					flag2 = currentRoom.IsOpen && (num2 == 0 || num < num2);
				}
			}
			catch
			{
			}
			LobbyTypes? lobbyTypeOrNull = GetLobbyTypeOrNull();
			string text = ReadServerName();
			bool multi = false;
			try
			{
				multi = SemiFunc.IsMultiplayer();
			}
			catch
			{
			}
			if (flag != _hudCacheUseChinese || hudShowPlayerCount != _hudCacheShowPlayerCount || hudShowRoomType != _hudCacheShowRoomType || hudShowRoomName != _hudCacheShowRoomName || num != _hudCachePlayerCount || num2 != _hudCacheMaxPlayers || lobbyTypeOrNull.HasValue != _hudCacheLobbyTypeKnown || (lobbyTypeOrNull.HasValue && lobbyTypeOrNull.Value != _hudCacheLobbyType) || text != _hudCacheServerName || flag2 != _hudCacheRoomOpen)
			{
				_hudCacheUseChinese = flag;
				_hudCacheShowPlayerCount = hudShowPlayerCount;
				_hudCacheShowRoomType = hudShowRoomType;
				_hudCacheShowRoomName = hudShowRoomName;
				_hudCachePlayerCount = num;
				_hudCacheMaxPlayers = num2;
				_hudCacheLobbyTypeKnown = lobbyTypeOrNull.HasValue;
				_hudCacheLobbyType = lobbyTypeOrNull.GetValueOrDefault();
				_hudCacheServerName = text;
				_hudCacheRoomOpen = flag2;
				_hudLines.Clear();
				if (hudShowPlayerCount)
				{
					_hudLines.Add((flag ? "房间人数:" : "Players: ") + GetPlayerCountText(num, num2, multi, flag));
				}
				if (hudShowRoomType)
				{
					_hudLines.Add((flag ? "房间类型:" : "Room: ") + GetRoomTypeText(lobbyTypeOrNull, multi, flag, flag2));
				}
				if (hudShowRoomName)
				{
					_hudLines.Add((flag ? "房间名称:" : "Name: ") + GetRoomNameText(lobbyTypeOrNull, text, multi, flag));
				}
			}
		}

		private static void DrawHud()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			float num = (float)((_cachedFontSize > 0) ? _cachedFontSize : 16) * 1.35f;
			List<string> list = (ShowJoinStatusEnabled ? BuildLateJoinStatusLines() : _emptyList);
			int num2 = list.Count + _hudLines.Count;
			if (num2 != 0)
			{
				float num3 = 12f;
				float num4 = (float)Screen.height - num * (float)num2 - 12f;
				for (int i = 0; i < list.Count; i++)
				{
					float num5 = num4 + (float)i * num;
					GUI.Label(new Rect(num3 + 1f, num5 + 1f, 480f, num), list[i], _hudShadowStyle);
					GUI.Label(new Rect(num3, num5, 480f, num), list[i], _hudLabelStyle);
				}
				for (int j = 0; j < _hudLines.Count; j++)
				{
					float num6 = num4 + (float)(list.Count + j) * num;
					GUI.Label(new Rect(num3 + 1f, num6 + 1f, 480f, num), _hudLines[j], _hudShadowStyle);
					GUI.Label(new Rect(num3, num6, 480f, num), _hudLines[j], _hudLabelStyle);
				}
			}
		}

		private static List<string> BuildLateJoinStatusLines()
		{
			int pendingLateJoinerCount = PendingLateJoinerCount;
			int num = ComputeCoopMaskForHud();
			float unscaledTime = Time.unscaledTime;
			if (pendingLateJoinerCount == _statusLinesLastEntryCount && num == _statusLinesLastCoopMask && unscaledTime < _statusLinesNextRefreshAt)
			{
				return _lateJoinStatusLines;
			}
			_statusLinesNextRefreshAt = unscaledTime + 0.25f;
			_statusLinesLastEntryCount = pendingLateJoinerCount;
			_statusLinesLastCoopMask = num;
			_lateJoinStatusLines.Clear();
			if (pendingLateJoinerCount == 0)
			{
				return _lateJoinStatusLines;
			}
			_hudEntriesScratch.Clear();
			foreach (LateJoinEntry lateJoinEntryValue in LateJoinEntryValues)
			{
				_hudEntriesScratch.Add(lateJoinEntryValue);
			}
			_hudEntriesScratch.Sort(_entrySortDesc);
			bool flag = UseChinese();
			for (int i = 0; i < _hudEntriesScratch.Count; i++)
			{
				LateJoinEntry lateJoinEntry = _hudEntriesScratch[i];
				string text;
				switch (lateJoinEntry.State)
				{
				case LateJoinState.Pending:
					text = (flag ? "等待连接" : "Connecting");
					break;
				case LateJoinState.Queued:
					text = (flag ? "排队中" : "Queued");
					break;
				case LateJoinState.Processing:
				case LateJoinState.Finalizing:
					text = (flag ? "加载中" : "Loading");
					break;
				case LateJoinState.Done:
					text = (flag ? "已中途加入" : "Late Joined");
					break;
				default:
					continue;
				}
				if (lateJoinEntry.State == LateJoinState.Done && IsCoopActorActive(lateJoinEntry.ActorNumber))
				{
					text += (flag ? "(优化)" : " (Optimized)");
				}
				_lateJoinStatusLines.Add(ResolveDisplayName(lateJoinEntry.ActorNumber, lateJoinEntry.NickName) + ": " + text);
			}
			return _lateJoinStatusLines;
		}

		private static int ComputeCoopMaskForHud()
		{
			int num = 0;
			foreach (LateJoinEntry lateJoinEntryValue in LateJoinEntryValues)
			{
				if (lateJoinEntryValue.State == LateJoinState.Done)
				{
					int num2 = (IsCoopActorActive(lateJoinEntryValue.ActorNumber) ? 1 : 0);
					num = num * 31 + (lateJoinEntryValue.ActorNumber << 1) + num2;
				}
			}
			return num;
		}

		private static LobbyTypes? GetLobbyTypeOrNull()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)GameManager.instance == (Object)null)
			{
				return null;
			}
			try
			{
				if (CompatibilityReport.GameManagerLobbyTypeRef != null)
				{
					return CompatibilityReport.GameManagerLobbyTypeRef.Invoke(GameManager.instance);
				}
				if (CompatibilityReport.GameManagerLobbyTypeField?.GetValue(GameManager.instance) is LobbyTypes value)
				{
					return value;
				}
			}
			catch
			{
			}
			return null;
		}

		private static string ReadServerName()
		{
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom == null)
				{
					return null;
				}
				string text = null;
				Hashtable customProperties = ((RoomInfo)currentRoom).CustomProperties;
				if (customProperties != null && ((Dictionary<object, object>)(object)customProperties).TryGetValue((object)"server_name", out object value))
				{
					text = value as string;
				}
				if (string.IsNullOrWhiteSpace(text))
				{
					text = currentRoom.Name;
				}
				return string.IsNullOrEmpty(text) ? null : text;
			}
			catch
			{
				return null;
			}
		}

		private static string GetRoomTypeText(LobbyTypes? lt, bool multi, bool cn, bool roomOpen)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected I4, but got Unknown
			if (!multi)
			{
				if (!cn)
				{
					return "Singleplayer";
				}
				return "单人模式";
			}
			string text;
			if (lt.HasValue)
			{
				LobbyTypes value = lt.Value;
				text = (int)value switch
				{
					1 => cn ? "公开服务器" : "Public", 
					0 => cn ? "私人服务器" : "Private", 
					2 => cn ? "匹配房间" : "Matchmaking", 
					_ => cn ? "未知" : "Unknown", 
				};
			}
			else
			{
				try
				{
					text = ((PhotonNetwork.CurrentRoom == null) ? (cn ? "未知" : "Unknown") : ((!PhotonNetwork.CurrentRoom.IsVisible) ? (cn ? "私人服务器" : "Private") : (cn ? "公开服务器" : "Public")));
				}
				catch
				{
					text = (cn ? "未知" : "Unknown");
				}
			}
			string text2 = ((!roomOpen) ? (cn ? "禁止加入" : "Closed") : (cn ? "可加入" : "Open"));
			return text + " - " + text2;
		}

		private static string GetRoomNameText(LobbyTypes? lt, string serverName, bool multi, bool cn)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			if (!multi)
			{
				if (!cn)
				{
					return "N/A";
				}
				return "无";
			}
			bool num;
			if (!lt.HasValue)
			{
				if (PhotonNetwork.CurrentRoom == null)
				{
					goto IL_004f;
				}
				num = !PhotonNetwork.CurrentRoom.IsVisible;
			}
			else
			{
				num = (int)lt.Value == 0;
			}
			if (num)
			{
				if (!cn)
				{
					return "Friends Only";
				}
				return "仅限好友加入";
			}
			goto IL_004f;
			IL_004f:
			if (string.IsNullOrEmpty(serverName))
			{
				if (!cn)
				{
					return "Unknown";
				}
				return "未知";
			}
			return serverName;
		}

		private static string GetPlayerCountText(int pc, int mp, bool multi, bool cn)
		{
			if (!multi)
			{
				return "1/1";
			}
			if (pc > 0)
			{
				return pc + "/" + ((mp > 0) ? mp : pc);
			}
			if (!cn)
			{
				return "Unknown";
			}
			return "未知";
		}

		private static FieldRef<PlayerAvatar, string> TryAvatarPlayerNameRef()
		{
			try
			{
				return (AccessTools.Field(typeof(PlayerAvatar), "playerName") != null) ? AccessTools.FieldRefAccess<PlayerAvatar, string>("playerName") : null;
			}
			catch
			{
				return null;
			}
		}

		internal static string ResolveDisplayName(int actorNumber, string fallback)
		{
			try
			{
				if (GameDirector.instance?.PlayerList != null)
				{
					foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
					{
						if (player == null)
						{
							continue;
						}
						PhotonView photonView = player.photonView;
						int? obj;
						if (photonView == null)
						{
							obj = null;
						}
						else
						{
							Player owner = photonView.Owner;
							obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null));
						}
						if (obj != actorNumber)
						{
							continue;
						}
						if (_avatarPlayerNameRef != null)
						{
							string text = _avatarPlayerNameRef.Invoke(player);
							if (!string.IsNullOrWhiteSpace(text))
							{
								return text.Trim();
							}
						}
						Player owner2 = player.photonView.Owner;
						string text2 = ((owner2 != null) ? owner2.NickName : null);
						if (!string.IsNullOrWhiteSpace(text2))
						{
							return text2.Trim();
						}
						break;
					}
				}
			}
			catch
			{
			}
			return fallback;
		}

		private static StateSyncEntry S(string type, string rpc, bool castToInt = true, string field = "currentState")
		{
			return new StateSyncEntry
			{
				TypeName = type,
				FieldName = field,
				RpcName = rpc,
				CastToInt = castToInt
			};
		}

		internal static IEnumerator SendStateSyncStreamed(Player target, LateJoinEntry lateE, PhotonView[] allPVs)
		{
			if (target == null)
			{
				yield break;
			}
			int sent = 0;
			int skipped = 0;
			int errors = 0;
			StateSyncEntry[] stateSyncEntries = _stateSyncEntries;
			for (int i = 0; i < stateSyncEntries.Length; i++)
			{
				ResolveEntryReflection(stateSyncEntries[i]);
			}
			if (allPVs == null)
			{
				try
				{
					allPVs = Object.FindObjectsOfType<PhotonView>();
				}
				catch
				{
				}
			}
			if (allPVs == null)
			{
				yield break;
			}
			if (_epIsLockedFieldInfo == null)
			{
				try
				{
					_epIsLockedFieldInfo = AccessTools.Field(typeof(ExtractionPoint), "isLocked");
				}
				catch
				{
				}
			}
			StateSyncEntry[] stateSyncEntries2 = _stateSyncEntries;
			foreach (StateSyncEntry stateSyncEntry in stateSyncEntries2)
			{
				if (stateSyncEntry.ResolvedType == null || stateSyncEntry.ResolvedField == null)
				{
					continue;
				}
				PhotonView[] array = allPVs;
				foreach (PhotonView val in array)
				{
					if ((Object)(object)val == (Object)null || val.ViewID == 0)
					{
						continue;
					}
					Component component = ((Component)val).GetComponent(stateSyncEntry.ResolvedType);
					MonoBehaviour val2 = (MonoBehaviour)(object)((component is MonoBehaviour) ? component : null);
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					try
					{
						object value = stateSyncEntry.ResolvedField.GetValue(val2);
						if (value == null)
						{
							skipped++;
							continue;
						}
						val.RPC(stateSyncEntry.RpcName, target, new object[1] { stateSyncEntry.CastToInt ? ((object)Convert.ToInt32(value)) : value });
						sent++;
						if (!(stateSyncEntry.ResolvedType == typeof(ExtractionPoint)) || !(_epIsLockedFieldInfo != null))
						{
							continue;
						}
						try
						{
							if ((bool)_epIsLockedFieldInfo.GetValue(val2))
							{
								object[] array2 = new object[2] { val.ViewID, true };
								RaiseEventOptions val3 = new RaiseEventOptions();
								val3.TargetActors = new int[1] { target.ActorNumber };
								PhotonNetwork.RaiseEvent((byte)171, (object)array2, val3, SendOptions.SendReliable);
							}
						}
						catch
						{
						}
					}
					catch (Exception ex)
					{
						errors++;
						Verbose("状态同步失败:" + ex.Message);
					}
				}
				yield return null;
				if (lateE.State == LateJoinState.Aborted || !IsTargetStillOnline(lateE.Target))
				{
					yield break;
				}
			}
			Verbose($"状态同步:sent={sent} skipped={skipped} errors={errors},分帧完成。");
			LogInfo(target.NickName + " 状态同步完成。");
		}

		private static void ResolveEntryReflection(StateSyncEntry entry)
		{
			if (entry.ResolveAttempted)
			{
				return;
			}
			entry.ResolveAttempted = true;
			try
			{
				Type type = typeof(PlayerAvatar).Assembly.GetType(entry.TypeName, throwOnError: false);
				if (!(type == null))
				{
					FieldInfo fieldInfo = AccessTools.Field(type, entry.FieldName);
					if (!(fieldInfo == null))
					{
						entry.ResolvedType = type;
						entry.ResolvedField = fieldInfo;
					}
				}
			}
			catch
			{
			}
		}

		internal static void ClearLocalCachesForClient()
		{
		}

		internal static bool TryGetLateJoinEntryByActor(int actor, out LateJoinEntry entry)
		{
			return _lateJoinEntries.TryGetValue(actor, out entry);
		}

		private static FieldRef<PlayerAvatar, bool> TryAvatarAnimCompletedRef()
		{
			try
			{
				return (AccessTools.Field(typeof(PlayerAvatar), "levelAnimationCompleted") != null) ? AccessTools.FieldRefAccess<PlayerAvatar, bool>("levelAnimationCompleted") : null;
			}
			catch
			{
				return null;
			}
		}

		private static FieldRef<MenuPageLobby, List<string>> TryMenuLobbyListRef(string name)
		{
			try
			{
				return (AccessTools.Field(typeof(MenuPageLobby), name) != null) ? AccessTools.FieldRefAccess<MenuPageLobby, List<string>>(name) : null;
			}
			catch
			{
				return null;
			}
		}

		private static FieldRef<MenuPageLobby, float> TryMenuLobbyFloatRef(string name)
		{
			try
			{
				return (AccessTools.Field(typeof(MenuPageLobby), name) != null) ? AccessTools.FieldRefAccess<MenuPageLobby, float>(name) : null;
			}
			catch
			{
				return null;
			}
		}

		internal static void NetworkManagerOnPlayerEnteredRoomPostfix(Player p)
		{
			if (p != null)
			{
				UpdateCoopStateFor(p);
			}
			if (IsHookActive() && PhotonNetwork.IsMasterClient && p != null && PhotonNetwork.CurrentRoom != null && IsSnapshotNeededForCurrentScene())
			{
				_lateJoinEntries[p.ActorNumber] = new LateJoinEntry
				{
					Target = p,
					ActorNumber = p.ActorNumber,
					NickName = p.NickName,
					State = LateJoinState.Pending,
					StartScene = CurrentSceneName()
				};
				LogInfo(p.NickName + " 中途加入,等待同步。");
			}
		}

		internal static void NetworkManagerOnPlayerLeftRoomPostfix(Player p)
		{
			if (p == null)
			{
				return;
			}
			RemoveCoopActor(p.ActorNumber);
			if (_lateJoinEntries.TryGetValue(p.ActorNumber, out var value))
			{
				value.State = LateJoinState.Aborted;
				Verbose(p.NickName + " 离开房间,同步取消。");
			}
			try
			{
				if ((Object)(object)MenuPageLobby.instance != (Object)null && _menuLobbyJoiningPlayersRef != null)
				{
					List<string> list = _menuLobbyJoiningPlayersRef.Invoke(MenuPageLobby.instance);
					if (list != null && list.Count > 0 && list.Remove(p.NickName))
					{
						if (_menuLobbyJoiningTimerRef != null)
						{
							_menuLobbyJoiningTimerRef.Invoke(MenuPageLobby.instance) = 0f;
						}
						if (_menuLobbyJoiningEndTimerRef != null)
						{
							_menuLobbyJoiningEndTimerRef.Invoke(MenuPageLobby.instance) = 0f;
						}
						Verbose(p.NickName + " 离开,已清理大厅按钮锁定状态。");
					}
				}
			}
			catch
			{
			}
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				if (GameDirector.instance?.PlayerList == null)
				{
					return;
				}
				foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
				{
					int? obj2;
					if (player == null)
					{
						obj2 = null;
					}
					else
					{
						PhotonView photonView = player.photonView;
						if (photonView == null)
						{
							obj2 = null;
						}
						else
						{
							Player owner = photonView.Owner;
							obj2 = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null));
						}
					}
					if (obj2 != p.ActorNumber)
					{
						continue;
					}
					PlayerCosmetics componentInChildren = ((Component)player).GetComponentInChildren<PlayerCosmetics>();
					if (!((Object)(object)componentInChildren == (Object)null))
					{
						PhotonView component = ((Component)componentInChildren).GetComponent<PhotonView>();
						if (!((Object)(object)component == (Object)null))
						{
							PhotonNetwork.RemoveBufferedRPCs(component.ViewID, (string)null, (int[])null);
							Verbose(p.NickName + " 离开,已清理外观缓存。");
							break;
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				TakeoverOrphanedPhysGrabObjects(p.ActorNumber);
			}
			catch (Exception ex)
			{
				Verbose("接管孤儿物品失败:" + ex.Message);
			}
		}

		private static void TakeoverOrphanedPhysGrabObjects(int leftActor)
		{
			int num = 0;
			PhysGrabObject[] array = null;
			try
			{
				array = Object.FindObjectsOfType<PhysGrabObject>();
			}
			catch
			{
			}
			if (array == null)
			{
				return;
			}
			PhysGrabObject[] array2 = array;
			foreach (PhysGrabObject val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				PhotonView component = ((Component)val).GetComponent<PhotonView>();
				if (!((Object)(object)component == (Object)null) && component.ViewID != 0 && (component.Owner == null || component.Owner.ActorNumber == leftActor))
				{
					try
					{
						component.TransferOwnership(PhotonNetwork.LocalPlayer);
						num++;
					}
					catch
					{
					}
				}
			}
			if (num > 0)
			{
				Verbose($"已接管 {num} 个孤儿物品到主机。");
			}
		}

		internal static void NetworkManagerPlayerSpawnedRPCPostfix(PhotonMessageInfo info)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!IsHookActive() || !PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null)
			{
				return;
			}
			Player sender = info.Sender;
			if (sender == null || sender.ActorNumber == PhotonNetwork.LocalPlayer.ActorNumber || !_lateJoinEntries.TryGetValue(sender.ActorNumber, out var value) || value.State != LateJoinState.Pending)
			{
				return;
			}
			if (!IsSnapshotNeededForCurrentScene())
			{
				value.State = LateJoinState.Aborted;
				return;
			}
			value.State = LateJoinState.Queued;
			value.Target = sender;
			_lateJoinQueue.Enqueue(sender.ActorNumber);
			LogInfo($"{sender.NickName} 开始排队同步(队列 {_lateJoinQueue.Count} 人)。");
			if (!_schedulerRunning && (Object)(object)Instance != (Object)null)
			{
				_schedulerRunning = true;
				((MonoBehaviour)Instance).StartCoroutine(LateJoinSchedulerCoroutine());
			}
		}

		private static IEnumerator LateJoinSchedulerCoroutine()
		{
			try
			{
				while (_lateJoinQueue.Count > 0)
				{
					if (!IsStaticModEnabled() || !PhotonNetwork.IsMasterClient)
					{
						_lateJoinQueue.Clear();
						break;
					}
					int key = _lateJoinQueue.Dequeue();
					if (_lateJoinEntries.TryGetValue(key, out var value) && value.State != LateJoinState.Aborted)
					{
						if (!IsTargetStillOnline(value.Target) || CurrentSceneName() != value.StartScene)
						{
							value.State = LateJoinState.Aborted;
							continue;
						}
						value.State = LateJoinState.Processing;
						LogInfo("正在同步 " + value.NickName + "。");
						((MonoBehaviour)Instance).StartCoroutine(RunSingleJoinerPipeline(value));
						yield return null;
					}
				}
			}
			finally
			{
				_schedulerRunning = false;
			}
		}

		private static IEnumerator RunSingleJoinerPipeline(LateJoinEntry e)
		{
			yield return ((MonoBehaviour)Instance).StartCoroutine(EnsurePlayerCorpseObjectsAndSnapshot(e));
			if (e.State != LateJoinState.Done && e.State != LateJoinState.Finalizing)
			{
				yield break;
			}
			while (true)
			{
				if (_refreshOldPlayersBusy)
				{
					if (IsTargetStillOnline(e.Target) && !(CurrentSceneName() != e.StartScene))
					{
						yield return null;
						continue;
					}
					break;
				}
				_refreshOldPlayersBusy = true;
				try
				{
					yield return ((MonoBehaviour)Instance).StartCoroutine(RefreshOldPlayersForNewJoiner(e.Target, e.StartScene));
					break;
				}
				finally
				{
					_refreshOldPlayersBusy = false;
				}
			}
		}

		private static IEnumerator RefreshOldPlayersForNewJoiner(Player newPlayer, string startScene)
		{
			if (_ownershipUpdate == null)
			{
				yield break;
			}
			int hostActor = PhotonNetwork.LocalPlayer.ActorNumber;
			int hostVoiceViewID = 0;
			PlayerAvatar hostAvatar = null;
			int newPlayerActor = newPlayer.ActorNumber;
			int newPlayerVoiceViewID = 0;
			PlayerAvatar newPlayerAvatar = null;
			try
			{
				if (GameDirector.instance?.PlayerList != null)
				{
					foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
					{
						if (!((Object)(object)player?.photonView == (Object)null) && player.photonView.IsMine)
						{
							hostAvatar = player;
							break;
						}
					}
				}
				if ((Object)(object)hostAvatar != (Object)null && CurrentSceneName() == startScene)
				{
					List<PlayerVoiceChat> list = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null);
					if (list != null)
					{
						foreach (PlayerVoiceChat item in list)
						{
							PhotonView val = ((item != null) ? ((Component)item).GetComponent<PhotonView>() : null);
							int num;
							if (val == null)
							{
								num = 1;
							}
							else
							{
								Player owner = val.Owner;
								num = ((((owner != null) ? new int?(owner.ActorNumber) : ((int?)null)) != hostActor) ? 1 : 0);
							}
							if (num == 0)
							{
								hostVoiceViewID = val.ViewID;
								try
								{
									hostAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", newPlayer, new object[1] { val.ViewID });
								}
								catch
								{
								}
								break;
							}
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				if (GameDirector.instance?.PlayerList != null)
				{
					foreach (PlayerAvatar player2 in GameDirector.instance.PlayerList)
					{
						if (!((Object)(object)player2?.photonView == (Object)null))
						{
							Player owner2 = player2.photonView.Owner;
							if (owner2 != null && owner2.ActorNumber == newPlayerActor)
							{
								newPlayerAvatar = player2;
								break;
							}
						}
					}
				}
				if ((Object)(object)newPlayerAvatar != (Object)null && CurrentSceneName() == startScene)
				{
					List<PlayerVoiceChat> list2 = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null);
					if (list2 != null)
					{
						foreach (PlayerVoiceChat item2 in list2)
						{
							PhotonView val2 = ((item2 != null) ? ((Component)item2).GetComponent<PhotonView>() : null);
							int num2;
							if (val2 == null)
							{
								num2 = 1;
							}
							else
							{
								Player owner3 = val2.Owner;
								num2 = ((((owner3 != null) ? new int?(owner3.ActorNumber) : ((int?)null)) != newPlayerActor) ? 1 : 0);
							}
							if (num2 == 0)
							{
								newPlayerVoiceViewID = val2.ViewID;
								try
								{
									newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", PhotonNetwork.LocalPlayer, new object[1] { val2.ViewID });
								}
								catch
								{
								}
								break;
							}
						}
					}
				}
			}
			catch
			{
			}
			yield return null;
			int master = PhotonNetwork.LocalPlayer.ActorNumber;
			bool isCoop = IsCoopActorActive(newPlayer.ActorNumber);
			int maxAttempts = (isCoop ? 1 : 3);
			for (int attempt = 0; attempt < maxAttempts; attempt++)
			{
				if (newPlayerVoiceViewID == 0 || (Object)(object)newPlayerAvatar == (Object)null)
				{
					try
					{
						if ((Object)(object)newPlayerAvatar == (Object)null && GameDirector.instance?.PlayerList != null)
						{
							foreach (PlayerAvatar player3 in GameDirector.instance.PlayerList)
							{
								if (player3 != null)
								{
									PhotonView photonView = player3.photonView;
									int? obj5;
									if (photonView == null)
									{
										obj5 = null;
									}
									else
									{
										Player owner4 = photonView.Owner;
										obj5 = ((owner4 != null) ? new int?(owner4.ActorNumber) : ((int?)null));
									}
									if (obj5 == newPlayerActor)
									{
										newPlayerAvatar = player3;
										break;
									}
								}
							}
						}
						if ((Object)(object)newPlayerAvatar != (Object)null && newPlayerVoiceViewID == 0)
						{
							List<PlayerVoiceChat> list3 = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null);
							if (list3 != null)
							{
								foreach (PlayerVoiceChat item3 in list3)
								{
									PhotonView val3 = ((item3 != null) ? ((Component)item3).GetComponent<PhotonView>() : null);
									int num3;
									if (val3 == null)
									{
										num3 = 1;
									}
									else
									{
										Player owner5 = val3.Owner;
										num3 = ((((owner5 != null) ? new int?(owner5.ActorNumber) : ((int?)null)) != newPlayerActor) ? 1 : 0);
									}
									if (num3 == 0)
									{
										newPlayerVoiceViewID = val3.ViewID;
										try
										{
											newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", PhotonNetwork.LocalPlayer, new object[1] { val3.ViewID });
										}
										catch
										{
										}
										break;
									}
								}
							}
						}
					}
					catch
					{
					}
				}
				if (attempt > 0)
				{
					yield return _wait25;
					if (hostVoiceViewID != 0 && (Object)(object)hostAvatar != (Object)null && CurrentSceneName() == startScene)
					{
						try
						{
							hostAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", newPlayer, new object[1] { hostVoiceViewID });
						}
						catch
						{
						}
					}
					if (newPlayerVoiceViewID != 0 && (Object)(object)newPlayerAvatar != (Object)null && CurrentSceneName() == startScene)
					{
						try
						{
							newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", PhotonNetwork.LocalPlayer, new object[1] { newPlayerVoiceViewID });
						}
						catch
						{
						}
					}
				}
				if (!IsTargetStillOnline(newPlayer) || CurrentSceneName() != startScene)
				{
					yield break;
				}
				_oldAvatarsScratch.Clear();
				if (GameDirector.instance?.PlayerList != null)
				{
					foreach (PlayerAvatar player4 in GameDirector.instance.PlayerList)
					{
						object obj10;
						if (player4 == null)
						{
							obj10 = null;
						}
						else
						{
							PhotonView photonView2 = player4.photonView;
							obj10 = ((photonView2 != null) ? photonView2.Owner : null);
						}
						if (obj10 != null && player4.photonView.Owner.ActorNumber != newPlayer.ActorNumber && !player4.photonView.IsMine)
						{
							int actorNumber = player4.photonView.Owner.ActorNumber;
							if (!_lateJoinEntries.TryGetValue(actorNumber, out var value) || value.State != LateJoinState.Aborted)
							{
								_oldAvatarsScratch.Add((player4, actorNumber));
							}
						}
					}
				}
				if (_oldAvatarsScratch.Count == 0)
				{
					continue;
				}
				for (int i = 0; i < _oldAvatarsScratch.Count; i++)
				{
					var (av, origActor) = _oldAvatarsScratch[i];
					if ((Object)(object)av?.photonView == (Object)null || CurrentSceneName() != startScene)
					{
						yield break;
					}
					Player origPlayer = av.photonView.Owner;
					if (origPlayer == null)
					{
						continue;
					}
					int vid = av.photonView.ViewID;
					int voiceViewID = 0;
					try
					{
						List<PlayerVoiceChat> list4 = ((_runManagerVoiceChatsRef != null && (Object)(object)RunManager.instance != (Object)null) ? _runManagerVoiceChatsRef.Invoke(RunManager.instance) : null);
						if (list4 != null)
						{
							foreach (PlayerVoiceChat item4 in list4)
							{
								PhotonView val4 = ((item4 != null) ? ((Component)item4).GetComponent<PhotonView>() : null);
								int num4;
								if (val4 == null)
								{
									num4 = 1;
								}
								else
								{
									Player owner6 = val4.Owner;
									num4 = ((((owner6 != null) ? new int?(owner6.ActorNumber) : ((int?)null)) != origActor) ? 1 : 0);
								}
								if (num4 == 0)
								{
									voiceViewID = val4.ViewID;
									break;
								}
							}
						}
					}
					catch
					{
					}
					if (voiceViewID == 0)
					{
						continue;
					}
					if (!isCoop && attempt == 0)
					{
						if (hostVoiceViewID != 0)
						{
							try
							{
								_ownershipScratch[0] = hostVoiceViewID;
								_ownershipScratch[1] = 999;
								_ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber);
							}
							catch
							{
							}
							yield return null;
						}
						try
						{
							Hashtable val5 = new Hashtable();
							val5[(byte)0] = "Voice";
							val5[(byte)1] = Vector3.zero;
							val5[(byte)2] = Quaternion.identity;
							val5[(byte)3] = (byte)0;
							val5[(byte)4] = new int[1] { voiceViewID };
							val5[(byte)5] = null;
							val5[(byte)6] = PhotonNetwork.ServerTimestamp;
							val5[(byte)7] = voiceViewID;
							val5[(byte)8] = (byte)0;
							RaiseEventOptions val6 = new RaiseEventOptions();
							val6.TargetActors = new int[1] { newPlayer.ActorNumber };
							RaiseEventOptions val7 = val6;
							PhotonNetwork.RaiseEvent((byte)202, (object)val5, val7, SendOptions.SendReliable);
						}
						catch
						{
						}
						yield return null;
						try
						{
							_ownershipScratch[0] = voiceViewID;
							_ownershipScratch[1] = origActor;
							_ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber);
						}
						catch
						{
						}
						yield return null;
						if (hostVoiceViewID != 0)
						{
							try
							{
								_ownershipScratch[0] = hostVoiceViewID;
								_ownershipScratch[1] = hostActor;
								_ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber);
							}
							catch
							{
							}
							yield return null;
						}
					}
					try
					{
						_ownershipScratch[0] = vid;
						_ownershipScratch[1] = master;
						_ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber);
					}
					catch
					{
						continue;
					}
					yield return null;
					try
					{
						av.photonView.RPC("LoadingLevelAnimationCompletedRPC", newPlayer, Array.Empty<object>());
					}
					catch
					{
					}
					try
					{
						av.photonView.RPC("UpdateMyPlayerVoiceChat", newPlayer, new object[1] { voiceViewID });
					}
					catch
					{
					}
					if (!isCoop)
					{
						yield return _wait15;
					}
					else
					{
						yield return null;
					}
					try
					{
						_ownershipScratch[0] = vid;
						_ownershipScratch[1] = origActor;
						_ownershipUpdate(_ownershipScratch, newPlayer.ActorNumber);
					}
					catch
					{
					}
					yield return null;
					if (newPlayerVoiceViewID != 0 && (Object)(object)newPlayerAvatar != (Object)null)
					{
						try
						{
							_ownershipScratch[0] = newPlayerAvatar.photonView.ViewID;
							_ownershipScratch[1] = master;
							_ownershipUpdate(_ownershipScratch, origActor);
						}
						catch
						{
							continue;
						}
						yield return null;
						try
						{
							newPlayerAvatar.photonView.RPC("UpdateMyPlayerVoiceChat", origPlayer, new object[1] { newPlayerVoiceViewID });
						}
						catch
						{
						}
						if (!isCoop)
						{
							yield return _wait15;
						}
						else
						{
							yield return null;
						}
						try
						{
							_ownershipScratch[0] = newPlayerAvatar.photonView.ViewID;
							_ownershipScratch[1] = newPlayerActor;
							_ownershipUpdate(_ownershipScratch, origActor);
						}
						catch
						{
						}
						yield return null;
					}
				}
			}
			if (!isCoop)
			{
				yield return _wait05;
			}
			if (CurrentSceneName() == startScene)
			{
				BroadcastLoadingAnimationCompletedOnce(newPlayer);
			}
		}

		internal static void ClearLateJoinQueue()
		{
			int count = _lateJoinQueue.Count;
			int count2 = _lateJoinEntries.Count;
			foreach (LateJoinEntry value in _lateJoinEntries.Values)
			{
				value.State = LateJoinState.Aborted;
			}
			_lateJoinQueue.Clear();
			_lateJoinEntries.Clear();
			_coopReadyByActor.Clear();
			_coopModuleCountByActor.Clear();
			if (count > 0 || count2 > 0)
			{
				Verbose($"切关清空同步队列:{count} 待处理,{count2} 条目。");
			}
		}

		private static IEnumerator EnsurePlayerCorpseObjectsAndSnapshot(LateJoinEntry e)
		{
			yield return null;
			if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target))
			{
				e.State = LateJoinState.Aborted;
				yield break;
			}
			EnsurePlayerCorpseObjects(e.Target);
			yield return ((MonoBehaviour)Instance).StartCoroutine(SendCatchupSnapshotStaged(e));
		}

		private static IEnumerator SendCatchupSnapshotStaged(LateJoinEntry e)
		{
			Player t = e.Target;
			Verbose("开始同步 " + e.NickName + "。");
			float avatarDeadline = Time.unscaledTime + 3f;
			while (Time.unscaledTime < avatarDeadline)
			{
				if (ShouldAbortSnapshot(e, "wait_avatar"))
				{
					yield break;
				}
				bool flag = false;
				if (GameDirector.instance?.PlayerList != null)
				{
					foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
					{
						int? obj;
						if (player == null)
						{
							obj = null;
						}
						else
						{
							PhotonView photonView = player.photonView;
							if (photonView == null)
							{
								obj = null;
							}
							else
							{
								Player owner = photonView.Owner;
								obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null));
							}
						}
						if (obj == e.Target.ActorNumber)
						{
							flag = true;
							break;
						}
					}
				}
				if (flag)
				{
					break;
				}
				yield return null;
			}
			if (ShouldAbortSnapshot(e, "1"))
			{
				yield break;
			}
			try
			{
				NetworkManager instance = NetworkManager.instance;
				if (instance != null)
				{
					PhotonView photonView2 = ((MonoBehaviourPun)instance).photonView;
					if (photonView2 != null)
					{
						photonView2.RPC("AllPlayerSpawnedRPC", t, Array.Empty<object>());
					}
				}
			}
			catch
			{
			}
			yield return null;
			if (ShouldAbortSnapshot(e, "2"))
			{
				yield break;
			}
			yield return ((MonoBehaviour)Instance).StartCoroutine(WaitHostGenerated(e));
			if (ShouldAbortSnapshot(e, "3"))
			{
				yield break;
			}
			Module[] allModules = null;
			PhotonView[] allPVs = null;
			ItemAttributes[] items = null;
			try
			{
				allModules = Object.FindObjectsOfType<Module>();
			}
			catch
			{
			}
			try
			{
				allPVs = Object.FindObjectsOfType<PhotonView>();
			}
			catch
			{
			}
			try
			{
				items = Object.FindObjectsOfType<ItemAttributes>();
			}
			catch
			{
			}
			int _syncDone = 0;
			Action onDone = delegate
			{
				_syncDone++;
			};
			((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendModuleSyncStreamed(t, e, allModules), onDone));
			((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendValuableSyncStreamed(t, e), onDone));
			((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendItemAttributesSyncStreamed(t, e, items), onDone));
			((MonoBehaviour)Instance).StartCoroutine(RunAndNotify(SendStateSyncStreamed(t, e, allPVs), onDone));
			float syncDeadline = Time.unscaledTime + 10f;
			while (_syncDone < 4 && Time.unscaledTime < syncDeadline)
			{
				if (ShouldAbortSnapshot(e, "3_parallel"))
				{
					yield break;
				}
				yield return null;
			}
			if (ShouldAbortSnapshot(e, "3_done"))
			{
				yield break;
			}
			try
			{
				LevelGenerator instance2 = LevelGenerator.Instance;
				if (instance2 != null)
				{
					PhotonView photonView3 = instance2.PhotonView;
					if (photonView3 != null)
					{
						photonView3.RPC("GenerateDone", t, Array.Empty<object>());
					}
				}
			}
			catch
			{
			}
			if (IsCoopActorActive(e.Target.ActorNumber))
			{
				ResetCoopReadyFor(e.Target.ActorNumber);
				yield return ((MonoBehaviour)Instance).StartCoroutine(WaitClientReadyOrTimeout(e, 8f));
				if (!ShouldAbortSnapshot(e, "4_coop"))
				{
					BroadcastLoadingAnimationCompletedOnce(t);
					e.State = LateJoinState.Finalizing;
					((MonoBehaviour)Instance).StartCoroutine(BackgroundStateSyncCoroutine(e, allModules));
				}
				yield break;
			}
			float nonCoopDeadline = Time.unscaledTime + 6f;
			while (Time.unscaledTime < nonCoopDeadline)
			{
				if (ShouldAbortSnapshot(e, "4_wait"))
				{
					yield break;
				}
				bool flag2 = false;
				if (_avatarAnimCompleted != null && GameDirector.instance?.PlayerList != null)
				{
					foreach (PlayerAvatar player2 in GameDirector.instance.PlayerList)
					{
						int? obj7;
						if (player2 == null)
						{
							obj7 = null;
						}
						else
						{
							PhotonView photonView4 = player2.photonView;
							if (photonView4 == null)
							{
								obj7 = null;
							}
							else
							{
								Player owner2 = photonView4.Owner;
								obj7 = ((owner2 != null) ? new int?(owner2.ActorNumber) : ((int?)null));
							}
						}
						if (obj7 != t.ActorNumber)
						{
							continue;
						}
						try
						{
							if (_avatarAnimCompleted.Invoke(player2))
							{
								flag2 = true;
							}
						}
						catch
						{
						}
						break;
					}
				}
				if (flag2)
				{
					break;
				}
				yield return null;
			}
			if (!ShouldAbortSnapshot(e, "4"))
			{
				BroadcastLoadingAnimationCompletedOnce(t);
				e.State = LateJoinState.Finalizing;
				((MonoBehaviour)Instance).StartCoroutine(BackgroundStateSyncCoroutine(e, allModules));
				((MonoBehaviour)Instance).StartCoroutine(LateRetryAnimationComplete(t));
			}
		}

		private static IEnumerator LateRetryAnimationComplete(Player target)
		{
			yield return _wait15;
			if (target == null || PhotonNetwork.CurrentRoom == null || !PhotonNetwork.CurrentRoom.Players.ContainsKey(target.ActorNumber))
			{
				yield break;
			}
			try
			{
				BroadcastLoadingAnimationCompletedOnce(target);
			}
			catch
			{
			}
		}

		private static IEnumerator BackgroundStateSyncCoroutine(LateJoinEntry e, Module[] modules)
		{
			Player t = e.Target;
			if (IsCoopActorActive(e.Target.ActorNumber))
			{
				int num = ((modules != null) ? modules.Length : 0);
				int coopActorModuleCount = GetCoopActorModuleCount(e.Target.ActorNumber);
				if (num > 0 && coopActorModuleCount >= num)
				{
					Verbose($"客户端 Module 已齐({coopActorModuleCount}/{num}),跳过墙体兜底重发。");
				}
				else
				{
					yield return _wait2;
					if (ShouldAbortSnapshot(e, "bg_coop"))
					{
						yield break;
					}
					try
					{
						SendModuleSyncToLateJoiner(t, modules);
					}
					catch (Exception ex)
					{
						Verbose("地图同步失败:" + ex.Message);
					}
				}
				if (e.State == LateJoinState.Finalizing)
				{
					e.State = LateJoinState.Done;
					LogInfo(e.NickName + " 同步完成,已进入游戏。");
				}
				yield break;
			}
			yield return _wait2;
			if (ShouldAbortSnapshot(e, "bg_r1"))
			{
				yield break;
			}
			try
			{
				SendModuleSyncToLateJoiner(t, modules);
			}
			catch (Exception ex2)
			{
				Verbose("地图同步失败:" + ex2.Message);
			}
			if (e.State == LateJoinState.Finalizing)
			{
				e.State = LateJoinState.Done;
				LogInfo(e.NickName + " 同步完成,已进入游戏。");
			}
			yield return _wait2;
			if (!ShouldAbortSnapshot(e, "bg_r2"))
			{
				try
				{
					SendModuleSyncToLateJoiner(t, modules);
				}
				catch (Exception ex3)
				{
					Verbose("地图同步失败:" + ex3.Message);
				}
			}
		}

		private static bool IsTargetStillOnline(Player p)
		{
			if (p != null && PhotonNetwork.CurrentRoom != null)
			{
				return PhotonNetwork.CurrentRoom.Players.ContainsKey(p.ActorNumber);
			}
			return false;
		}

		private static string CurrentSceneName()
		{
			RunManager instance = RunManager.instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				Level levelCurrent = instance.levelCurrent;
				obj = ((levelCurrent != null) ? ((Object)levelCurrent).name : null);
			}
			if (obj == null)
			{
				obj = "<null>";
			}
			return (string)obj;
		}

		private static bool ShouldAbortSnapshot(LateJoinEntry e, string stage)
		{
			if (!IsStaticModEnabled() || !PhotonNetwork.IsMasterClient)
			{
				e.State = LateJoinState.Aborted;
				return true;
			}
			if (e.State == LateJoinState.Aborted)
			{
				return true;
			}
			if (e.State == LateJoinState.Done || e.State == LateJoinState.Finalizing)
			{
				return false;
			}
			if (!IsTargetStillOnline(e.Target))
			{
				e.State = LateJoinState.Aborted;
				Verbose(e.NickName + " 已离线,取消同步。");
				return true;
			}
			if (CurrentSceneName() != e.StartScene)
			{
				e.State = LateJoinState.Aborted;
				Verbose(e.NickName + " 场景已切换,取消同步。");
				return true;
			}
			return false;
		}

		private static bool IsSnapshotNeededForCurrentScene()
		{
			try
			{
				return (Object)(object)RunManager.instance != (Object)null && (SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel());
			}
			catch
			{
				return false;
			}
		}

		private static IEnumerator WaitClientReadyOrTimeout(LateJoinEntry e, float timeoutSeconds)
		{
			float deadline = Time.unscaledTime + timeoutSeconds;
			float started = Time.unscaledTime;
			while (Time.unscaledTime < deadline)
			{
				if (!IsTargetStillOnline(e.Target) || e.State == LateJoinState.Aborted)
				{
					yield break;
				}
				if (!IsCoopActorActive(e.Target.ActorNumber))
				{
					Verbose("协同状态消失(" + e.NickName + "),中止 ack 等待走非协同 fallback");
					yield break;
				}
				if (IsCoopActorReady(e.Target.ActorNumber))
				{
					Verbose($"协同就绪:{e.NickName} 用时 {Time.unscaledTime - started:F2}s");
					yield break;
				}
				yield return null;
			}
			Verbose($"协同就绪等待超时({timeoutSeconds}s),按原流程继续。");
		}

		private static IEnumerator WaitHostGenerated(LateJoinEntry e)
		{
			float deadline = Time.unscaledTime + 30f;
			if (LevelGenerator.Instance?.Generated ?? false)
			{
				yield break;
			}
			while (Time.unscaledTime < deadline)
			{
				if (!IsTargetStillOnline(e.Target) || e.State == LateJoinState.Aborted || (LevelGenerator.Instance?.Generated ?? false))
				{
					yield break;
				}
				yield return null;
			}
			Verbose("等待关卡生成超时。");
		}

		private static IEnumerator RunAndNotify(IEnumerator coroutine, Action onDone)
		{
			yield return ((MonoBehaviour)Instance).StartCoroutine(coroutine);
			onDone?.Invoke();
		}

		private static void EnsurePlayerCorpseObjects(Player target)
		{
			if (!PhotonNetwork.IsMasterClient || target == null)
			{
				return;
			}
			try
			{
				LevelGenerator instance = LevelGenerator.Instance;
				if ((Object)(object)instance?.PlayerDeathHeadPrefab == (Object)null || (Object)(object)instance.PlayerTumblePrefab == (Object)null || GameDirector.instance?.PlayerList == null || SemiFunc.RunIsLobby())
				{
					return;
				}
				PlayerAvatar av = null;
				foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
				{
					int? obj;
					if (player == null)
					{
						obj = null;
					}
					else
					{
						PhotonView photonView = player.photonView;
						if (photonView == null)
						{
							obj = null;
						}
						else
						{
							Player owner = photonView.Owner;
							obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null));
						}
					}
					if (obj == target.ActorNumber)
					{
						av = player;
						break;
					}
				}
				if (!((Object)(object)av == (Object)null))
				{
					SpawnCorpseComponent(((Object)instance.PlayerDeathHeadPrefab).name, av, delegate(GameObject go)
					{
						PlayerDeathHead component = go.GetComponent<PlayerDeathHead>();
						component.playerAvatar = av;
						_playerDeathHeadRef.Invoke(av) = component;
					}, (Object)(object)_playerDeathHeadRef.Invoke(av) == (Object)null);
					SpawnCorpseComponent(((Object)instance.PlayerTumblePrefab).name, av, delegate(GameObject go)
					{
						PlayerTumble component = go.GetComponent<PlayerTumble>();
						component.playerAvatar = av;
						_playerTumbleRef.Invoke(av) = component;
					}, (Object)(object)_playerTumbleRef.Invoke(av) == (Object)null);
				}
			}
			catch (Exception ex)
			{
				Verbose("补创组件失败:" + ex.Message);
			}
		}

		private static void SpawnCorpseComponent(string prefabName, PlayerAvatar av, Action<GameObject> setup, bool needed)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (needed)
			{
				GameObject obj = PhotonNetwork.Instantiate(prefabName, new Vector3(0f, 3000f, 0f), Quaternion.identity, (byte)0, (object[])null);
				setup(obj);
			}
		}

		private static void SendModuleSyncToLateJoiner(Player target, Module[] modules = null)
		{
			if (_modTop == null || _modBottom == null || _modRight == null || _modLeft == null || _modFirst == null || _modDone == null)
			{
				return;
			}
			if (modules == null)
			{
				try
				{
					modules = Object.FindObjectsOfType<Module>();
				}
				catch
				{
				}
			}
			if (modules == null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			Module[] array = modules;
			foreach (Module val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				PhotonView component = ((Component)val).GetComponent<PhotonView>();
				if ((Object)(object)component == (Object)null || component.ViewID == 0 || !_modDone.Invoke(val))
				{
					num2++;
					continue;
				}
				try
				{
					component.RPC("ModuleConnectionSetRPC", target, new object[5]
					{
						_modTop.Invoke(val),
						_modBottom.Invoke(val),
						_modRight.Invoke(val),
						_modLeft.Invoke(val),
						_modFirst.Invoke(val)
					});
					num++;
				}
				catch
				{
					num2++;
				}
			}
			Verbose($"地图同步:{num} 个(跳过 {num2})。");
		}

		private static IEnumerator SendModuleSyncStreamed(Player target, LateJoinEntry e, Module[] modules)
		{
			if (_modTop == null || _modBottom == null || _modRight == null || _modLeft == null || _modFirst == null || _modDone == null || modules == null || modules.Length == 0)
			{
				yield break;
			}
			LevelGenerator instance = LevelGenerator.Instance;
			if (instance == null || !instance.Generated)
			{
				float setupDeadline = Time.unscaledTime + 5f;
				while (Time.unscaledTime < setupDeadline)
				{
					bool flag = false;
					foreach (Module val in modules)
					{
						if ((Object)(object)val != (Object)null && !_modDone.Invoke(val))
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						break;
					}
					yield return null;
					if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target))
					{
						yield break;
					}
				}
			}
			int sent = 0;
			int skip = 0;
			int batched = 0;
			foreach (Module val2 in modules)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				PhotonView component = ((Component)val2).GetComponent<PhotonView>();
				if ((Object)(object)component == (Object)null || component.ViewID == 0 || !_modDone.Invoke(val2))
				{
					skip++;
					continue;
				}
				try
				{
					component.RPC("ModuleConnectionSetRPC", target, new object[5]
					{
						_modTop.Invoke(val2),
						_modBottom.Invoke(val2),
						_modRight.Invoke(val2),
						_modLeft.Invoke(val2),
						_modFirst.Invoke(val2)
					});
					sent++;
				}
				catch
				{
					skip++;
				}
				batched++;
				if (batched >= 8)
				{
					batched = 0;
					yield return null;
					if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target))
					{
						yield break;
					}
				}
			}
			Verbose($"地图同步:{sent} 个(跳过 {skip}),分帧完成。");
		}

		private static IEnumerator SendValuableSyncStreamed(Player target, LateJoinEntry e)
		{
			if (_valSet == null || _valCurrent == null || _valDisc == null || _valHaul == null || ValuableDirector.instance?.valuableList == null)
			{
				yield break;
			}
			int vs = 0;
			int ds = 0;
			int hs = 0;
			int batched = 0;
			foreach (ValuableObject valuable in ValuableDirector.instance.valuableList)
			{
				if ((Object)(object)valuable == (Object)null)
				{
					continue;
				}
				PhotonView component = ((Component)valuable).GetComponent<PhotonView>();
				if ((Object)(object)component == (Object)null || component.ViewID == 0)
				{
					continue;
				}
				try
				{
					if (_valSet.Invoke(valuable))
					{
						component.RPC("DollarValueSetRPC", target, new object[1] { _valCurrent.Invoke(valuable) });
						vs++;
					}
					if (_valDisc.Invoke(valuable))
					{
						component.RPC("DiscoverRPC", target, Array.Empty<object>());
						ds++;
					}
					if (_valHaul.Invoke(valuable))
					{
						component.RPC("AddToDollarHaulListRPC", target, Array.Empty<object>());
						hs++;
					}
				}
				catch
				{
				}
				batched++;
				if (batched >= 6)
				{
					batched = 0;
					yield return null;
					if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target))
					{
						yield break;
					}
				}
			}
			Verbose($"物品同步:价值 {vs},发现 {ds},出口 {hs},分帧完成。");
		}

		private static FieldRef<ItemAttributes, int> TryItemValueRef()
		{
			try
			{
				return (AccessTools.Field(typeof(ItemAttributes), "value") != null) ? AccessTools.FieldRefAccess<ItemAttributes, int>("value") : null;
			}
			catch
			{
				return null;
			}
		}

		private static IEnumerator SendItemAttributesSyncStreamed(Player target, LateJoinEntry e, ItemAttributes[] items)
		{
			if (_itemValueRef == null || items == null || items.Length == 0)
			{
				yield break;
			}
			int sent = 0;
			int skipped = 0;
			int batched = 0;
			foreach (ItemAttributes val in items)
			{
				if ((Object)(object)val == (Object)null)
				{
					skipped++;
					continue;
				}
				PhotonView component = ((Component)val).GetComponent<PhotonView>();
				if ((Object)(object)component == (Object)null || component.ViewID == 0)
				{
					skipped++;
					continue;
				}
				try
				{
					int num = _itemValueRef.Invoke(val);
					if (num > 0)
					{
						component.RPC("GetValueRPC", target, new object[1] { num });
						sent++;
					}
				}
				catch
				{
					skipped++;
				}
				batched++;
				if (batched >= 8)
				{
					batched = 0;
					yield return null;
					if (e.State == LateJoinState.Aborted || !IsTargetStillOnline(e.Target))
					{
						yield break;
					}
				}
			}
			Verbose($"Item 价格同步:sent={sent} skipped={skipped},分帧完成。");
		}

		private static int BroadcastLoadingAnimationCompletedOnce(Player target)
		{
			int num = 0;
			try
			{
				IEnumerable<PlayerAvatar> enumerable2;
				if (GameDirector.instance?.PlayerList == null)
				{
					IEnumerable<PlayerAvatar> enumerable = Object.FindObjectsOfType<PlayerAvatar>();
					enumerable2 = enumerable;
				}
				else
				{
					IEnumerable<PlayerAvatar> enumerable = GameDirector.instance.PlayerList;
					enumerable2 = enumerable;
				}
				foreach (PlayerAvatar item in enumerable2)
				{
					if ((Object)(object)item?.photonView != (Object)null)
					{
						try
						{
							item.photonView.RPC("LoadingLevelAnimationCompletedRPC", target, Array.Empty<object>());
							num++;
						}
						catch
						{
						}
					}
				}
			}
			catch
			{
			}
			return num;
		}

		public static void SafeDestroySlowProjectile(Object obj)
		{
			if (obj == (Object)null)
			{
				Object.Destroy(obj);
				return;
			}
			GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				Object.Destroy(obj);
				return;
			}
			if (!PhotonNetwork.IsMasterClient || PhotonNetwork.LocalPlayer == null || PhotonNetwork.CurrentRoom == null)
			{
				Object.Destroy((Object)(object)val);
				return;
			}
			bool flag = false;
			try
			{
				flag = GameManager.Multiplayer();
			}
			catch
			{
			}
			if (!flag)
			{
				Object.Destroy((Object)(object)val);
				return;
			}
			PhotonView val2 = null;
			try
			{
				val2 = val.GetComponent<PhotonView>();
			}
			catch
			{
			}
			if ((Object)(object)val2 == (Object)null || val2.ViewID == 0)
			{
				Object.Destroy((Object)(object)val);
				return;
			}
			SlowProjectile val3 = null;
			try
			{
				val3 = val.GetComponent<SlowProjectile>();
			}
			catch
			{
			}
			if ((Object)(object)val3 == (Object)null)
			{
				Object.Destroy((Object)(object)val);
				return;
			}
			try
			{
				if (_ownershipUpdate != null)
				{
					_projectileOwnershipScratch[0] = val2.ViewID;
					_projectileOwnershipScratch[1] = PhotonNetwork.LocalPlayer.ActorNumber;
					_ownershipUpdate(_projectileOwnershipScratch, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				if ((Object)(object)val == (Object)null)
				{
					Verbose($"[SlowProjectile] ViewID={val2.ViewID} 在所有权转让后已被销毁,跳过 PhotonNetwork.Destroy。");
					return;
				}
				PhotonNetwork.Destroy(val);
				Verbose($"[SlowProjectile] ViewID={val2.ViewID} 已通过 PhotonNetwork.Destroy 清理服务器缓存。");
				return;
			}
			catch (Exception ex)
			{
				Verbose($"[SlowProjectile] PhotonNetwork.Destroy 失败(ViewID={((val2 != null) ? new int?(val2.ViewID) : ((int?)null))}),回退到 Object.Destroy:{ex.Message}");
			}
			try
			{
				Object.Destroy((Object)(object)val);
			}
			catch
			{
			}
		}

		private static FieldRef<DataDirector, string> TryFieldRefRoom()
		{
			try
			{
				return (AccessTools.Field(typeof(DataDirector), "networkServerName") != null) ? AccessTools.FieldRefAccess<DataDirector, string>("networkServerName") : null;
			}
			catch
			{
				return null;
			}
		}

		private static FieldRef<RunManager, List<PlayerVoiceChat>> TryFieldRefRM()
		{
			try
			{
				return (AccessTools.Field(typeof(RunManager), "voiceChats") != null) ? AccessTools.FieldRefAccess<RunManager, List<PlayerVoiceChat>>("voiceChats") : null;
			}
			catch
			{
				return null;
			}
		}

		internal static void HideRoomForChangeLevelCooldown()
		{
			if (PhotonNetwork.IsMasterClient && PhotonNetwork.CurrentRoom != null && !((Object)(object)Instance == (Object)null))
			{
				LockRoomNow();
				_changeLevelCooldownUntil = Time.unscaledTime + 600f;
				_steamUnlockEnsureNextAt = 0f;
				if (!_changeLevelCooldownRunning)
				{
					_changeLevelCooldownRunning = true;
					((MonoBehaviour)Instance).StartCoroutine(CooldownCoroutine());
				}
			}
		}

		private static IEnumerator CooldownCoroutine()
		{
			try
			{
				float t0 = Time.unscaledTime;
				LevelGenerator old = LevelGenerator.Instance;
				bool newSeen = false;
				while (Time.unscaledTime - t0 < 60f)
				{
					if (!IsCooldownStillRelevant())
					{
						yield break;
					}
					LevelGenerator instance = LevelGenerator.Instance;
					if ((Object)(object)instance != (Object)null && instance != old)
					{
						newSeen = true;
						break;
					}
					if ((Object)(object)old == (Object)null && (Object)(object)instance != (Object)null)
					{
						newSeen = true;
						break;
					}
					yield return _waitGeneratedPoll;
				}
				if (newSeen)
				{
					while (Time.unscaledTime - t0 < 60f)
					{
						if (!IsCooldownStillRelevant())
						{
							yield break;
						}
						GameDirector instance2 = GameDirector.instance;
						if (instance2 != null && (int)instance2.currentState == 2)
						{
							break;
						}
						yield return _waitGeneratedPoll;
					}
				}
				if (IsCooldownStillRelevant())
				{
					float num = Time.unscaledTime - t0;
					Verbose($"房间已解锁(等待 {num:F1}s)。");
					_changeLevelCooldownUntil = Time.unscaledTime + 0f;
				}
			}
			finally
			{
				_changeLevelCooldownRunning = false;
			}
		}

		private static bool IsCooldownStillRelevant()
		{
			if (PhotonNetwork.IsMasterClient)
			{
				return PhotonNetwork.CurrentRoom != null;
			}
			return false;
		}

		private static bool LockRoomNow()
		{
			if (PhotonNetwork.CurrentRoom == null)
			{
				return false;
			}
			bool result = false;
			if (PhotonNetwork.CurrentRoom.IsOpen)
			{
				PhotonNetwork.CurrentRoom.IsOpen = false;
				result = true;
			}
			if (PhotonNetwork.CurrentRoom.IsVisible)
			{
				PhotonNetwork.CurrentRoom.IsVisible = false;
				result = true;
			}
			if (!_steamLobbyLocked)
			{
				try
				{
					SteamManager instance = SteamManager.instance;
					if (instance != null)
					{
						instance.LockLobby();
					}
					_steamLobbyLocked = true;
					result = true;
				}
				catch
				{
				}
			}
			return result;
		}

		internal static void EnforceLobbyState()
		{
			if (!PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null || (Object)(object)SteamManager.instance == (Object)null)
			{
				return;
			}
			if (IsInPreGameMenu())
			{
				_lastSceneAllowed = true;
				return;
			}
			try
			{
				if (IsInChangeLevelCooldown)
				{
					LockRoomNow();
					return;
				}
				if (IsCurrentSceneAllowed())
				{
					EnforceAllowedSceneState();
					_lastSceneAllowed = true;
					return;
				}
				LockRoomNow();
				if (_lastSceneAllowed != false)
				{
					_lastSceneAllowed = false;
					Verbose("场景不允许中途加入,房间已锁定。");
				}
			}
			catch (Exception ex)
			{
				LogWarning("房间状态维护失败:" + ex.Message);
			}
		}

		private static bool IsSteamLobbyActuallyLocked()
		{
			try
			{
				if (_steamPrivateLobbyField == null)
				{
					_steamPrivateLobbyField = AccessTools.Field(typeof(SteamManager), "privateLobby");
				}
				if (_steamPrivateLobbyField == null)
				{
					return true;
				}
				return (bool)_steamPrivateLobbyField.GetValue(SteamManager.instance);
			}
			catch
			{
				return true;
			}
		}

		private static void EnforceAllowedSceneState()
		{
			Room currentRoom = PhotonNetwork.CurrentRoom;
			bool flag = false;
			bool flag2 = IsPublicGame();
			if (!currentRoom.IsOpen)
			{
				currentRoom.IsOpen = true;
				flag = true;
			}
			if (_steamLobbyLocked)
			{
				try
				{
					SteamManager.instance.UnlockLobby(flag2);
					_steamLobbyLocked = false;
					flag = true;
				}
				catch
				{
				}
			}
			else
			{
				float unscaledTime = Time.unscaledTime;
				if (unscaledTime >= _steamUnlockEnsureNextAt)
				{
					_steamUnlockEnsureNextAt = unscaledTime + 5f;
					if (IsSteamLobbyActuallyLocked())
					{
						try
						{
							SteamManager.instance.UnlockLobby(flag2);
						}
						catch
						{
						}
					}
				}
			}
			if (flag2)
			{
				if (!currentRoom.IsVisible)
				{
					currentRoom.IsVisible = true;
					flag = true;
				}
				GameManager instance = GameManager.instance;
				if (instance != null)
				{
					instance.SetConnectRandom(true);
				}
			}
			if (flag)
			{
				Verbose("房间已开放。");
			}
		}

		internal static void NetworkConnectOnConnectedToMasterPrefix()
		{
			try
			{
				if (IsStaticModEnabled() && CompatibilityReport.RuntimeReady && PublicRoomPrefixEnabled && !((Object)(object)DataDirector.instance == (Object)null) && !((Object)(object)GameManager.instance == (Object)null) && _networkServerNameRef != null && IsPublicGame())
				{
					ref string reference = ref _networkServerNameRef.Invoke(DataDirector.instance);
					if (!string.IsNullOrEmpty(reference) && !HasRoomNamePrefix(reference))
					{
						reference = (UseChinese() ? "[中途加入] " : "[Late Join] ") + reference;
						LogInfo("房间名称已更新。");
					}
				}
			}
			catch (Exception ex)
			{
				Verbose("网络连接前置处理失败:" + ex.Message);
			}
		}

		private static bool HasRoomNamePrefix(string name)
		{
			if (name != null)
			{
				if (!name.StartsWith("[中途加入] ", StringComparison.Ordinal))
				{
					return name.StartsWith("[Late Join] ", StringComparison.Ordinal);
				}
				return true;
			}
			return false;
		}

		private static bool IsPublicGame()
		{
			if ((Object)(object)GameManager.instance == (Object)null)
			{
				return false;
			}
			try
			{
				if (CompatibilityReport.GameManagerConnectRandomRef != null)
				{
					return CompatibilityReport.GameManagerConnectRandomRef.Invoke(GameManager.instance);
				}
				object obj = CompatibilityReport.GameManagerConnectRandomField?.GetValue(GameManager.instance);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		internal static bool ClearInstantiationCacheById(int id)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (id == 0 || !CompatibilityReport.CanCleanPhotonCache)
			{
				return false;
			}
			try
			{
				Hashtable val = new Hashtable();
				object value = CompatibilityReport.PhotonKeyByteSeven.GetValue(null);
				RaiseEventOptions val2 = new RaiseEventOptions();
				val[value] = id;
				val2.CachingOption = (EventCaching)6;
				CompatibilityReport.PhotonRaiseEventInternal.Invoke(null, new object[4]
				{
					(byte)202,
					val,
					val2,
					SendOptions.SendReliable
				});
				return true;
			}
			catch (Exception ex)
			{
				Plugin instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)("缓存清理失败:" + ex.Message));
				}
				return false;
			}
		}

		internal static void ClearPhotonInstantiationCache(PhotonView pv)
		{
			if ((Object)(object)pv != (Object)null)
			{
				ClearInstantiationCacheById(pv.InstantiationId);
			}
		}

		internal static void ClearAllScenePhotonCacheWithKeepPublic(HashSet<int> keep)
		{
			ClearAllScenePhotonCacheWithKeepPublic(keep, clearPersistent: false);
		}

		internal static void ClearAllScenePhotonCacheWithKeepPublic(HashSet<int> keep, bool clearPersistent)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = 0;
				int num2 = 0;
				int num3 = 0;
				PhotonView[] array = Object.FindObjectsOfType<PhotonView>();
				foreach (PhotonView val in array)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Scene scene = ((Component)val).gameObject.scene;
					if (((Scene)(ref scene)).buildIndex == -1 || keep.Contains(val.ViewID) || (!clearPersistent && IsPlayerPersistentByName(val)))
					{
						continue;
					}
					if (val.ViewID != 0)
					{
						try
						{
							PhotonNetwork.RemoveBufferedRPCs(val.ViewID, (string)null, (int[])null);
							num3++;
						}
						catch
						{
						}
					}
					if (val.InstantiationId == 0)
					{
						continue;
					}
					if (val.isRuntimeInstantiated && val.IsMine)
					{
						t