using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
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("MoreCriminal.MaxPlayers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Configurable max player count mod for MoreCriminal online sessions.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MoreCriminal.MaxPlayers")]
[assembly: AssemblyTitle("MoreCriminal.MaxPlayers")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace MoreCriminal.MaxPlayers;
[BepInPlugin("morecriminal.maxplayers", "MoreCriminal Max Players", "1.1.0")]
public sealed class Plugin : BaseUnityPlugin
{
internal static ManualLogSource Log;
private static int _configuredMaxPlayers;
private void Awake()
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Expected O, but got Unknown
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
ConfigEntry<int> val = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxPlayers", 8, new ConfigDescription("Online host room size override.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 32), Array.Empty<object>()));
_configuredMaxPlayers = Math.Max(1, val.Value);
try
{
new Harmony("morecriminal.maxplayers").PatchAll(typeof(Plugin).Assembly);
Log.LogInfo((object)$"[MaxPlayers] Loaded. Configured max players: {_configuredMaxPlayers}");
}
catch (Exception arg)
{
Log.LogError((object)$"[MaxPlayers] Failed to apply patches: {arg}");
}
}
internal static int GetConfiguredMaxPlayers()
{
return Math.Max(1, _configuredMaxPlayers);
}
internal static void UpdateSteamLobbyMetadata(object steamManagerInstance, int currentPlayers)
{
try
{
if (steamManagerInstance == null)
{
return;
}
object instanceMemberValue = ReflectionHelper.GetInstanceMemberValue(steamManagerInstance, "current_lobbyID");
if (instanceMemberValue == null || Convert.ToUInt64(instanceMemberValue) == 0L)
{
return;
}
Type type = AccessTools.TypeByName("Steamworks.CSteamID");
Type type2 = AccessTools.TypeByName("Steamworks.SteamMatchmaking");
if (!(type == null) && !(type2 == null))
{
object obj = Activator.CreateInstance(type, Convert.ToUInt64(instanceMemberValue));
int configuredMaxPlayers = GetConfiguredMaxPlayers();
MethodInfo methodInfo = AccessTools.Method(type2, "SetLobbyData", (Type[])null, (Type[])null);
if (methodInfo != null)
{
methodInfo.Invoke(null, new object[3]
{
obj,
"playerCount",
$"{currentPlayers}/{configuredMaxPlayers}"
});
methodInfo.Invoke(null, new object[3]
{
obj,
"maxPlayers",
configuredMaxPlayers.ToString()
});
}
MethodInfo methodInfo2 = AccessTools.Method(type2, "SetLobbyMemberLimit", (Type[])null, (Type[])null);
if (methodInfo2 != null)
{
methodInfo2.Invoke(null, new object[2] { obj, configuredMaxPlayers });
}
}
}
catch (Exception ex)
{
Log.LogWarning((object)("[MaxPlayers] Failed to update Steam lobby metadata: " + ex.Message));
}
}
internal static MethodInfo FindMethod(Type type, string name, int parameterCount)
{
if (type == null)
{
return null;
}
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.Name == name && methodInfo.GetParameters().Length == parameterCount)
{
return methodInfo;
}
}
return null;
}
}
internal static class ReflectionHelper
{
private const BindingFlags AllInstanceBindings = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags AllStaticBindings = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
internal static object GetInstanceMemberValue(object instance, string memberName)
{
Type type = instance.GetType();
while (type != null)
{
FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
return field.GetValue(instance);
}
PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.CanRead)
{
return property.GetValue(instance, null);
}
type = type.BaseType;
}
return null;
}
internal static object GetStaticMemberValue(Type type, string memberName)
{
Type type2 = type;
while (type2 != null)
{
FieldInfo field = type2.GetField(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
return field.GetValue(null);
}
PropertyInfo property = type2.GetProperty(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.CanRead)
{
return property.GetValue(null, null);
}
type2 = type2.BaseType;
}
return null;
}
internal static bool SetInstanceMemberValue(object instance, string memberName, object value)
{
Type type = instance.GetType();
while (type != null)
{
FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
field.SetValue(instance, value);
return true;
}
PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.CanWrite)
{
property.SetValue(instance, value, null);
return true;
}
type = type.BaseType;
}
return false;
}
}
[HarmonyPatch]
internal static class PlayOnlinePlayerCountPatch
{
private static MethodBase TargetMethod()
{
Type type = AccessTools.TypeByName("MainMenu+<PlayOnline>d__101");
if (!(type == null))
{
return AccessTools.Method(type, "MoveNext", (Type[])null, (Type[])null);
}
return null;
}
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
ConstructorInfo objB = AccessTools.Constructor(typeof(int?), new Type[1] { typeof(int) }, false);
for (int i = 1; i < list.Count; i++)
{
if (object.Equals(list[i].operand, objB) && LoadsInt(list[i - 1], 4))
{
list[i - 1] = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Plugin), "GetConfiguredMaxPlayers", (Type[])null, (Type[])null));
Plugin.Log.LogInfo((object)"[MaxPlayers] Patched MainMenu.PlayOnline room size.");
return list;
}
}
Plugin.Log.LogWarning((object)"[MaxPlayers] Failed to patch MainMenu.PlayOnline room size.");
return list;
}
private static bool LoadsInt(CodeInstruction instruction, int value)
{
if (instruction.opcode == OpCodes.Ldc_I4)
{
if (instruction.operand is int num)
{
return num == value;
}
return false;
}
if (instruction.opcode == OpCodes.Ldc_I4_S)
{
if (instruction.operand is sbyte b)
{
return b == value;
}
return false;
}
if (instruction.opcode == OpCodes.Ldc_I4_4)
{
return value == 4;
}
return false;
}
}
[HarmonyPatch]
internal static class SteamCreateLobbyPatch
{
private static MethodBase TargetMethod()
{
Type type = AccessTools.TypeByName("MainMenu");
if (!(type == null))
{
return AccessTools.Method(type, "Update", (Type[])null, (Type[])null);
}
return null;
}
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
bool flag = false;
for (int i = 1; i < list.Count; i++)
{
MethodInfo methodInfo = list[i].operand as MethodInfo;
if (!(methodInfo == null) && !(methodInfo.Name != "CreateLobby") && LoadsInt(list[i - 1], 4))
{
list[i - 1] = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Plugin), "GetConfiguredMaxPlayers", (Type[])null, (Type[])null));
flag = true;
}
}
if (flag)
{
Plugin.Log.LogInfo((object)"[MaxPlayers] Patched Steam lobby creation size.");
}
else
{
Plugin.Log.LogWarning((object)"[MaxPlayers] Failed to patch Steam lobby creation size.");
}
return list;
}
private static bool LoadsInt(CodeInstruction instruction, int value)
{
if (instruction.opcode == OpCodes.Ldc_I4)
{
if (instruction.operand is int num)
{
return num == value;
}
return false;
}
if (instruction.opcode == OpCodes.Ldc_I4_S)
{
if (instruction.operand is sbyte b)
{
return b == value;
}
return false;
}
if (instruction.opcode == OpCodes.Ldc_I4_4)
{
return value == 4;
}
return false;
}
}
[HarmonyPatch]
internal static class SteamLobbyCreatedPatch
{
private static MethodBase TargetMethod()
{
Type type = AccessTools.TypeByName("SteamManager");
if (!(type == null))
{
return AccessTools.Method(type, "OnLobbyCreated", (Type[])null, (Type[])null);
}
return null;
}
private static void Postfix(object __instance)
{
Plugin.UpdateSteamLobbyMetadata(__instance, 1);
}
}
[HarmonyPatch]
internal static class LobbyPlayerCountPatch
{
private static MethodBase TargetMethod()
{
Type type = AccessTools.TypeByName("Lobby");
if (!(type == null))
{
return AccessTools.Method(type, "SetPlayerCount", (Type[])null, (Type[])null);
}
return null;
}
private static void Postfix(object __instance)
{
try
{
int num = ((ReflectionHelper.GetInstanceMemberValue(__instance, "players") is ICollection collection) ? collection.Count : 0);
if (ReflectionHelper.GetInstanceMemberValue(__instance, "playerSpawns") is List<Transform> list && list.Count > 0)
{
int num2 = num % list.Count;
if (ReflectionHelper.SetInstanceMemberValue(__instance, "spawnedPlayers", num2))
{
Plugin.Log.LogInfo((object)$"[MaxPlayers] Lobby spawnedPlayers wrapped to {num2} (players={num}, spawns={list.Count}).");
}
}
Type type = AccessTools.TypeByName("SteamManager");
if (!(type == null))
{
object staticMemberValue = ReflectionHelper.GetStaticMemberValue(type, "Instance");
if (staticMemberValue != null)
{
Plugin.UpdateSteamLobbyMetadata(staticMemberValue, num);
}
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("[MaxPlayers] Failed to sync lobby player count: " + ex.Message));
}
}
}