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 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("darkg")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Configurable max player limit mod for R.E.P.O.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("RepoMorePlayers")] [assembly: AssemblyTitle("RepoMorePlayers")] [assembly: AssemblyVersion("0.1.0.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 RepoMorePlayers { [BepInPlugin("darkg.repo.moreplayers", "RepoMorePlayers", "0.1.0")] [BepInProcess("REPO.exe")] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(LoadBalancingClient), "OpCreateRoom", new Type[] { typeof(EnterRoomParams) })] private static class OpCreateRoomPatch { private static void Prefix(EnterRoomParams enterRoomParams) { ApplyRoomOptions(enterRoomParams, "OpCreateRoom"); } } [HarmonyPatch(typeof(LoadBalancingClient), "OpJoinOrCreateRoom", new Type[] { typeof(EnterRoomParams) })] private static class OpJoinOrCreateRoomPatch { private static void Prefix(EnterRoomParams enterRoomParams) { ApplyRoomOptions(enterRoomParams, "OpJoinOrCreateRoom"); } } private static class SteamLobbyPatch { internal static void TryApply(Harmony harmony) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown if (!patchSteamLobby.Value) { return; } Type type = AccessTools.TypeByName("SteamManager"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "HostLobby", (Type[])null, (Type[])null)); if (methodInfo == null) { ManualLogSource log = Plugin.log; if (log != null) { log.LogWarning((object)"SteamManager.HostLobby was not found. Photon room limits will still be patched."); } return; } MethodBase methodBase = methodInfo; AsyncStateMachineAttribute customAttribute = methodInfo.GetCustomAttribute(); if (customAttribute?.StateMachineType != null) { methodBase = AccessTools.Method(customAttribute.StateMachineType, "MoveNext", (Type[])null, (Type[])null); } if (methodBase == null) { ManualLogSource log2 = Plugin.log; if (log2 != null) { log2.LogWarning((object)"SteamManager.HostLobby state machine was not found. Photon room limits will still be patched."); } return; } harmony.Patch(methodBase, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SteamLobbyPatch), "ReplaceVanillaLobbyLimit", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log3 = Plugin.log; if (log3 != null) { log3.LogInfo((object)$"Steam lobby limit patch applied to {methodBase.DeclaringType?.FullName}.{methodBase.Name}."); } } private static IEnumerable ReplaceVanillaLobbyLimit(IEnumerable instructions) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown List list = new List(instructions); int num = 0; for (int i = 0; i < list.Count; i++) { if (LoadsInt(list[i], 6)) { num++; } } if (num != 1) { ManualLogSource log = Plugin.log; if (log != null) { log.LogWarning((object)$"Steam lobby patch expected one hardcoded 6 but found {num}. Leaving Steam lobby IL unchanged."); } return list; } MethodInfo methodInfo = AccessTools.Method(typeof(Plugin), "GetConfiguredMaxPlayers", (Type[])null, (Type[])null); for (int j = 0; j < list.Count; j++) { if (LoadsInt(list[j], 6)) { CodeInstruction value = new CodeInstruction(OpCodes.Call, (object)methodInfo) { labels = list[j].labels, blocks = list[j].blocks }; list[j] = value; break; } } return list; } private static bool LoadsInt(CodeInstruction instruction, int value) { if (value == -1 && instruction.opcode == OpCodes.Ldc_I4_M1) { return true; } if (value >= 0 && value <= 8 && instruction.opcode.Name == "ldc.i4." + value) { return true; } if (instruction.opcode == OpCodes.Ldc_I4_S && instruction.operand is sbyte b) { return b == value; } if (instruction.opcode == OpCodes.Ldc_I4 && instruction.operand is int num) { return num == value; } return false; } } public const string PluginGuid = "darkg.repo.moreplayers"; public const string PluginName = "RepoMorePlayers"; public const string PluginVersion = "0.1.0"; private static ConfigEntry maxPlayers; private static ConfigEntry patchSteamLobby; private static ConfigEntry shrinkClosedRooms; private static ManualLogSource log; private Harmony harmony; private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown log = ((BaseUnityPlugin)this).Logger; maxPlayers = ((BaseUnityPlugin)this).Config.Bind("General", "MaxPlayers", 10, new ConfigDescription("Maximum players for newly created rooms. Keep this at 20 or below unless you enjoy desyncs.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), Array.Empty())); patchSteamLobby = ((BaseUnityPlugin)this).Config.Bind("General", "PatchSteamLobby", true, "Also raises the Steam lobby member limit so Steam friends can join expanded rooms."); shrinkClosedRooms = ((BaseUnityPlugin)this).Config.Bind("General", "ShrinkClosedRooms", true, "When the host closes a room, reduce Photon MaxPlayers to the current player count to avoid scene-load waits for missing players."); harmony = new Harmony("darkg.repo.moreplayers"); harmony.PatchAll(typeof(Plugin).Assembly); SteamLobbyPatch.TryApply(harmony); ((MonoBehaviour)this).StartCoroutine(MonitorRoomCap()); log.LogInfo((object)$"{"RepoMorePlayers"} {"0.1.0"} loaded. MaxPlayers={GetConfiguredMaxPlayers()}"); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } internal static int GetConfiguredMaxPlayers() { if (maxPlayers == null) { return 10; } return Math.Max(1, Math.Min(20, maxPlayers.Value)); } internal static void ApplyRoomOptions(EnterRoomParams enterRoomParams, string source) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (enterRoomParams == null) { return; } if (enterRoomParams.RoomOptions == null) { enterRoomParams.RoomOptions = new RoomOptions(); } int configuredMaxPlayers = GetConfiguredMaxPlayers(); int num = enterRoomParams.RoomOptions.MaxPlayers; if (num != configuredMaxPlayers) { enterRoomParams.RoomOptions.MaxPlayers = configuredMaxPlayers; ManualLogSource obj = log; if (obj != null) { obj.LogDebug((object)$"{source}: MaxPlayers {num} -> {configuredMaxPlayers}"); } } } private IEnumerator MonitorRoomCap() { WaitForSeconds wait = new WaitForSeconds(1f); while (true) { try { Room currentRoom = PhotonNetwork.CurrentRoom; if (shrinkClosedRooms.Value && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && currentRoom != null) { byte b = (byte)GetConfiguredMaxPlayers(); byte b2 = (byte)Math.Max(1, currentRoom.PlayerCount); if (currentRoom.IsOpen && currentRoom.MaxPlayers < b) { currentRoom.MaxPlayers = b; ManualLogSource obj = log; if (obj != null) { obj.LogDebug((object)$"Open room cap restored to {b}."); } } else if (!currentRoom.IsOpen && currentRoom.MaxPlayers > b2) { currentRoom.MaxPlayers = b2; ManualLogSource obj2 = log; if (obj2 != null) { obj2.LogInfo((object)$"Closed room cap reduced to {b2} players."); } } } } catch (Exception ex) { ManualLogSource obj3 = log; if (obj3 != null) { obj3.LogWarning((object)("Room cap monitor skipped a tick: " + ex.Message)); } } yield return wait; } } } }