Decompiled source of ScheduleOneMorePlayers v0.1.0

Mods/MorePlayers.dll

Decompiled a day ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using HarmonyLib;
using Il2CppFishNet.Transporting;
using Il2CppFishySteamworks;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.Networking;
using Il2CppScheduleOne.UI.Multiplayer;
using Il2CppSteamworks;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using MorePlayers;
using MorePlayers.Patches;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "MorePlayers", "0.1.0", "darkharasho", "https://github.com/darkharasho/schedule1-more-players")]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: HarmonyDontPatchAll]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MorePlayers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+7ecce7e526fbdc12e28acb3c394835712b444e74")]
[assembly: AssemblyProduct("MorePlayers")]
[assembly: AssemblyTitle("MorePlayers")]
[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 MorePlayers
{
	public class Core : MelonMod
	{
		public const string ModName = "MorePlayers";

		public const string ModVersion = "0.1.0";

		internal const int MaxCap = 250;

		internal static Instance Log;

		internal static MelonPreferences_Entry<int> MaxPlayers;

		internal static int Cap => Math.Clamp(MaxPlayers.Value, 1, 250);

		public override void OnInitializeMelon()
		{
			Log = ((MelonBase)this).LoggerInstance;
			MaxPlayers = MelonPreferences.CreateCategory("MorePlayers", "More Players").CreateEntry<int>("MaxPlayers", 8, "Max players", "Most players allowed in a lobby, including the host. The game's default is 4. Everyone in the lobby needs the mod. Changing it while hosting updates the current lobby.", false, false, (ValueValidator)(object)new ValueRange<int>(1, 250), (string)null);
			((MelonEventBase<LemonAction<int, int>>)(object)MaxPlayers.OnEntryValueChanged).Subscribe((LemonAction<int, int>)delegate
			{
				PlayerCap.ApplyLive();
			}, 0, false);
			try
			{
				((MelonBase)this).HarmonyInstance.PatchAll(typeof(Core).Assembly);
			}
			catch (Exception value)
			{
				Log.Error($"Patching failed, likely a game update; the player cap stays at the game's default. {value}");
				((MelonBase)this).HarmonyInstance.UnpatchSelf();
				return;
			}
			Log.Msg($"{"MorePlayers"} {"0.1.0"} loaded (max players {Cap})");
		}
	}
}
namespace MorePlayers.Patches
{
	internal static class LobbySlots
	{
		private const int CellsPerRow = 5;

		private const int MaxRows = 3;

		private static bool _haveBase;

		private static Vector2 _baseCellSize;

		private static Vector2 _baseSpacing;

		private static int _basePaddingLeft;

		internal static void Ensure(LobbyInterface ui)
		{
			Il2CppReferenceArray<RectTransform> playerSlots = ui.PlayerSlots;
			if (playerSlots == null || ((Il2CppArrayBase<RectTransform>)(object)playerSlots).Length == 0)
			{
				return;
			}
			int num = ((Il2CppArrayBase<RectTransform>)(object)playerSlots).Length;
			int cap = Core.Cap;
			if (num < cap)
			{
				RectTransform val = ((Il2CppArrayBase<RectTransform>)(object)playerSlots)[num - 1];
				Il2CppReferenceArray<RectTransform> val2 = new Il2CppReferenceArray<RectTransform>((long)cap);
				for (int i = 0; i < num; i++)
				{
					((Il2CppArrayBase<RectTransform>)(object)val2)[i] = ((Il2CppArrayBase<RectTransform>)(object)playerSlots)[i];
				}
				for (int j = num; j < cap; j++)
				{
					GameObject val3 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Transform)val).parent);
					((Object)val3).name = $"Entry ({j})";
					val3.SetActive(false);
					((Il2CppArrayBase<RectTransform>)(object)val2)[j] = val3.GetComponent<RectTransform>();
				}
				ui.PlayerSlots = val2;
				num = cap;
			}
			if (num > 4)
			{
				Layout(((Component)((Transform)((Il2CppArrayBase<RectTransform>)(object)playerSlots)[0]).parent).GetComponent<GridLayoutGroup>(), num + 1);
			}
		}

		private static void Layout(GridLayoutGroup grid, int cells)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)grid == (Object)null))
			{
				if (!_haveBase)
				{
					_baseCellSize = grid.cellSize;
					_baseSpacing = grid.spacing;
					_basePaddingLeft = ((LayoutGroup)grid).padding.left;
					_haveBase = true;
				}
				int num = Math.Max(5, (cells + 3 - 1) / 3);
				float num2 = 5f / (float)num;
				grid.cellSize = _baseCellSize * num2;
				grid.spacing = new Vector2(_baseSpacing.x * num2, _baseSpacing.y);
				((LayoutGroup)grid).padding.left = Mathf.RoundToInt((float)_basePaddingLeft * num2);
				grid.constraint = (Constraint)1;
				grid.constraintCount = num;
				LayoutRebuilder.MarkLayoutForRebuild(((Component)grid).GetComponent<RectTransform>());
			}
		}
	}
	[HarmonyPatch(typeof(LobbyInterface), "Start")]
	internal static class LobbyInterfaceStartPatch
	{
		private static void Postfix(LobbyInterface __instance)
		{
			try
			{
				LobbySlots.Ensure(__instance);
				__instance.UpdateUI();
			}
			catch (Exception e)
			{
				PlayerCap.LogFailureOnce(e);
			}
		}
	}
	[HarmonyPatch(typeof(LobbyInterface), "UpdateUI")]
	internal static class UpdateUIPatch
	{
		private static readonly Regex LimitSuffix = new Regex("/\\d+\\)$");

		private static void Postfix(LobbyInterface __instance)
		{
			try
			{
				TextMeshProUGUI lobbyTitle = __instance.LobbyTitle;
				if ((Object)(object)lobbyTitle != (Object)null)
				{
					((TMP_Text)lobbyTitle).text = LimitSuffix.Replace(((TMP_Text)lobbyTitle).text ?? "", $"/{Core.Cap})");
				}
				InviteButton.Update(__instance);
			}
			catch (Exception e)
			{
				PlayerCap.LogFailureOnce(e);
			}
		}
	}
	[HarmonyPatch(typeof(LobbyInterface), "UpdateButtons")]
	internal static class UpdateButtonsPatch
	{
		private static void Postfix(LobbyInterface __instance)
		{
			try
			{
				InviteButton.Update(__instance);
			}
			catch (Exception e)
			{
				PlayerCap.LogFailureOnce(e);
			}
		}
	}
	internal static class InviteButton
	{
		internal static void Update(LobbyInterface ui)
		{
			Lobby lobby = ui.Lobby;
			if (!((Object)(object)lobby == (Object)null) && !((Object)(object)ui.InviteButton == (Object)null))
			{
				((Component)ui.InviteButton).gameObject.SetActive(lobby.IsHost && lobby.PlayerCount < Core.Cap);
			}
		}
	}
	internal static class PlayerCap
	{
		private static bool _loggedFailure;

		internal static void ApplyLive()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int cap = Core.Cap;
				Lobby val = Object.FindObjectOfType<Lobby>();
				object obj;
				if (val == null)
				{
					obj = null;
				}
				else
				{
					ILobbyService lobbyService = val._lobbyService;
					obj = ((lobbyService != null) ? ((Il2CppObjectBase)lobbyService).TryCast<SteamLobbyService>() : null);
				}
				SteamLobbyService val2 = (SteamLobbyService)obj;
				if (val2 != null && val.IsHost && val2._lobbyID != 0L)
				{
					SteamMatchmaking.SetLobbyMemberLimit(val2._lobbySteamID, cap);
				}
				FishySteamworks val3 = Object.FindObjectOfType<FishySteamworks>();
				if ((Object)(object)val3 != (Object)null && RaiseTransportLimit(val3))
				{
					((Transport)val3).SetMaximumClients(cap);
				}
				LobbyInterface val4 = Object.FindObjectOfType<LobbyInterface>();
				if ((Object)(object)val4 != (Object)null)
				{
					LobbySlots.Ensure(val4);
					val4.UpdateUI();
				}
				Core.Log.Msg($"Max players: {cap}");
			}
			catch (Exception e)
			{
				LogFailureOnce(e);
			}
		}

		internal static bool RaiseTransportLimit(FishySteamworks transport)
		{
			int cap = Core.Cap;
			if (transport._maximumClients >= cap)
			{
				return false;
			}
			transport._maximumClients = (ushort)cap;
			return true;
		}

		internal static void LogFailureOnce(Exception e)
		{
			if (!_loggedFailure)
			{
				_loggedFailure = true;
				Core.Log.Error($"Couldn't apply the player cap, likely a game update; it may stay at the game's default. {e}");
			}
		}
	}
	[HarmonyPatch(typeof(SteamLobbyService), "CreateLobby")]
	internal static class CreateLobbyPatch
	{
		private static void Prefix(ref int maxPlayers)
		{
			maxPlayers = Core.Cap;
		}
	}
	[HarmonyPatch(typeof(SteamLobbyService), "UpdateLobbyMembers")]
	internal static class UpdateLobbyMembersPatch
	{
		private static void Prefix(SteamLobbyService __instance)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = Core.Cap;
				if (__instance._lobbyID != 0L)
				{
					num = Math.Max(num, SteamMatchmaking.GetNumLobbyMembers(__instance._lobbySteamID));
				}
				Il2CppStructArray<CSteamID> players = __instance._players;
				int num2 = ((Il2CppArrayBase<CSteamID>)(object)players)?.Length ?? 0;
				if (num2 < num)
				{
					Il2CppStructArray<CSteamID> val = new Il2CppStructArray<CSteamID>((long)num);
					for (int i = 0; i < num2; i++)
					{
						((Il2CppArrayBase<CSteamID>)(object)val)[i] = ((Il2CppArrayBase<CSteamID>)(object)players)[i];
					}
					__instance._players = val;
				}
			}
			catch (Exception e)
			{
				PlayerCap.LogFailureOnce(e);
			}
		}
	}
	[HarmonyPatch(typeof(SteamLobbyService), "OpenInviteUI")]
	internal static class OpenInviteUIPatch
	{
		private static bool Prefix(SteamLobbyService __instance)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int cap = Core.Cap;
				if (__instance._lobbyID == 0L)
				{
					Core.Log.Msg("Not currently in a lobby, creating one...");
					SteamMatchmaking.CreateLobby((ELobbyType)1, cap);
				}
				if (SteamMatchmaking.GetNumLobbyMembers(__instance._lobbySteamID) >= cap)
				{
					Core.Log.Warning("Lobby already at max capacity!");
					return false;
				}
				SteamFriends.ActivateGameOverlayInviteDialog(__instance._lobbySteamID);
				return false;
			}
			catch (Exception e)
			{
				PlayerCap.LogFailureOnce(e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(FishySteamworks), "StartConnection")]
	internal static class StartConnectionPatch
	{
		private static void Prefix(FishySteamworks __instance, bool server)
		{
			if (!server)
			{
				return;
			}
			try
			{
				PlayerCap.RaiseTransportLimit(__instance);
			}
			catch (Exception e)
			{
				PlayerCap.LogFailureOnce(e);
			}
		}
	}
}