Decompiled source of RepoMorePlayers v0.1.0

BepInEx/plugins/RepoMorePlayers/RepoMorePlayers.dll

Decompiled 2 hours ago
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;
			}
		}
	}
}