Due to update 2.4.3, some mods may no longer function. FixedConfig may be necessary.
Decompiled source of MoreBopl v2.1.1
BepInEx/plugins/MoreBopl.dll
Decompiled 7 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using HarmonyLib.Tools; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyCompany("MoreMultiPlayer")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("My first plugin")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MoreMultiPlayer")] [assembly: AssemblyTitle("MoreMultiPlayer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 MoreMultiPlayer { [HarmonyPatch(typeof(CharacterSelectHandler_online))] [HarmonyPatch("ForceStartGame")] public static class CharacterSelecterHandler_online_ForceStartGamePatch { [HarmonyReversePatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(typeof(CharacterSelectHandler_online), "InitPlayer")] public static Player InitPlayer(int id, byte color, byte team, byte ability1, byte ability2, byte ability3, int nrOfAbilities, PlayerColors playerColors) { throw new NotImplementedException("It's a stub"); } public static bool Prefix([CanBeNull] PlayerColors pcs) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) CharacterSelectHandler_online val = AccessTools.StaticFieldRefAccess<CharacterSelectHandler_online, CharacterSelectHandler_online>("selfRef"); if ((Object)(object)pcs == (Object)null) { pcs = val.playerColors; } MultiStartRequestPacket startParameters = SteamManagerExtended.startParameters; Updater.ReInit(); List<Player> list = new List<Player>(); Updater.InitSeed(startParameters.seed); Plugin.Log.LogInfo((object)$"SteamID: {SteamClient.SteamId}"); for (int i = 0; i < startParameters.nrOfPlayers; i++) { Plugin.Log.LogInfo((object)$"Initializing player {i}: [id: {startParameters.p_ids[i]}, color: {startParameters.p_colors[i]}, team: {startParameters.p_teams[i]}, ability1: {startParameters.p_ability1s[i]}, ability2: {startParameters.p_ability2s[i]}, ability3: {startParameters.p_ability3s[i]}]"); list.Add(InitPlayer(i + 1, startParameters.p_colors[i], startParameters.p_teams[i], startParameters.p_ability1s[i], startParameters.p_ability2s[i], startParameters.p_ability3s[i], startParameters.nrOfAbilites, pcs)); } Player val2 = null; if (GameLobby.isPlayingAReplay) { for (int j = 0; j < list.Count; j++) { if (list[j].Id == 1) { val2 = list[j]; break; } } } else { int num = Array.IndexOf(startParameters.p_ids, SteamId.op_Implicit(SteamClient.SteamId)); Plugin.Log.LogInfo((object)("SteamIdIndex: " + num)); if (num != -1) { val2 = list[num]; } } if (val2 == null) { Plugin.Log.LogError((object)"Failed to find local player on ForceStart."); return false; } for (int k = 0; k < list.Count; k++) { list[k].steamId = SteamId.op_Implicit(startParameters.p_ids[k]); } val2.IsLocalPlayer = true; val2.inputDevice = CharacterSelectHandler_online.localPlayerInit.inputDevice; val2.UsesKeyboardAndMouse = CharacterSelectHandler_online.localPlayerInit.usesKeyboardMouse; val2.CustomKeyBinding = CharacterSelectHandler_online.localPlayerInit.keybindOverride; CharacterSelectHandler_online.startButtonAvailable = false; PlayerHandler.Get().SetPlayerList(list); SteamManager.instance.StartHostedGame(); AudioManager obj = AudioManager.Get(); if (obj != null) { obj.Play("startGame"); } GameSession.Init(); if (GameLobby.isPlayingAReplay) { SceneManager.LoadScene(startParameters.currentLevel + 6); } else { SceneManager.LoadScene("Level1"); } if (!WinnerTriangleCanvas.HasBeenSpawned) { SceneManager.LoadScene("winnerTriangle", (LoadSceneMode)1); } return false; } } [HarmonyPatch(typeof(GameSessionHandler))] [HarmonyPatch("LoadNextLevelScene")] public class GameSessionHandlerPatch_LoadNextLevelScene { [HarmonyTranspiler] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { Plugin.Log.LogInfo((object)"Patching GameSessionHandler.LoadNextLevelScene"); FieldInfo fromA = AccessTools.Field(typeof(SteamManager), "startParameters"); FieldInfo toA = AccessTools.Field(typeof(SteamManagerExtended), "startParameters"); FieldInfo fromB = AccessTools.Field(typeof(StartRequestPacket), "seed"); FieldInfo toB = AccessTools.Field(typeof(MultiStartRequestPacket), "seed"); return Plugin.PatchFieldLoad(fromA, fromB, toA, toB, instructions); } } [HarmonyPatch(typeof(GameSessionHandler))] [HarmonyPatch("SpawnPlayers")] public class GameSessionHandlerPatch_SpawnPlayers { private static void Prefix() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) List<Player> list = PlayerHandler.Get().PlayerList(); Plugin.Log.LogInfo((object)$"Spawning {list.Count} players:"); foreach (Player item in list) { Plugin.Log.LogInfo((object)$"Player {item.Id} ({item.steamId})"); } } } public class HostPatch { public static Queue<MultiInputPacket> InputBuffer = new Queue<MultiInputPacket>(); public static MultiInputPacket previousInputPacket; public static uint GetMaxSeqNumber(MultiInputPacket packet) { return (packet.inputPackets.Count != 0) ? packet.inputPackets.Max((KeyValuePair<int, InputPacket> p) => p.Value.seqNumber) : 0u; } public static int GetMaxPreviousTargetDelayBufferSize() { return (previousInputPacket.inputPackets.Count != 0) ? previousInputPacket.inputPackets.Max((KeyValuePair<int, InputPacket> p) => p.Value.targetDelayBufferSize) : 0; } } [HarmonyPatch(typeof(Host))] [HarmonyPatch("Start")] public class HostPatch_Start { public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { Plugin.Log.LogInfo((object)"Patching Host.Start"); FieldInfo fromA = AccessTools.Field(typeof(SteamManager), "startParameters"); FieldInfo toA = AccessTools.Field(typeof(SteamManagerExtended), "startParameters"); FieldInfo fromB = AccessTools.Field(typeof(StartRequestPacket), "nrOfAbilites"); FieldInfo toB = AccessTools.Field(typeof(MultiStartRequestPacket), "nrOfAbilites"); return Plugin.PatchFieldLoad(fromA, fromB, toA, toB, instructions); } } [HarmonyPatch(typeof(Host))] [HarmonyPatch("ReInit")] public class HostPatch_ReInit { public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { Plugin.Log.LogInfo((object)"Patching Host.ReInit"); FieldInfo fromA = AccessTools.Field(typeof(SteamManager), "startParameters"); FieldInfo toA = AccessTools.Field(typeof(SteamManagerExtended), "startParameters"); FieldInfo fromB = AccessTools.Field(typeof(StartRequestPacket), "frameBufferSize"); FieldInfo toB = AccessTools.Field(typeof(MultiStartRequestPacket), "frameBufferSize"); return Plugin.PatchFieldLoad(fromA, fromB, toA, toB, instructions); } } [HarmonyPatch(typeof(Host))] [HarmonyPatch("Init")] public class HostPatch_Init { public static void Prefix() { Plugin.Log.LogInfo((object)"Host::Init::Prefix"); HostPatch.previousInputPacket = new MultiInputPacket(); } } [HarmonyPatch(typeof(Host))] [HarmonyPatch("ProcessNetworkPackets")] public class HostPatch_ProcessNetworkPackets { private static void AddDefaultPacket() { Plugin.Log.LogInfo((object)"Adding default packet"); HostPatch.InputBuffer.Enqueue(new MultiInputPacket()); } private static void AddInputPacket(List<Client> clients, Queue<InputPacket> sentPacketHistory, int localPlayerId) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) MultiInputPacket item = new MultiInputPacket(); foreach (Client client in clients) { InputPacket value = client.inputHistory.Dequeue(); item.inputPackets.Add(client.PlayerId, value); } item.inputPackets.Add(localPlayerId, sentPacketHistory.Dequeue()); HostPatch.InputBuffer.Enqueue(item); } private static IEnumerable<CodeInstruction> DebugLogInstructions(string message) { yield return new CodeInstruction(OpCodes.Ldstr, (object)message); yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(HostPatch_ProcessNetworkPackets), "LogText", (Type[])null, (Type[])null)); } private static void LogText(string message) { Plugin.Log.LogInfo((object)message); } [HarmonyDebug] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { Plugin.Log.LogInfo((object)"Patching Host.ProcessNetworkPackets"); FieldInfo delayBufferSizeFieldInfo = AccessTools.Field(typeof(Host), "delayBufferSize"); FieldInfo updatesPlacedInDelayBufferFieldInfo = AccessTools.Field(typeof(Host), "UpdatesPlacedInDelayBuffer"); IEnumerator<CodeInstruction> enumerator = instructions.GetEnumerator(); Label? last_br = null; Label? blabel = default(Label?); while (enumerator.MoveNext()) { CodeInstruction instruction = enumerator.Current; if (CodeInstructionExtensions.Branches(instruction, ref blabel)) { last_br = blabel; } if (instruction.opcode == OpCodes.Ldloca_S) { CodeInstruction loopStart = instruction; List<Label> labels = CodeInstructionExtensions.ExtractLabels(loopStart); List<CodeInstruction> instructionsSkipped = new List<CodeInstruction> { loopStart }; if (labels.Count == 0) { yield return loopStart; continue; } if (!enumerator.MoveNext()) { Plugin.Log.LogError((object)"Expected to find next instruction after Ldloca_S"); yield return loopStart; yield break; } CodeInstruction nextInstruction = enumerator.Current; instructionsSkipped.Add(nextInstruction); if (CodeInstructionExtensions.Is(nextInstruction, OpCodes.Initobj, (MemberInfo)typeof(InputPacketQuad))) { Plugin.Log.LogInfo((object)"Found possible start of loop. Searching for end..."); bool foundEnd = false; while (enumerator.MoveNext()) { CodeInstruction instructionA = enumerator.Current; instructionsSkipped.Add(instructionA); if (instructionA.opcode == OpCodes.Blt && labels.Contains((Label)instructionA.operand)) { Plugin.Log.LogInfo((object)"Found End of loop"); foundEnd = true; break; } } if (foundEnd) { if (!last_br.HasValue) { Plugin.Log.LogError((object)"What this shouldn't be null?!"); } if (instructionsSkipped.Any((CodeInstruction i) => CodeInstructionExtensions.LoadsField(i, delayBufferSizeFieldInfo, false))) { Plugin.Log.LogInfo((object)"Found 'fill InputBuffer with default InputPacket' loop"); Label label2 = generator.DefineLabel(); yield return CodeInstructionExtensions.WithLabels(CodeInstruction.Call(typeof(HostPatch_ProcessNetworkPackets), "AddDefaultPacket", (Type[])null, (Type[])null), new Label[1] { label2 }); yield return new CodeInstruction(OpCodes.Ldloc_3, (object)null); yield return new CodeInstruction(OpCodes.Ldc_I4_1, (object)null); yield return new CodeInstruction(OpCodes.Add, (object)null); yield return new CodeInstruction(OpCodes.Stloc_3, (object)null); yield return CodeInstructionExtensions.WithLabels(new CodeInstruction(OpCodes.Ldloc_3, (object)null), new Label[1] { last_br.Value }); yield return new CodeInstruction(OpCodes.Ldsfld, (object)AccessTools.Field(typeof(Host), "delayBufferSize")); yield return new CodeInstruction(OpCodes.Blt_S, (object)label2); } else if (instructionsSkipped.Any((CodeInstruction i) => CodeInstructionExtensions.LoadsField(i, updatesPlacedInDelayBufferFieldInfo, false))) { object local_i = instructionsSkipped[instructionsSkipped.Count - 3].operand; object local_m = instructionsSkipped[instructionsSkipped.Count - 2].operand; Plugin.Log.LogInfo((object)$"Local_i: {local_i}"); Plugin.Log.LogInfo((object)$"Local_m: {local_m}"); Plugin.Log.LogInfo((object)"Found InputPacket creation loop"); Label label = generator.DefineLabel(); yield return CodeInstructionExtensions.WithLabels(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new Label[1] { label }); yield return new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Host), "clients")); yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Host), "sentPacketHistory")); yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Host), "localPlayerId")); yield return CodeInstruction.Call(typeof(HostPatch_ProcessNetworkPackets), "AddInputPacket", (Type[])null, (Type[])null); yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Host), "UpdatesPlacedInDelayBuffer")); yield return new CodeInstruction(OpCodes.Ldc_I4_1, (object)null); yield return new CodeInstruction(OpCodes.Add, (object)null); yield return new CodeInstruction(OpCodes.Stfld, (object)AccessTools.Field(typeof(Host), "UpdatesPlacedInDelayBuffer")); yield return new CodeInstruction(OpCodes.Ldloc_S, local_i); yield return new CodeInstruction(OpCodes.Ldc_I4_1, (object)null); yield return new CodeInstruction(OpCodes.Add, (object)null); yield return new CodeInstruction(OpCodes.Stloc_S, local_i); yield return CodeInstructionExtensions.WithLabels(new CodeInstruction(OpCodes.Ldloc_S, local_i), new Label[1] { last_br.Value }); yield return new CodeInstruction(OpCodes.Ldloc_S, local_m); yield return new CodeInstruction(OpCodes.Blt_S, (object)label); } else { instructionsSkipped[instructionsSkipped.Count - 7].labels.Add((Label)instructionsSkipped[instructionsSkipped.Count - 1].operand); for (int j = instructionsSkipped.Count - 7; j < instructionsSkipped.Count; j++) { yield return instructionsSkipped[j]; } } continue; } foreach (CodeInstruction item in instructionsSkipped) { yield return item; } yield break; } foreach (CodeInstruction item2 in instructionsSkipped) { yield return item2; } } else { yield return instruction; blabel = null; } } enumerator.Dispose(); } } [HarmonyPatch(typeof(Host))] [HarmonyPatch("Update")] public class HostPatch_Update { public static int GetCount() { return HostPatch.InputBuffer.Count; } public static MultiInputPacket Dequeue() { return HostPatch.InputBuffer.Dequeue(); } public static int GetTargetDelayBufferSize(MultiInputPacket packet, int currentTargetDelayBufferSize) { if (HostPatch.GetMaxSeqNumber(packet) % 32 == 0) { int maxPreviousTargetDelayBufferSize = HostPatch.GetMaxPreviousTargetDelayBufferSize(); if (maxPreviousTargetDelayBufferSize > 0) { return maxPreviousTargetDelayBufferSize; } } return currentTargetDelayBufferSize; } public static void OverrideInputWithNetworkInput(MultiInputPacket packet) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) PlayerHandler val = PlayerHandler.Get(); List<Player> list = val.PlayerList(); for (int i = 0; i < list.Count; i++) { if (packet.inputPackets.TryGetValue(list[i].Id, out var value)) { list[i].OverrideInputWithNetworkInput(value); } } HostPatch.previousInputPacket = packet; } public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { Plugin.Log.LogInfo((object)"Patching Host.Update"); FieldInfo inputBufferFieldInfo = AccessTools.Field(typeof(Host), "InputBuffer"); MethodInfo getCountMethodInfo = AccessTools.Method(inputBufferFieldInfo.FieldType, "get_Count", (Type[])null, (Type[])null); MethodInfo dequeueMethodInfo = AccessTools.Method(inputBufferFieldInfo.FieldType, "Dequeue", (Type[])null, (Type[])null); IEnumerator<CodeInstruction> enumerator = instructions.GetEnumerator(); LocalBuilder mipl = generator.DeclareLocal(typeof(MultiInputPacket)); bool foundInputBuffer = false; Label? label = default(Label?); while (enumerator.MoveNext()) { CodeInstruction instruction5 = enumerator.Current; if (CodeInstructionExtensions.LoadsField(instruction5, inputBufferFieldInfo, false)) { Plugin.Log.LogInfo((object)"Found InputBuffer field load instruction"); foundInputBuffer = true; if (!enumerator.MoveNext()) { Plugin.Log.LogError((object)"Expected to find next instruction after InputBuffer field load instruction"); yield return instruction5; continue; } instruction5 = enumerator.Current; if (CodeInstructionExtensions.Calls(instruction5, getCountMethodInfo)) { Plugin.Log.LogInfo((object)"Patching InputBuffer.getCount"); yield return new CodeInstruction(OpCodes.Pop, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(HostPatch_Update), "GetCount", (Type[])null, (Type[])null)); continue; } if (CodeInstructionExtensions.Calls(instruction5, dequeueMethodInfo)) { Plugin.Log.LogInfo((object)"Patching InputBuffer.Dequeue"); yield return new CodeInstruction(OpCodes.Pop, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(HostPatch_Update), "Dequeue", (Type[])null, (Type[])null)); if (!enumerator.MoveNext() && enumerator.Current.opcode == OpCodes.Stloc_S) { Plugin.Log.LogError((object)"Expected a store local instruction."); break; } yield return new CodeInstruction(OpCodes.Stloc, (object)mipl); while (enumerator.MoveNext()) { instruction5 = enumerator.Current; if (!(instruction5.opcode == OpCodes.Ldarg_0)) { continue; } if (!enumerator.MoveNext()) { Plugin.Log.LogError((object)"Expected to find next instruction after Ldarg_0"); yield return instruction5; continue; } instruction5 = enumerator.Current; if (!CodeInstructionExtensions.LoadsField(instruction5, AccessTools.Field(typeof(Host), "AdaptiveInputDelayBuffer"), false)) { continue; } Plugin.Log.LogInfo((object)"Found AdaptiveInputDelayBuffer check"); yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return instruction5; if (!enumerator.MoveNext()) { Plugin.Log.LogError((object)"Invalid input instruction stream."); continue; } instruction5 = enumerator.Current; yield return instruction5; if (!CodeInstructionExtensions.Branches(instruction5, ref label)) { Plugin.Log.LogError((object)"Assumption about branch after AdaptiveInputDelayBuffer was incorrect."); continue; } while (enumerator.MoveNext()) { instruction5 = enumerator.Current; if (CodeInstructionExtensions.StoresField(instruction5, AccessTools.Field(typeof(Host), "targetDelayBufferSize"))) { break; } } yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Ldloc, (object)mipl); yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Host), "targetDelayBufferSize")); yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(HostPatch_Update), "GetTargetDelayBufferSize", (Type[])null, (Type[])null)); yield return new CodeInstruction(OpCodes.Stfld, (object)AccessTools.Field(typeof(Host), "targetDelayBufferSize")); break; } continue; } Plugin.Log.LogError((object)"Failed to patch InputBuffer reference out."); } if (foundInputBuffer && CodeInstructionExtensions.Calls(instruction5, AccessTools.Method(typeof(PlayerHandler), "Get", (Type[])null, (Type[])null))) { Plugin.Log.LogInfo((object)"Patching PlayerHandler.Get calls"); while (enumerator.MoveNext() && !CodeInstructionExtensions.StoresField(enumerator.Current, AccessTools.Field(typeof(Host), "previousInputQuad"))) { } if (enumerator.Current == null) { Plugin.Log.LogError((object)"Failed to find previousInputQuad store"); break; } yield return new CodeInstruction(OpCodes.Ldloc, (object)mipl); yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(HostPatch_Update), "OverrideInputWithNetworkInput", (Type[])null, (Type[])null)); } else { yield return instruction5; } } } } public struct MultiInputPacket { public Dictionary<int, InputPacket> inputPackets; public MultiInputPacket() { inputPackets = new Dictionary<int, InputPacket>(); } public void Log() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)$"MultiInputPacket: {inputPackets.Count} input packets"); foreach (KeyValuePair<int, InputPacket> inputPacket in inputPackets) { Plugin.Log.LogInfo((object)$"Player {inputPacket.Key}:"); Plugin.Log.LogInfo((object)$"seqNumber: {inputPacket.Value.seqNumber}"); Plugin.Log.LogInfo((object)$"joystickAngle: {inputPacket.Value.joystickAngle}"); Plugin.Log.LogInfo((object)$"jump: {inputPacket.Value.jump}"); Plugin.Log.LogInfo((object)$"ab1: {inputPacket.Value.ab1}"); Plugin.Log.LogInfo((object)$"ab2: {inputPacket.Value.ab2}"); Plugin.Log.LogInfo((object)$"ab3: {inputPacket.Value.ab3}"); Plugin.Log.LogInfo((object)$"start: {inputPacket.Value.start}"); Plugin.Log.LogInfo((object)$"select: {inputPacket.Value.select}"); Plugin.Log.LogInfo((object)$"w: {inputPacket.Value.w}"); Plugin.Log.LogInfo((object)$"a: {inputPacket.Value.a}"); Plugin.Log.LogInfo((object)$"s: {inputPacket.Value.s}"); Plugin.Log.LogInfo((object)$"d: {inputPacket.Value.d}"); Plugin.Log.LogInfo((object)$"targetDelayBufferSize: {inputPacket.Value.targetDelayBufferSize}"); } } } public struct MultiStartRequestPacket { public ushort seqNum; public uint seed; public byte nrOfPlayers; public byte nrOfAbilites; public byte currentLevel; public byte frameBufferSize; public byte isDemoMask; public ulong[] p_ids; public byte[] p_colors; public byte[] p_teams; public byte[] p_ability1s; public byte[] p_ability2s; public byte[] p_ability3s; public void Initialize(int count) { p_ids = new ulong[count]; p_colors = new byte[count]; p_teams = new byte[count]; p_ability1s = new byte[count]; p_ability2s = new byte[count]; p_ability3s = new byte[count]; } public override string ToString() { return string.Format("seqNum: {0}, seed: {1}, nrOfPlayers: {2}, nrOfAbilites: {3}, currentLevel: {4}, frameBufferSize: {5}, isDemoMask: {6}, p_ids: {7}, p_colors: {8}, p_teams: {9}, p_ability1s: {10}, p_ability2s: {11}, p_ability3s: {12}", seqNum, seed, nrOfPlayers, nrOfAbilites, currentLevel, frameBufferSize, isDemoMask, string.Join(", ", p_ids), string.Join(", ", p_colors), string.Join(", ", p_teams), string.Join(", ", p_ability1s), string.Join(", ", p_ability2s), string.Join(", ", p_ability3s)); } } public static class NetworkToolsExtensions { public static MultiStartRequestPacket ReadMultiStartRequest(byte[] data, ref byte[] uintConversionHelperArray, ref byte[] ulongConversionHelperArray, ref byte[] ushortConversionHelperArray) { Plugin.Log.LogInfo((object)$"Decoding MultiStartRequestPacket, size of array: {data.Length}"); MultiStartRequestPacket result = default(MultiStartRequestPacket); int num = 0; ushortConversionHelperArray[0] = data[num++]; ushortConversionHelperArray[1] = data[num++]; result.seqNum = NetworkTools.SwapBytesIfLittleEndian(BitConverter.ToUInt16(ushortConversionHelperArray, 0)); Plugin.Log.LogInfo((object)$"Decoded seqNum: {result.seqNum} (byte: {num})"); uintConversionHelperArray[0] = data[num++]; uintConversionHelperArray[1] = data[num++]; uintConversionHelperArray[2] = data[num++]; uintConversionHelperArray[3] = data[num++]; result.seed = NetworkTools.SwapBytesIfLittleEndian(BitConverter.ToUInt32(uintConversionHelperArray, 0)); Plugin.Log.LogInfo((object)$"Decoded seed: {result.seed} (byte: {num})"); result.nrOfPlayers = data[num++]; result.nrOfAbilites = data[num++]; result.currentLevel = data[num++]; result.frameBufferSize = data[num++]; result.isDemoMask = data[num++]; Plugin.Log.LogInfo((object)$"Decoded nrOfPlayers: {result.nrOfPlayers}, nrOfAbilities: {result.nrOfAbilites}, currentLevel: {result.currentLevel}, frameBufferSize: {result.frameBufferSize}, isDemoMask: {result.isDemoMask} (byte: {num})"); result.Initialize(result.nrOfPlayers); for (int i = 0; i < result.nrOfPlayers; i++) { ulongConversionHelperArray[0] = data[num++]; ulongConversionHelperArray[1] = data[num++]; ulongConversionHelperArray[2] = data[num++]; ulongConversionHelperArray[3] = data[num++]; ulongConversionHelperArray[4] = data[num++]; ulongConversionHelperArray[5] = data[num++]; ulongConversionHelperArray[6] = data[num++]; ulongConversionHelperArray[7] = data[num++]; result.p_ids[i] = NetworkTools.SwapBytesIfLittleEndian(BitConverter.ToUInt64(ulongConversionHelperArray, 0)); Plugin.Log.LogInfo((object)$"Decoded p_ids[{i}]: {result.p_ids[i]} (byte: {num})"); } for (int j = 0; j < result.nrOfPlayers; j++) { result.p_colors[j] = data[num++]; Plugin.Log.LogInfo((object)$"Decoded p_colors[{j}]: {result.p_colors[j]} (byte: {num})"); } for (int k = 0; k < result.nrOfPlayers; k++) { result.p_teams[k] = data[num++]; Plugin.Log.LogInfo((object)$"Decoded p_teams[{k}]: {result.p_teams[k]} (byte: {num})"); } for (int l = 0; l < result.nrOfPlayers; l++) { result.p_ability1s[l] = data[num++]; Plugin.Log.LogInfo((object)$"Decoded p_ability1s[{l}]: {result.p_ability1s[l]} (byte: {num})"); } for (int m = 0; m < result.nrOfPlayers; m++) { result.p_ability2s[m] = data[num++]; Plugin.Log.LogInfo((object)$"Decoded p_ability2s[{m}]: {result.p_ability2s[m]} (byte: {num})"); } for (int n = 0; n < result.nrOfPlayers; n++) { result.p_ability3s[n] = data[num++]; Plugin.Log.LogInfo((object)$"Decoded p_ability3s[{n}]: {result.p_ability3s[n]} (byte: {num})"); } return result; } public static void EncodeMultiStartRequest(ref byte[] data, MultiStartRequestPacket p) { Plugin.Log.LogInfo((object)$"Encoding MultiStartRequestPacket, size of array: {data.Length}"); int num = 0; Plugin.Log.LogInfo((object)$"Encoding seqNum: {p.seqNum} (byte: {num})"); p.seqNum = NetworkTools.SwapBytesIfLittleEndian(p.seqNum); byte[] bytes = BitConverter.GetBytes(p.seqNum); data[num++] = bytes[0]; data[num++] = bytes[1]; Plugin.Log.LogInfo((object)$"Encoding seed: {p.seed} (byte: {num})"); p.seed = NetworkTools.SwapBytesIfLittleEndian(p.seed); bytes = BitConverter.GetBytes(p.seed); data[num++] = bytes[0]; data[num++] = bytes[1]; data[num++] = bytes[2]; data[num++] = bytes[3]; data[num++] = p.nrOfPlayers; data[num++] = p.nrOfAbilites; data[num++] = p.currentLevel; data[num++] = p.frameBufferSize; data[num++] = p.isDemoMask; Plugin.Log.LogInfo((object)$"Encoded nrOfPlayers: {p.nrOfPlayers}, nrOfAbilities: {p.nrOfAbilites}, currentLevel: {p.currentLevel}, frameBufferSize: {p.frameBufferSize}, isDemoMask: {p.isDemoMask} (byte: {num})"); for (int i = 0; i < p.nrOfPlayers; i++) { Plugin.Log.LogInfo((object)$"Encoding p_ids[{i}]: {p.p_ids[i]} (byte: {num})"); ulong value = NetworkTools.SwapBytesIfLittleEndian(p.p_ids[i]); bytes = BitConverter.GetBytes(value); data[num++] = bytes[0]; data[num++] = bytes[1]; data[num++] = bytes[2]; data[num++] = bytes[3]; data[num++] = bytes[4]; data[num++] = bytes[5]; data[num++] = bytes[6]; data[num++] = bytes[7]; } for (int j = 0; j < p.nrOfPlayers; j++) { data[num++] = p.p_colors[j]; Plugin.Log.LogInfo((object)$"Encoded p_colors[{j}]: {p.p_colors[j]} (byte: {num})"); } for (int k = 0; k < p.nrOfPlayers; k++) { data[num++] = p.p_teams[k]; Plugin.Log.LogInfo((object)$"Encoded p_teams[{k}]: {p.p_teams[k]} (byte: {num})"); } for (int l = 0; l < p.nrOfPlayers; l++) { data[num++] = p.p_ability1s[l]; Plugin.Log.LogInfo((object)$"Encoded p_ability1s[{l}]: {p.p_ability1s[l]} (byte: {num})"); } for (int m = 0; m < p.nrOfPlayers; m++) { data[num++] = p.p_ability2s[m]; Plugin.Log.LogInfo((object)$"Encoded p_ability2s[{m}]: {p.p_ability2s[m]} (byte: {num})"); } for (int n = 0; n < p.nrOfPlayers; n++) { data[num++] = p.p_ability3s[n]; Plugin.Log.LogInfo((object)$"Encoded p_ability3s[{n}]: {p.p_ability3s[n]} (byte: {num})"); } } public static int GetMultiStartRequestSize(MultiStartRequestPacket startParameters) { return 11 + startParameters.nrOfPlayers * 13; } } [HarmonyPatch(typeof(NetworkTools))] [HarmonyPatch("ReadLobbyReadyPacket")] public static class NetworkTools_ReadLobbyReadyPacket { private static void Postfix(ref LobbyReadyPacket __result) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) Debug.Log((object)"ReadLobbyReadyPacket"); Debug.Log((object)__result.steamId); Debug.Log((object)__result.ability1); Debug.Log((object)__result.ability2); Debug.Log((object)__result.ability3); Debug.Log((object)__result.color); Debug.Log((object)__result.team); Debug.Log((object)__result.ownsFullGame); Debug.Log((object)__result.usesKeyboardAndMouse); Debug.Log((object)"Connected Players"); foreach (SteamConnection connectedPlayer in SteamManager.instance.connectedPlayers) { Debug.Log((object)connectedPlayer.id); } } } [BepInPlugin("MoreMultiPlayer", "MoreMultiPlayer", "1.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; private Harmony harmony; private static IEnumerable<CodeInstruction> SteamManagerCreateFriendLobbyPatch(IEnumerable<CodeInstruction> instructions) { foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.LoadsConstant(instruction, 4L)) { Log.LogMessage((object)$"Found create lobby instruction to patch from 4 to {Constants.MAX_PLAYERS}"); yield return new CodeInstruction(OpCodes.Ldc_I4, (object)Constants.MAX_PLAYERS); } else { yield return instruction; } } } public static IEnumerable<CodeInstruction> PatchFieldLoad(FieldInfo fromA, FieldInfo fromB, FieldInfo toA, FieldInfo toB, IEnumerable<CodeInstruction> instructions) { bool patched = false; IEnumerator<CodeInstruction> enumerator = instructions.GetEnumerator(); while (enumerator.MoveNext()) { CodeInstruction instruction = enumerator.Current; if (CodeInstructionExtensions.LoadsField(instruction, fromA, true)) { if (!enumerator.MoveNext()) { Log.LogError((object)$"Expected to find next instruction after {fromA} load instruction"); yield return instruction; } CodeInstruction nextInstruction = enumerator.Current; if (!CodeInstructionExtensions.LoadsField(nextInstruction, fromB, false)) { Log.LogInfo((object)$"Candidate patch instruction is not loading {fromB} field was: {nextInstruction.opcode}:{nextInstruction.operand}"); yield return instruction; yield return nextInstruction; } else { Log.LogInfo((object)$"Found {fromA} field load instruction to patch"); Log.LogInfo((object)$"Found {fromB} field load instruction to patch"); yield return new CodeInstruction(instruction.opcode, (object)toA); yield return new CodeInstruction(nextInstruction.opcode, (object)toB); patched = true; } } else { yield return instruction; } } if (!patched) { Log.LogError((object)"Failed to patch GameSessionHandler.LoadNextLevelScene"); } } private void Test() { SteamManagerExtended.startParameters = default(MultiStartRequestPacket); SteamManagerExtended.startParameters.seqNum = 5; SteamManagerExtended.startParameters.nrOfPlayers = 2; SteamManagerExtended.startParameters.nrOfAbilites = (byte)Settings.Get().NumberOfAbilities; SteamManagerExtended.startParameters.currentLevel = GameSession.CurrentLevel(); SteamManagerExtended.startParameters.seed = (uint)Environment.TickCount; SteamManagerExtended.startParameters.Initialize(1); SteamManagerExtended.startParameters.p_ids[0] = 10uL; SteamManagerExtended.startParameters.p_teams[0] = 1; SteamManagerExtended.startParameters.p_colors[0] = 2; SteamManagerExtended.startParameters.p_ability1s[0] = 3; SteamManagerExtended.startParameters.p_ability2s[0] = 4; SteamManagerExtended.startParameters.p_ability3s[0] = 5; for (int i = 1; i < 2; i++) { SteamManagerExtended.startParameters.p_ids[i] = (byte)i; SteamManagerExtended.startParameters.p_teams[i] = (byte)i; SteamManagerExtended.startParameters.p_colors[i] = (byte)i; SteamManagerExtended.startParameters.p_ability1s[i] = 4; SteamManagerExtended.startParameters.p_ability2s[i] = 24; SteamManagerExtended.startParameters.p_ability3s[i] = 56; } byte b = (byte)(SteamManager.instance.dlc.HasDLC() ? 1u : 0u); for (int j = 0; j < 1; j++) { bool flag = true; b = (byte)(b | (1 << j + 1)); } SteamManagerExtended.startParameters.isDemoMask = b; byte[] data = new byte[NetworkToolsExtensions.GetMultiStartRequestSize(SteamManagerExtended.startParameters)]; NetworkToolsExtensions.EncodeMultiStartRequest(ref data, SteamManagerExtended.startParameters); } private void Awake() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"secret number 5"); HarmonyFileLog.Enabled = true; Log.LogInfo((object)HarmonyFileLog.FileWriterPath); Constants.MAX_PLAYERS = 8; Host.recordReplay = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Disabled replay recording"); harmony = new Harmony("MoreMultiPlayer"); harmony.PatchAll(); MethodInfo method = typeof(SteamManager).GetMethod("CreateFriendLobby", BindingFlags.Instance | BindingFlags.Public); if (method == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Failed to find SteamManager::CreateFriendLobby!"); return; } AsyncStateMachineAttribute customAttribute = method.GetCustomAttribute<AsyncStateMachineAttribute>(); MethodInfo method2 = customAttribute.StateMachineType.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.NonPublic); MethodInfo method3 = typeof(Plugin).GetMethod("SteamManagerCreateFriendLobbyPatch", BindingFlags.Static | BindingFlags.NonPublic); PatchProcessor val = harmony.CreateProcessor((MethodBase)method2); val.AddTranspiler(method3); val.Patch(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin MoreMultiPlayer is loaded!"); } private void OnDestroy() { harmony.UnpatchSelf(); } } [HarmonyPatch(typeof(printText))] [HarmonyPatch("Awake")] public static class YourPatchClass { public static void Postfix() { Plugin.Log.LogInfo((object)("Found version " + Constants.version)); Constants.version += "-mpmodded"; Plugin.Log.LogInfo((object)("Patched to version " + Constants.version)); } } internal class SteamManagerExtended { public static MultiStartRequestPacket startParameters; } [HarmonyPatch(typeof(SteamManager))] [HarmonyPatch("HostGame")] internal static class SteamManagerPatch_HostGame { private static FieldRef<SteamManager, ushort> nextStartGameSeqAccess = AccessTools.FieldRefAccess<SteamManager, ushort>("nextStartGameSeq"); public static bool Prefix(SteamManager __instance, PlayerInit hostPlayer) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)"Running custom HostGame patch"); ((Lobby)(ref __instance.currentLobby)).SetData("LFM", "0"); ((Lobby)(ref __instance.currentLobby)).SetFriendsOnly(); ((Lobby)(ref __instance.currentLobby)).SetJoinable(false); SteamManagerExtended.startParameters = default(MultiStartRequestPacket); SteamManagerExtended.startParameters.seqNum = nextStartGameSeqAccess.Invoke(__instance)++; SteamManagerExtended.startParameters.nrOfPlayers = (byte)(__instance.connectedPlayers.Count + 1); SteamManagerExtended.startParameters.nrOfAbilites = (byte)Settings.Get().NumberOfAbilities; SteamManagerExtended.startParameters.currentLevel = GameSession.CurrentLevel(); SteamManagerExtended.startParameters.seed = (uint)Environment.TickCount; SteamManagerExtended.startParameters.Initialize(__instance.connectedPlayers.Count + 1); ManualLogSource log = Plugin.Log; SteamId steamId = SteamClient.SteamId; log.LogInfo((object)("My SteamID: " + ((object)(SteamId)(ref steamId)).ToString())); SteamManagerExtended.startParameters.p_ids[0] = SteamId.op_Implicit(SteamClient.SteamId); SteamManagerExtended.startParameters.p_teams[0] = (byte)hostPlayer.team; SteamManagerExtended.startParameters.p_colors[0] = (byte)hostPlayer.color; SteamManagerExtended.startParameters.p_ability1s[0] = (byte)hostPlayer.ability0; SteamManagerExtended.startParameters.p_ability2s[0] = (byte)hostPlayer.ability1; SteamManagerExtended.startParameters.p_ability3s[0] = (byte)hostPlayer.ability2; for (int i = 1; i < SteamManagerExtended.startParameters.nrOfPlayers; i++) { Plugin.Log.LogInfo((object)$"{i} <? {__instance.connectedPlayers.Count}"); SteamManagerExtended.startParameters.p_ids[i] = SteamId.op_Implicit(__instance.connectedPlayers[i - 1].id); SteamManagerExtended.startParameters.p_teams[i] = __instance.connectedPlayers[i - 1].lobby_team; SteamManagerExtended.startParameters.p_colors[i] = (byte)__instance.connectedPlayers[i - 1].lobby_color; SteamManagerExtended.startParameters.p_ability1s[i] = __instance.connectedPlayers[i - 1].lobby_ability1; SteamManagerExtended.startParameters.p_ability2s[i] = __instance.connectedPlayers[i - 1].lobby_ability2; SteamManagerExtended.startParameters.p_ability3s[i] = __instance.connectedPlayers[i - 1].lobby_ability3; } byte b = (byte)(SteamManager.instance.dlc.HasDLC() ? 1u : 0u); for (int j = 0; j < __instance.connectedPlayers.Count; j++) { if (__instance.connectedPlayers[j].ownsFullGame) { b = (byte)(b | (1 << j + 1)); } } SteamManagerExtended.startParameters.isDemoMask = b; byte[] data = new byte[NetworkToolsExtensions.GetMultiStartRequestSize(SteamManagerExtended.startParameters)]; Plugin.Log.LogInfo((object)SteamManagerExtended.startParameters.ToString()); NetworkToolsExtensions.EncodeMultiStartRequest(ref data, SteamManagerExtended.startParameters); for (int k = 0; k < __instance.connectedPlayers.Count; k++) { ((Connection)(ref ((ConnectionManager)__instance.connectedPlayers[k]).Connection)).SendMessage(data, (SendType)8); } return false; } } [HarmonyPatch(typeof(SteamManager))] [HarmonyPatch("ForceLoadNextLevel")] internal static class SteamManagerPatch_ForceLoadNextLevel { [HarmonyReversePatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(typeof(SteamManager), "ChangePlayerAbilites")] public static void ChangePlayerAbilities(Player player, byte ability1, byte ability2, byte ability3, int nrOfAbilities, NamedSpriteList abilityIcons) { throw new NotImplementedException("It's a stub"); } public static bool Prefix() { Plugin.Log.LogInfo((object)"Running custom ForceLoadNextLevel patch"); SteamManager.networkClientHandle.DeInit(); MultiStartRequestPacket startParameters = SteamManagerExtended.startParameters; List<Player> list = PlayerHandler.Get().PlayerList(); if (startParameters.nrOfPlayers <= 1) { GameSessionHandler.LeaveGame(true, false); return false; } int num; for (num = list.Count - 1; num >= 0; num--) { if (!list[num].IsLocalPlayer && !SteamManager.instance.connectedPlayers.Any((SteamConnection player) => SteamId.op_Implicit(player.id) == SteamId.op_Implicit(list[num].steamId))) { list.RemoveAt(num); Debug.Log((object)"dropping player because they were disconnected"); } } for (int i = 0; i < list.Count; i++) { ChangePlayerAbilities(list[i], startParameters.p_ability1s[i], startParameters.p_ability2s[i], startParameters.p_ability3s[i], startParameters.nrOfAbilites, SteamManager.instance.abilityIcons); } GameSession.SetCurrentLevel(startParameters.currentLevel); if (!GameSessionHandler.GameIsPaused) { CharacterStatsList.ForceNextLevelImmediately(); } else { CharacterStatsList.ForceNextLevelLoad(); } return false; } } [HarmonyPatch(typeof(SteamManager))] [HarmonyPatch("InitNetworkClient")] internal static class SteamManagerPatch_InitNetworkClient { private static bool Prefix(SteamManager __instance) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown Plugin.Log.LogInfo((object)"Running custom InitNetworkClient patch"); int num = Array.IndexOf(SteamManagerExtended.startParameters.p_ids, SteamClient.SteamId.Value) + 1; List<Client> list = new List<Client>(); foreach (SteamConnection connectedPlayer in __instance.connectedPlayers) { ulong value = connectedPlayer.id.Value; int num2 = Array.IndexOf(SteamManagerExtended.startParameters.p_ids, value) + 1; list.Add(new Client(num2, connectedPlayer)); } __instance.networkClient.Init(list, num, SteamManagerExtended.startParameters.currentLevel, __instance.startFrameBuffer, __instance.checkForDesyncs); SteamManager.networkClientHandle = __instance.networkClient; return false; } } [HarmonyPatch(typeof(SteamManager))] [HarmonyPatch("HostNextLevel")] internal static class SteamManagerPatch_HostNextLevel { private static bool Prefix(SteamManager __instance, Player hostPlayer, NamedSpriteList abilityIcons) { //IL_01a1: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)"Running custom HostNextLevel patch"); GameSession.CurrentLevel(); SteamManagerExtended.startParameters.frameBufferSize = (byte)Host.CurrentDelayBufferSize; SteamManagerExtended.startParameters.seed = (uint)Environment.TickCount; SteamManagerExtended.startParameters.nrOfPlayers = (byte)(__instance.connectedPlayers.Count + 1); SteamManagerExtended.startParameters.currentLevel = GameSession.CurrentLevel(); SteamManagerExtended.startParameters.p_ability1s[0] = (byte)abilityIcons.IndexOf(((Object)hostPlayer.Abilities[0]).name); if (Settings.Get().NumberOfAbilities > 1) { SteamManagerExtended.startParameters.p_ability2s[0] = (byte)abilityIcons.IndexOf(((Object)hostPlayer.Abilities[1]).name); } if (Settings.Get().NumberOfAbilities > 2) { SteamManagerExtended.startParameters.p_ability3s[0] = (byte)abilityIcons.IndexOf(((Object)hostPlayer.Abilities[2]).name); } for (int i = 0; i < __instance.connectedPlayers.Count; i++) { SteamManagerExtended.startParameters.p_ability1s[i + 1] = __instance.connectedPlayers[i].lobby_ability1; SteamManagerExtended.startParameters.p_ability2s[i + 1] = __instance.connectedPlayers[i].lobby_ability2; SteamManagerExtended.startParameters.p_ability3s[i + 1] = __instance.connectedPlayers[i].lobby_ability3; } byte[] data = new byte[NetworkToolsExtensions.GetMultiStartRequestSize(SteamManagerExtended.startParameters)]; NetworkToolsExtensions.EncodeMultiStartRequest(ref data, SteamManagerExtended.startParameters); for (int j = 0; j < __instance.connectedPlayers.Count; j++) { ((Connection)(ref ((ConnectionManager)__instance.connectedPlayers[j]).Connection)).SendMessage(data, (SendType)8); } return false; } } [HarmonyPatch(typeof(SteamSocket))] [HarmonyPatch("OnMessage")] public static class SteamSocketPatch_OnMessage { private static byte[] ulongConversionArray = new byte[8]; private static byte[] uintConversionArray = new byte[4]; private static byte[] ushortConversionArray = new byte[2]; private static CodeInstruction Log(CodeInstruction i) { Plugin.Log.LogInfo((object)i); return i; } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { Plugin.Log.LogInfo((object)"Patching SteamSocket.OnMessage()"); IEnumerator<CodeInstruction> enumerator = instructions.GetEnumerator(); while (enumerator.MoveNext()) { CodeInstruction instruction2 = enumerator.Current; if (CodeInstructionExtensions.Calls(instruction2, AccessTools.Method(typeof(Ack), "get_PacketTypeAsEnum", (Type[])null, (Type[])null))) { yield return instruction2; if (!enumerator.MoveNext()) { Plugin.Log.LogError((object)"Expected to find next instruction after Ack.get_PacketTypeAsEnum() call"); break; } instruction2 = enumerator.Current; if (CodeInstructionExtensions.LoadsConstant(instruction2, 5L)) { yield return Log(new CodeInstruction(OpCodes.Ldc_I4, (object)254)); } else if (CodeInstructionExtensions.LoadsConstant(instruction2, 6L)) { yield return new CodeInstruction(OpCodes.Ldc_I4, (object)255); } else { yield return instruction2; } } else { yield return instruction2; } } } private static bool Prefix(SteamSocket __instance, Connection connection, NetIdentity identity, IntPtr data, int size, long messageNum, long recvTime, int channel) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (size >= 24 && size != 67 && size != 83) { SteamId steamId = ((NetIdentity)(ref identity)).SteamId; if (!((SteamId)(ref steamId)).IsValid) { Plugin.Log.LogWarning((object)"got message from invalid steamId"); return false; } SteamId steamId2 = ((NetIdentity)(ref identity)).SteamId; Friend val = new Friend(steamId2); if (!((Friend)(ref val)).IsIn(((Lobby)(ref SteamManager.instance.currentLobby)).Id)) { ManualLogSource log = Plugin.Log; steamId = ((NetIdentity)(ref identity)).SteamId; log.LogWarning((object)("Ignored a msg from " + ((object)(SteamId)(ref steamId)).ToString())); return false; } byte[] array = new byte[size]; Marshal.Copy(data, array, 0, size); if (((Lobby)(ref SteamManager.instance.currentLobby)).IsOwnedBy(((NetIdentity)(ref identity)).SteamId)) { SteamManagerExtended.startParameters = NetworkToolsExtensions.ReadMultiStartRequest(array, ref uintConversionArray, ref ulongConversionArray, ref ushortConversionArray); if (GameSession.inMenus) { CharacterSelectHandler_online.ForceStartGame((PlayerColors)null); } else { SteamManager.ForceLoadNextLevel(); } } return false; } return true; } } public static class PluginInfo { public const string PLUGIN_GUID = "MoreMultiPlayer"; public const string PLUGIN_NAME = "MoreMultiPlayer"; public const string PLUGIN_VERSION = "1.0.0"; } }