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<AsyncStateMachineAttribute>();
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<CodeInstruction> ReplaceVanillaLobbyLimit(IEnumerable<CodeInstruction> 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<CodeInstruction> list = new List<CodeInstruction>(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<int> maxPlayers;
private static ConfigEntry<bool> patchSteamLobby;
private static ConfigEntry<bool> 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<int>("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<int>(1, 20), Array.Empty<object>()));
patchSteamLobby = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PatchSteamLobby", true, "Also raises the Steam lobby member limit so Steam friends can join expanded rooms.");
shrinkClosedRooms = ((BaseUnityPlugin)this).Config.Bind<bool>("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;
}
}
}
}