Decompiled source of forcejoin v1.0.2

ForceJoin.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ForceJoin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("ForceJoin")]
[assembly: AssemblyTitle("ForceJoin")]
[assembly: AssemblyVersion("1.0.2.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 ForceJoin
{
	internal static class ForceJoinCore
	{
		private static FieldInfo _poolField;

		private static FieldInfo _singlePoolField;

		private static readonly Dictionary<string, GameObject> Stubs = new Dictionary<string, GameObject>(StringComparer.Ordinal);

		private static readonly HashSet<string> Reported = new HashSet<string>(StringComparer.Ordinal);

		private static int _missingCount;

		internal static int MissingCount => _missingCount;

		internal static MultiplayerPool GetPool()
		{
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			if (_poolField == null)
			{
				_poolField = AccessTools.Field(typeof(RunManager), "multiplayerPool");
			}
			object? obj = _poolField?.GetValue(instance);
			return (MultiplayerPool)((obj is MultiplayerPool) ? obj : null);
		}

		private static Dictionary<string, Object> GetSinglePool()
		{
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			if (_singlePoolField == null)
			{
				_singlePoolField = AccessTools.Field(typeof(RunManager), "singleplayerPool");
			}
			return _singlePoolField?.GetValue(instance) as Dictionary<string, Object>;
		}

		internal static void EnsureMany(string[] paths)
		{
			if (paths != null && paths.Length != 0)
			{
				for (int i = 0; i < paths.Length; i++)
				{
					Ensure(paths[i]);
				}
			}
		}

		internal static void Ensure(string path)
		{
			if (!ForceJoinPlugin.FillMissingPrefabs.Value || string.IsNullOrEmpty(path))
			{
				return;
			}
			MultiplayerPool pool = GetPool();
			if (pool != null && (!pool.ResourceCache.TryGetValue(path, out var value) || !((Object)(object)value != (Object)null)))
			{
				GameObject val = TryLoadReal(path);
				bool flag = (Object)(object)val == (Object)null;
				if (flag)
				{
					val = GetOrCreateStub(path);
					_missingCount++;
				}
				pool.ResourceCache[path] = val;
				Dictionary<string, Object> singlePool = GetSinglePool();
				if (singlePool != null)
				{
					singlePool[path] = (Object)(object)val;
				}
				Report(path, flag);
			}
		}

		private static GameObject TryLoadReal(string path)
		{
			try
			{
				GameObject val = Resources.Load<GameObject>(path);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			catch
			{
			}
			if (!path.StartsWith("AssetBundles/", StringComparison.Ordinal))
			{
				return null;
			}
			string[] array = path.Split('/');
			if (array.Length < 3)
			{
				return null;
			}
			string text = array[1];
			string text2 = string.Join("/", array, 2, array.Length - 2);
			try
			{
				foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles())
				{
					if (!((Object)(object)allLoadedAssetBundle == (Object)null) && !(((Object)allLoadedAssetBundle).name != text))
					{
						GameObject val2 = allLoadedAssetBundle.LoadAsset<GameObject>(text2);
						if ((Object)(object)val2 != (Object)null)
						{
							return val2;
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static GameObject GetOrCreateStub(string path)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (Stubs.TryGetValue(path, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = new GameObject("ForceJoinStub:" + path);
			value.SetActive(false);
			PhotonView obj = value.AddComponent<PhotonView>();
			obj.ViewID = 0;
			obj.Synchronization = (ViewSynchronization)0;
			obj.ObservedComponents = new List<Component>();
			Object.DontDestroyOnLoad((Object)(object)value);
			Stubs[path] = value;
			return value;
		}

		internal static GameObject InstantiateStub(string prefabId, Vector3 position, Quaternion rotation)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(prefabId))
			{
				return null;
			}
			GameObject val = null;
			MultiplayerPool pool = GetPool();
			if (pool != null && pool.ResourceCache.TryGetValue(prefabId, out var value) && (Object)(object)value != (Object)null)
			{
				val = value;
			}
			if ((Object)(object)val == (Object)null)
			{
				val = TryLoadReal(prefabId);
			}
			if ((Object)(object)val == (Object)null)
			{
				val = GetOrCreateStub(prefabId);
			}
			return Object.Instantiate<GameObject>(val, position, rotation);
		}

		private static void Report(string path, bool stub)
		{
			if (Reported.Add(path) && (ForceJoinPlugin.Verbose.Value || stub))
			{
				if (stub)
				{
					ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 本机缺少 prefab,已用占位补齐: " + path));
				}
				else
				{
					ForceJoinPlugin.Log.LogInfo((object)("[ForceJoin] 已从本地资源补齐 prefab: " + path));
				}
			}
		}

		internal unsafe static void LogDisconnect(DisconnectCause cause)
		{
			ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 连接断开,原因: " + ((object)(*(DisconnectCause*)(&cause))/*cast due to .constrained prefix*/).ToString()));
		}
	}
	internal sealed class ForceJoinModuleSpawn : MonoBehaviour
	{
		private static ForceJoinModuleSpawn _instance;

		private static FieldInfo _spawnedField;

		private static FieldInfo _targetField;

		private static FieldInfo _amountReceivedField;

		private static FieldInfo _setupDoneField;

		private static MethodInfo _connectionSetupMethod;

		private static bool _reflectionFailed;

		private float _stuckSince = -1f;

		private bool _triedLocalSetup;

		private bool _errorLogged;

		internal static void Install()
		{
			//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)
			//IL_001f: Expected O, but got Unknown
			if ((Object)(object)_instance != (Object)null)
			{
				return;
			}
			try
			{
				GameObject val = new GameObject("ForceJoinModuleSpawn");
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<ForceJoinModuleSpawn>();
				ForceJoinPlugin.Log.LogInfo((object)"[ForceJoin] 模块同步看门狗已启动。");
			}
			catch (Exception ex)
			{
				ForceJoinPlugin.Log.LogError((object)("[ForceJoin] 看门狗启动失败: " + ex.Message));
			}
		}

		private void Update()
		{
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				if (!_errorLogged)
				{
					_errorLogged = true;
					ForceJoinPlugin.Log.LogError((object)("[ForceJoin] 看门狗异常: " + ex));
				}
			}
		}

		private void Tick()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			if (!ForceJoinPlugin.ForceModuleSpawn.Value)
			{
				Clear();
				return;
			}
			LevelGenerator instance = LevelGenerator.Instance;
			if ((Object)(object)instance == (Object)null || instance.Generated)
			{
				Clear();
				return;
			}
			if (!SemiFunc.IsMultiplayer() || SemiFunc.IsMasterClientOrSingleplayer())
			{
				Clear();
				return;
			}
			if ((int)instance.State != 7 && (int)instance.State != 8)
			{
				Clear();
				return;
			}
			int spawned = GetSpawned(instance);
			int target = GetTarget(instance);
			bool amountReceived = GetAmountReceived(instance);
			if (amountReceived && (target <= 0 || spawned >= target))
			{
				Clear();
				return;
			}
			if (_stuckSince < 0f)
			{
				_stuckSince = Time.realtimeSinceStartup;
				return;
			}
			float num = Time.realtimeSinceStartup - _stuckSince;
			if (!_triedLocalSetup && num >= 2f)
			{
				_triedLocalSetup = true;
				int num2 = RunLocalConnectionSetup();
				if (num2 > 0)
				{
					int spawned2 = GetSpawned(instance);
					ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 本机 " + num2 + " 个 Module 未完成连接初始化(房主 RPC 未补发),已补跑 ConnectionSetup,ModulesSpawned " + spawned2 + "/" + target + "。"));
					if (target > 0 && spawned2 >= target)
					{
						Clear();
						return;
					}
				}
			}
			if (!(num < ForceJoinPlugin.ForceGraceSeconds.Value))
			{
				int spawned3 = GetSpawned(instance);
				if (target > 0 && spawned3 < target)
				{
					SetSpawned(instance, target);
					ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 模块同步停滞 " + num.ToString("F1") + " 秒,ModulesSpawned " + spawned3 + "/" + target + ",已强制顶到目标数放行。"));
				}
				if (!amountReceived)
				{
					SetAmountReceived(instance, value: true);
					ForceJoinPlugin.Log.LogWarning((object)"[ForceJoin] 未收到房主 ModuleAmountRPC,已跳过模块数量检查。");
				}
				Clear();
			}
		}

		private void Clear()
		{
			_stuckSince = -1f;
			_triedLocalSetup = false;
		}

		private static void EnsureReflection()
		{
			if (!(_spawnedField != null) && !_reflectionFailed)
			{
				Type? typeFromHandle = typeof(LevelGenerator);
				Type typeFromHandle2 = typeof(Module);
				_spawnedField = AccessTools.Field(typeFromHandle, "ModulesSpawned");
				_targetField = AccessTools.Field(typeFromHandle, "ModulesSpawnedTarget");
				_amountReceivedField = AccessTools.Field(typeFromHandle, "moduleAmountReceived");
				_setupDoneField = AccessTools.Field(typeFromHandle2, "SetupDone");
				_connectionSetupMethod = AccessTools.Method(typeFromHandle2, "ConnectionSetup", Type.EmptyTypes, (Type[])null);
				if (_spawnedField == null || _targetField == null || _amountReceivedField == null)
				{
					_reflectionFailed = true;
					ForceJoinPlugin.Log.LogError((object)"[ForceJoin] 反射字段定位失败,模块同步强行放行不可用。");
				}
			}
		}

		private static int GetSpawned(LevelGenerator lg)
		{
			EnsureReflection();
			if (_spawnedField == null)
			{
				return 0;
			}
			object value = _spawnedField.GetValue(lg);
			if (value is int)
			{
				return (int)value;
			}
			return 0;
		}

		private static int GetTarget(LevelGenerator lg)
		{
			EnsureReflection();
			if (_targetField == null)
			{
				return 0;
			}
			object value = _targetField.GetValue(lg);
			if (value is int)
			{
				return (int)value;
			}
			return 0;
		}

		private static bool GetAmountReceived(LevelGenerator lg)
		{
			EnsureReflection();
			if (_amountReceivedField == null)
			{
				return true;
			}
			object value = _amountReceivedField.GetValue(lg);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static void SetSpawned(LevelGenerator lg, int value)
		{
			EnsureReflection();
			_spawnedField?.SetValue(lg, value);
		}

		private static void SetAmountReceived(LevelGenerator lg, bool value)
		{
			EnsureReflection();
			_amountReceivedField?.SetValue(lg, value);
		}

		private static int RunLocalConnectionSetup()
		{
			EnsureReflection();
			if (_connectionSetupMethod == null || _setupDoneField == null)
			{
				return 0;
			}
			Module[] array = Object.FindObjectsOfType<Module>();
			if (array == null || array.Length == 0)
			{
				return 0;
			}
			int num = 0;
			bool flag = default(bool);
			foreach (Module val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				bool flag2;
				try
				{
					object value = _setupDoneField.GetValue(val);
					int num2;
					if (value is bool)
					{
						flag = (bool)value;
						num2 = 1;
					}
					else
					{
						num2 = 0;
					}
					flag2 = (byte)((uint)num2 & (flag ? 1u : 0u)) != 0;
				}
				catch
				{
					continue;
				}
				if (!flag2)
				{
					try
					{
						_connectionSetupMethod.Invoke(val, null);
						num++;
					}
					catch (Exception ex)
					{
						ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] Module 连接初始化失败(已跳过): " + ex.GetType().Name + " " + ex.Message));
					}
				}
			}
			return num;
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "PrefabsLoadRPC")]
	internal static class PatchPrefabsLoadRpc
	{
		private static void Prefix(string[] __0, string[] __1)
		{
			ForceJoinCore.EnsureMany(__0);
			ForceJoinCore.EnsureMany(__1);
		}
	}
	[HarmonyPatch(typeof(MultiplayerPool), "Instantiate")]
	internal static class PatchPoolInstantiate
	{
		private static void Prefix(string __0)
		{
			ForceJoinCore.Ensure(__0);
		}
	}
	[HarmonyPatch(typeof(MultiplayerPool), "InstantiateNew")]
	internal static class PatchPoolInstantiateNew
	{
		private static void Postfix(string __0, Vector3 __1, Quaternion __2, ref GameObject __result)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__result != (Object)null))
			{
				__result = ForceJoinCore.InstantiateStub(__0, __1, __2);
				ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] Instantiate 返回 null,已用占位实例顶上: " + __0));
			}
		}
	}
	[HarmonyPatch(typeof(MultiplayerPool), "SpareTake")]
	internal static class PatchSpareTake
	{
		private static Exception Finalizer(string __0, Exception __exception, ref GameObject __result)
		{
			if (__exception == null)
			{
				return null;
			}
			if (!ForceJoinPlugin.SuppressInstantiateKick.Value)
			{
				return __exception;
			}
			ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 模块副本未就绪,改为现场实例化: " + __0 + " -> " + __exception.Message));
			__result = null;
			return null;
		}
	}
	[HarmonyPatch(typeof(MultiplayerPool), "SpareAddAsync")]
	internal static class PatchSpareAddAsync
	{
		private static Exception Finalizer(string __0, Exception __exception)
		{
			if (__exception == null)
			{
				return null;
			}
			if (!ForceJoinPlugin.SuppressInstantiateKick.Value)
			{
				return __exception;
			}
			ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 模块副本预拷贝失败,已忽略: " + __0 + " -> " + __exception.Message));
			return null;
		}
	}
	[HarmonyPatch(typeof(MultiplayerPool), "InstantiateFailed")]
	internal static class PatchInstantiateFailed
	{
		private static bool Prefix(string __0)
		{
			if (!ForceJoinPlugin.SuppressInstantiateKick.Value)
			{
				return true;
			}
			ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 拦截缺失 prefab 踢人: " + __0));
			return false;
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "GenerationFailed")]
	internal static class PatchGenerationFailed
	{
		private static bool Prefix(Exception __0)
		{
			if (!ForceJoinPlugin.SwallowGenerationFailed.Value)
			{
				return true;
			}
			ForceJoinPlugin.Log.LogWarning((object)("[ForceJoin] 已拦截 GenerationFailed: " + __0?.Message));
			return false;
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnDisconnected")]
	internal static class PatchOnDisconnected
	{
		private static void Prefix(DisconnectCause __0)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ForceJoinCore.LogDisconnect(__0);
		}
	}
	[BepInPlugin("Aartyns.REPO.ForceJoin", "ForceJoin", "1.0.2")]
	public class ForceJoinPlugin : BaseUnityPlugin
	{
		public const string Guid = "Aartyns.REPO.ForceJoin";

		public const string Version = "1.0.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> FillMissingPrefabs;

		internal static ConfigEntry<bool> SuppressInstantiateKick;

		internal static ConfigEntry<bool> SwallowGenerationFailed;

		internal static ConfigEntry<bool> Verbose;

		internal static ConfigEntry<bool> ForceModuleSpawn;

		internal static ConfigEntry<float> ForceGraceSeconds;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			FillMissingPrefabs = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FillMissingPrefabs", true, "本机缺少房主下发的 prefab 时,用空占位对象塞进 MultiplayerPool.ResourceCache,让关卡生成不中断。");
			SuppressInstantiateKick = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SuppressInstantiateKick", true, "拦截 MultiplayerPool.InstantiateFailed,避免因缺失 prefab 被送回主菜单。");
			SwallowGenerationFailed = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SwallowGenerationFailed", false, "拦截 LevelGenerator.GenerationFailed(危险:协程已被打断,关卡会停在半成品状态,默认关闭,仅用于诊断)。");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", true, "在日志中打印被补齐的 prefab 路径与联机断开原因。");
			ForceModuleSpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceModuleSpawn", true, "中途加入时房主的 ModuleConnectionSetRPC 不会补发,客机 ModulesSpawned 永远停在 0,卡死在模块同步。开启后先对本地已有 Module 补跑 ConnectionSetup,仍不达标则强制把 ModulesSpawned 顶到目标数放行。");
			ForceGraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ForceGraceSeconds", 8f, "客机模块同步停滞多少秒后强制放行。房主房间较大时可适当调大。");
			_harmony = new Harmony("Aartyns.REPO.ForceJoin");
			int num = 0;
			int num2 = 0;
			Type[] types = typeof(ForceJoinPlugin).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if (!type.IsClass || type.Name.StartsWith("<", StringComparison.Ordinal))
				{
					continue;
				}
				bool flag = false;
				object[] customAttributes = type.GetCustomAttributes(inherit: true);
				for (int j = 0; j < customAttributes.Length; j++)
				{
					if (customAttributes[j].GetType().Name.StartsWith("Harmony", StringComparison.Ordinal))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					try
					{
						_harmony.CreateClassProcessor(type).Patch();
						num++;
					}
					catch (Exception ex)
					{
						num2++;
						Log.LogError((object)("[ForceJoin] 补丁挂载失败 " + type.Name + ": " + ex.Message));
					}
				}
			}
			Log.LogInfo((object)("[ForceJoin] 已加载 v1.0.2,补丁 " + num + " 个成功 / " + num2 + " 个失败。"));
			Log.LogInfo((object)"[ForceJoin] 说明:缺失 prefab 走占位补齐,模块同步停滞会自动放行。");
			ForceJoinModuleSpawn.Install();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}