Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of forcejoin v1.0.2
ForceJoin.dll
Decompiled 2 hours agousing 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(); } } } }