Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of ScheduleOneMorePlayers v0.1.0
Mods/MorePlayers.dll
Decompiled a day agousing 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); } } } }