Decompiled source of MorePlayers v0.3.0

Mods\MorePlayersMod.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using MorePlayersMod;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "MorePlayers", "0.3.0", "leesky", null)]
[assembly: MelonGame("Keepsake Games", "Jump Space")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MorePlayersMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MorePlayersMod")]
[assembly: AssemblyTitle("MorePlayersMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 MorePlayersMod
{
	public class Core : MelonMod
	{
		public static MelonPreferences_Entry<int> MaxPlayers;

		public static MelonPreferences_Entry<bool> PatchSetters;

		private static bool _patchesApplied;

		public override void OnInitializeMelon()
		{
			MelonPreferences_Category obj = MelonPreferences.CreateCategory("MorePlayers", "More Players");
			MaxPlayers = obj.CreateEntry<int>("MaxPlayers", 6, "Max Players", "Maximum players allowed in a lobby.", false, false, (ValueValidator)null, (string)null);
			PatchSetters = obj.CreateEntry<bool>("PatchSetters", true, "Patch Setters", "Patch lobby max-player setters that are capped at 4.", false, false, (ValueValidator)null, (string)null);
			MelonLogger.Msg($"MorePlayers v0.3.0 loaded. Target max players: {MaxPlayers.Value}");
			MelonLogger.Warning("Experimental mod. Everyone in the lobby must install the same version.");
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			if (!_patchesApplied)
			{
				MelonLogger.Msg($"Scene loaded: {sceneName} (index {buildIndex}). Applying deferred patches...");
				MelonCoroutines.Start(ApplyPatchesAfterScene());
			}
		}

		private static IEnumerator ApplyPatchesAfterScene()
		{
			for (int i = 0; i < 120; i++)
			{
				yield return null;
			}
			if (!_patchesApplied)
			{
				HarmonyPatcher.Apply();
				_patchesApplied = true;
			}
		}
	}
	internal static class HarmonyPatcher
	{
		private static readonly Harmony HarmonyInstance = new Harmony("com.cursoragent.jumpspace.moreplayers");

		private static bool _applied;

		internal static void Apply()
		{
			if (!_applied)
			{
				_applied = true;
				int num = 0;
				num += TryPatchExactSetter("Il2CppEpic.OnlineServices.Lobby.CreateLobbyOptions", "set_MaxLobbyMembers", typeof(uint));
				num += TryPatchKeepsakeMaxMemberCount();
				MelonLogger.Msg($"MorePlayers applied {num} Harmony patches (deferred).");
			}
		}

		private static int TryPatchExactSetter(string typeName, string methodName, Type parameterType)
		{
			Type type = AccessTools.TypeByName(typeName);
			if (type == null)
			{
				MelonLogger.Warning("Type not found: " + typeName);
				return 0;
			}
			MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				MelonLogger.Warning("Method not found: " + typeName + "." + methodName);
				return 0;
			}
			if (methodInfo.GetParameters()[0].ParameterType != parameterType)
			{
				MelonLogger.Warning("Unexpected parameter type on " + typeName + "." + methodName);
				return 0;
			}
			return TryPatchSetter(methodInfo) ? 1 : 0;
		}

		private static int TryPatchKeepsakeMaxMemberCount()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly.GetName().Name != "Il2CppKeepsake.Online.Epic")
				{
					continue;
				}
				Type[] array;
				try
				{
					array = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types.Where((Type t) => t != null).Cast<Type>().ToArray();
				}
				catch
				{
					continue;
				}
				Type[] array2 = array;
				foreach (Type type in array2)
				{
					MethodInfo methodInfo = AccessTools.Method(type, "set_MaxMemberCount", (Type[])null, (Type[])null);
					MethodInfo methodInfo2 = AccessTools.Method(type, "get_CurrentMemberCount", (Type[])null, (Type[])null);
					if (!(methodInfo == null) && !(methodInfo2 == null) && !(methodInfo.GetParameters()[0].ParameterType != typeof(int)) && TryPatchSetter(methodInfo))
					{
						MelonLogger.Msg("Patched Keepsake setter: " + type.FullName + ".set_MaxMemberCount");
						return 1;
					}
				}
			}
			MelonLogger.Warning("Keepsake set_MaxMemberCount target not found.");
			return 0;
		}

		private static bool TryPatchSetter(MethodInfo method)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			Type parameterType = method.GetParameters()[0].ParameterType;
			HarmonyMethod val = null;
			if (parameterType == typeof(int))
			{
				val = new HarmonyMethod(typeof(HarmonyPatcher), "MaxPlayerSetterPrefixInt", (Type[])null);
			}
			else
			{
				if (!(parameterType == typeof(uint)))
				{
					return false;
				}
				val = new HarmonyMethod(typeof(HarmonyPatcher), "MaxPlayerSetterPrefixUInt", (Type[])null);
			}
			try
			{
				HarmonyInstance.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				MelonLogger.Msg($"Patched setter: {method.DeclaringType?.FullName}.{method.Name} ({parameterType.Name})");
				return true;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning($"Failed to patch setter {method.DeclaringType?.FullName}.{method.Name}: {ex.Message}");
				return false;
			}
		}

		public static void MaxPlayerSetterPrefixInt(ref int __0)
		{
			if (Core.PatchSetters.Value)
			{
				int num = __0;
				if (num > 0 && num <= 4)
				{
					__0 = Core.MaxPlayers.Value;
				}
			}
		}

		public static void MaxPlayerSetterPrefixUInt(ref uint __0)
		{
			if (Core.PatchSetters.Value)
			{
				uint num = __0;
				if (num != 0 && num <= 4)
				{
					__0 = (uint)Core.MaxPlayers.Value;
				}
			}
		}
	}
}