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;
}
}
}
}
}