From e0ac37272f3a2cd7927d9e6cba5309844cb1afd1 Mon Sep 17 00:00:00 2001 From: Xilophor Date: Sun, 4 Feb 2024 22:16:59 -0500 Subject: [PATCH 1/2] Transpile new Local Var; Remove outside prefix & store of query values --- LobbyCompatibility/Features/LobbyHelper.cs | 2 - .../Patches/LoadServerListTranspiler.cs | 56 +++++++++++++++---- .../Patches/RequestAsyncPrefix.cs | 25 --------- 3 files changed, 44 insertions(+), 39 deletions(-) delete mode 100644 LobbyCompatibility/Patches/RequestAsyncPrefix.cs diff --git a/LobbyCompatibility/Features/LobbyHelper.cs b/LobbyCompatibility/Features/LobbyHelper.cs index 20def0f..9993c3f 100644 --- a/LobbyCompatibility/Features/LobbyHelper.cs +++ b/LobbyCompatibility/Features/LobbyHelper.cs @@ -13,8 +13,6 @@ namespace LobbyCompatibility.Features; internal static class LobbyHelper { public static LobbyDiff LatestLobbyDiff { get; private set; } = new(new List()); - public static Dictionary LatestLobbyRequestStringFilters = new(); - public static LobbyDistanceFilter? LatestLobbyRequestDistanceFilter; private static Dictionary _lobbyDiffCache { get; set; } = new(); private static List? _clientPlugins; diff --git a/LobbyCompatibility/Patches/LoadServerListTranspiler.cs b/LobbyCompatibility/Patches/LoadServerListTranspiler.cs index 51835ef..8b78e61 100644 --- a/LobbyCompatibility/Patches/LoadServerListTranspiler.cs +++ b/LobbyCompatibility/Patches/LoadServerListTranspiler.cs @@ -34,44 +34,76 @@ public class LoadServerListTranspiler [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator ilGenerator) { + var searchRequestMethod = AccessTools.Method(typeof(LobbyQuery), nameof(LobbyQuery.RequestAsync)); var searchCoroutineMethod = AccessTools.Method(typeof(MonoBehaviour), nameof(MonoBehaviour.StartCoroutine), new [] { typeof(IEnumerator) }); + + var cloneMethod = AccessTools.Method(typeof(LoadServerListTranspiler), nameof(CreateClone)); var postfixMethod = AccessTools.Method(typeof(LoadServerListTranspiler), nameof(LoadListPostfix)); - + + // Create a new local variable in the method & store the index + var cloneVarIndex = ilGenerator.DeclareLocal(typeof(LobbyQuery)).LocalIndex; + + CodeMatcher codeMatcher = new(instructions, ilGenerator); CodeInstruction loadVarInstruction; + + // Does the following: + // - Finds the RequestAsync call + // - Calls CloneLobby + // - Stores the returned query + // - Loads lobbyQuery2's address + codeMatcher + .SearchForward(inst => inst.Calls(searchRequestMethod)) + .ThrowIfInvalid("Unable to find RequestAsync.") + .InsertAndAdvance(new[] { + new CodeInstruction(OpCodes.Callvirt, cloneMethod), + new CodeInstruction(OpCodes.Stloc_S, cloneVarIndex), + new CodeInstruction(OpCodes.Ldloca_S, 3) + }); // Does the following: + // - Finds the StartCoroutine call // - Sets final instruction of the coroutine line to Nop - // - Sets the first instruction of the coroutine line to unconditionally branch to Nop - // - Moves to the end of the "try" section of the MoveNext method - // - Load steamLobbyManager variable - // - Load lobbyQuery2 variable - // - Call Postfix method - var codeMatcher = new CodeMatcher(instructions, ilGenerator) + // - Sets first instruction of the coroutine line to unconditionally branch to Nop + codeMatcher .SearchForward(inst => inst.Calls(searchCoroutineMethod)) .ThrowIfInvalid("Unable to find StartCoroutine.") .Advance(1) .SetInstruction(new CodeInstruction(OpCodes.Nop)) .CreateLabel(out var label) .Advance(-7) - .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Br, label)) + .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Br, label)); + + // Does the following: + // - Finds the end of the final state of the MoveNext method + // - Loads steamLobbyManager variable + // - Loads cloned lobby query variable + // - Calls Postfix + codeMatcher .SearchForward(inst => inst.opcode == OpCodes.Leave) .ThrowIfInvalid("Unable to find leave instruction.") .InsertAndAdvance(new[] { loadVarInstruction = new CodeInstruction(OpCodes.Ldloc_1), + new CodeInstruction(OpCodes.Ldloc_S, cloneVarIndex), new CodeInstruction(OpCodes.Call, postfixMethod) }); + // Does the following: + // - Move the Branch label from the leave instruction to the start of our postfix instructions codeMatcher.Instruction.MoveLabelsTo(loadVarInstruction); return codeMatcher.InstructionEnumeration(); } - internal static async void LoadListPostfix(SteamLobbyManager steamLobbyManager) + private static LobbyQuery CreateClone(ref LobbyQuery lobbyQuery) => new() + { + distance = lobbyQuery.distance, + stringFilters = lobbyQuery.stringFilters ?? new Dictionary() + }; + + internal static async void LoadListPostfix(SteamLobbyManager steamLobbyManager, LobbyQuery lobbyQuery) { // Create a new LobbyQuery using the previous request's values - LobbyQuery query = SteamMatchmaking.LobbyList; - query.stringFilters = LobbyHelper.LatestLobbyRequestStringFilters; - query.distance = LobbyHelper.LatestLobbyRequestDistanceFilter; + var query = lobbyQuery; // If there is not a ModdedLobbyFilterDropdown Instance, treat as if we are doing no filtering var currentFilter = ModdedLobbyFilterDropdown.Instance != null ? ModdedLobbyFilterDropdown.Instance.LobbyFilter : ModdedLobbyFilter.All; diff --git a/LobbyCompatibility/Patches/RequestAsyncPrefix.cs b/LobbyCompatibility/Patches/RequestAsyncPrefix.cs deleted file mode 100644 index 132fed5..0000000 --- a/LobbyCompatibility/Patches/RequestAsyncPrefix.cs +++ /dev/null @@ -1,25 +0,0 @@ -using HarmonyLib; -using LobbyCompatibility.Features; -using Steamworks.Data; - -namespace LobbyCompatibility.Patches; - -/// -/// Patches . -/// Used to store previous lobby filter settings, so we can make additional requests with similar settings -/// This is done as late in the execution chain as possible, to maintain compatibility with lobby filters from other mods -/// -/// -[HarmonyPatch(typeof(LobbyQuery), nameof(LobbyQuery.RequestAsync))] -[HarmonyPriority(Priority.Last)] -[HarmonyWrapSafe] -internal static class RequestAsyncPrefix -{ - [HarmonyPrefix] - private static void Prefix(LobbyQuery __instance) - { - var lobbyQuery = __instance; - LobbyHelper.LatestLobbyRequestStringFilters = lobbyQuery.stringFilters ?? new(); - LobbyHelper.LatestLobbyRequestDistanceFilter = lobbyQuery.distance; - } -} \ No newline at end of file From c685f689b79a5a5c6f3cce434a5989857b2d9c60 Mon Sep 17 00:00:00 2001 From: Xilophor Date: Mon, 5 Feb 2024 08:12:52 -0500 Subject: [PATCH 2/2] Remove unnecessary filter check Originally was for the cached filter, but now that the query is being given directly from the transpiled method, the check can be removed --- LobbyCompatibility/Patches/LoadServerListTranspiler.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/LobbyCompatibility/Patches/LoadServerListTranspiler.cs b/LobbyCompatibility/Patches/LoadServerListTranspiler.cs index 8b78e61..ab09e8a 100644 --- a/LobbyCompatibility/Patches/LoadServerListTranspiler.cs +++ b/LobbyCompatibility/Patches/LoadServerListTranspiler.cs @@ -117,12 +117,8 @@ internal static async void LoadListPostfix(SteamLobbyManager steamLobbyManager, // we only need to run the hashfilter if we're specifically looking for compatible lobbies if (PluginHelper.Checksum != "" && (currentFilter == ModdedLobbyFilter.CompatibleFirst || currentFilter == ModdedLobbyFilter.CompatibleOnly)) { - // If our previous cached query already has a checksum attribute, remove it, as otherwise WithKeyValue throws an error - if (query.stringFilters.ContainsKey(LobbyMetadata.RequiredChecksum)) - query.stringFilters.Remove(LobbyMetadata.RequiredChecksum); - - else - query.WithKeyValue(LobbyMetadata.RequiredChecksum, PluginHelper.Checksum); + // Add checksum filter to query + query.WithKeyValue(LobbyMetadata.RequiredChecksum, PluginHelper.Checksum); // Make an additional search for lobbies that match the checksum filteredLobbies = await query.RequestAsync();