From d8b84be0c240898310ccb75d31e3158e1fde260e Mon Sep 17 00:00:00 2001 From: Niko233 <139348239+NikoCat233@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:50:53 +0800 Subject: [PATCH 1/5] Fix judge of custom server from findgamebycode --- .../Miscellaneous/CustomServersPatch.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs index 6232fba..6678dac 100644 --- a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs +++ b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs @@ -10,6 +10,10 @@ namespace Reactor.Patches.Miscellaneous; internal static class CustomServersPatch { + // A code lookup can point to an official game while a custom region is selected. + // Keep only the target IP from an official response so later lookups replace stale state. + private static string? _officialFindGameIp; + private static bool IsCurrentServerOfficial() { const string Domain = "among.us"; @@ -19,6 +23,17 @@ private static bool IsCurrentServerOfficial() regionInfo.Servers.All(serverInfo => serverInfo.Ip.EndsWith(Domain, StringComparison.Ordinal)); } + private static bool IsOfficialFindGameTarget() + { + return _officialFindGameIp is { Length: > 0 } targetIp && + string.Equals(AmongUsClient.Instance.networkAddress, targetIp, StringComparison.Ordinal); + } + + private static bool IsOfficialRegion(StringNames region) + { + return region is StringNames.ServerNA or StringNames.ServerEU or StringNames.ServerAS; + } + [HarmonyPatch] public static class DisableAuthServerPatch { @@ -30,7 +45,7 @@ public static IEnumerable TargetMethods() => public static bool Prefix(ref bool __result) { - if (IsCurrentServerOfficial()) + if (IsCurrentServerOfficial() || IsOfficialFindGameTarget()) { return true; } @@ -40,6 +55,18 @@ public static bool Prefix(ref bool __result) } } + [HarmonyPatch(typeof(EnterCodeManager), nameof(EnterCodeManager.FindGameResult))] + public static class CacheFindGameTargetPatch + { + [HarmonyPostfix] + public static void Postfix([HarmonyArgument(0)] HttpMatchmakerManager.FindGameByCodeResponse response) + { + _officialFindGameIp = response != null && IsOfficialRegion(response.Region) + ? response.Game?.IPString + : null; + } + } + [HarmonyPatch] public static class EnableUdpPatch { From a1533cd33f3d466882781c4d74c8b383064ee7cf Mon Sep 17 00:00:00 2001 From: NikoCat233 <139348239+NikoCat233@users.noreply.github.com> Date: Sun, 4 Oct 2026 00:29:38 +0800 Subject: [PATCH 2/5] Fix official lookup detection using server domains --- Reactor/Patches/Miscellaneous/CustomServersPatch.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs index 6678dac..a6f482a 100644 --- a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs +++ b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs @@ -29,11 +29,6 @@ private static bool IsOfficialFindGameTarget() string.Equals(AmongUsClient.Instance.networkAddress, targetIp, StringComparison.Ordinal); } - private static bool IsOfficialRegion(StringNames region) - { - return region is StringNames.ServerNA or StringNames.ServerEU or StringNames.ServerAS; - } - [HarmonyPatch] public static class DisableAuthServerPatch { @@ -61,7 +56,7 @@ public static class CacheFindGameTargetPatch [HarmonyPostfix] public static void Postfix([HarmonyArgument(0)] HttpMatchmakerManager.FindGameByCodeResponse response) { - _officialFindGameIp = response != null && IsOfficialRegion(response.Region) + _officialFindGameIp = response != null && IsCurrentServerOfficial() ? response.Game?.IPString : null; } From e6af0f2f7504f51273f3785027fd95ce8b2b5aa8 Mon Sep 17 00:00:00 2001 From: NikoCat233 <139348239+NikoCat233@users.noreply.github.com> Date: Sun, 4 Oct 2026 00:32:14 +0800 Subject: [PATCH 3/5] Capture matchmaking targets for all code lookup paths --- .../Miscellaneous/CustomServersPatch.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs index a6f482a..33dad69 100644 --- a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs +++ b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs @@ -50,15 +50,23 @@ public static bool Prefix(ref bool __result) } } - [HarmonyPatch(typeof(EnterCodeManager), nameof(EnterCodeManager.FindGameResult))] + [HarmonyPatch(typeof(HttpMatchmakerManager), nameof(HttpMatchmakerManager.CoFindGameInfo))] public static class CacheFindGameTargetPatch { - [HarmonyPostfix] - public static void Postfix([HarmonyArgument(0)] HttpMatchmakerManager.FindGameByCodeResponse response) + public static void Prefix([HarmonyArgument(1)] ref Il2CppSystem.Action onGameInfo) { - _officialFindGameIp = response != null && IsCurrentServerOfficial() - ? response.Game?.IPString - : null; + // Failed lookups do not invoke the callback, so clear the previous target up front. + _officialFindGameIp = null; + var originalCallback = onGameInfo; + onGameInfo = (Action) ((response, matchmakerToken) => + { + // The queried region is still selected here. Cache before invoking the callback, + // because invitation callbacks can start joining the game immediately. + _officialFindGameIp = response != null && IsCurrentServerOfficial() + ? response.Game?.IPString + : null; + originalCallback.Invoke(response!, matchmakerToken); + }); } } From c5b92acd390fb23ef60a6d9aae8e7dc9a9443c02 Mon Sep 17 00:00:00 2001 From: NikoCat233 <139348239+NikoCat233@users.noreply.github.com> Date: Sun, 4 Oct 2026 00:43:05 +0800 Subject: [PATCH 4/5] Preserve lookup server classification across region reloads --- .../Miscellaneous/CustomServersPatch.cs | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs index 33dad69..168f4d7 100644 --- a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs +++ b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs @@ -15,10 +15,15 @@ internal static class CustomServersPatch private static string? _officialFindGameIp; private static bool IsCurrentServerOfficial() + { + return IsOfficialRegion(ServerManager.Instance.CurrentRegion); + } + + private static bool IsOfficialRegion(IRegionInfo? region) { const string Domain = "among.us"; - return ServerManager.Instance.CurrentRegion?.TryCast() is { } regionInfo && + return region?.TryCast() is { } regionInfo && regionInfo.PingServer.EndsWith(Domain, StringComparison.Ordinal) && regionInfo.Servers.All(serverInfo => serverInfo.Ip.EndsWith(Domain, StringComparison.Ordinal)); } @@ -38,9 +43,18 @@ public static IEnumerable TargetMethods() => Il2CppStateMachineWrapper.GetStateMachineMoveNext(nameof(AuthManager.CoWaitForNonce))! ]; - public static bool Prefix(ref bool __result) + public static bool Prefix(Il2CppObjectBase __instance, ref bool __result) { - if (IsCurrentServerOfficial() || IsOfficialFindGameTarget()) + var officialRegion = IsCurrentServerOfficial(); + var officialTarget = IsOfficialFindGameTarget(); + var allowAuth = officialRegion || officialTarget; + var stateMachine = new Il2CppStateMachineWrapper(__instance); + if (stateMachine.State == 0) + { + Info($"Authentication {__instance.GetType().Name}: {(allowAuth ? "running" : "skipped")} for {AmongUsClient.Instance.networkAddress} (official region: {officialRegion}, code lookup target: {officialTarget})"); + } + + if (allowAuth) { return true; } @@ -57,14 +71,24 @@ public static void Prefix([HarmonyArgument(1)] ref Il2CppSystem.Action region.Name) + .ToHashSet(StringComparer.Ordinal); + Info($"Code lookup started with {officialRegionNames.Count} official regions"); + var originalCallback = onGameInfo; onGameInfo = (Action) ((response, matchmakerToken) => { - // The queried region is still selected here. Cache before invoking the callback, - // because invitation callbacks can start joining the game immediately. - _officialFindGameIp = response != null && IsCurrentServerOfficial() - ? response.Game?.IPString - : null; + var officialRegion = response?.UntranslatedRegion is { } regionName && officialRegionNames.Contains(regionName); + var targetIp = response?.Game?.IPString; + _officialFindGameIp = officialRegion ? targetIp : null; + Info($"Code lookup completed: official region: {officialRegion}, target: {targetIp ?? ""}"); + + // Invitation callbacks can start joining immediately, so cache first. originalCallback.Invoke(response!, matchmakerToken); }); } From 6a15f4a364c8830ae5da3245af295db6e9d37555 Mon Sep 17 00:00:00 2001 From: NikoCat233 <139348239+NikoCat233@users.noreply.github.com> Date: Sun, 4 Oct 2026 00:54:34 +0800 Subject: [PATCH 5/5] Forward refreshed lookup tokens to game authentication --- .../Miscellaneous/CustomServersPatch.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs index 168f4d7..d52b1b4 100644 --- a/Reactor/Patches/Miscellaneous/CustomServersPatch.cs +++ b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using AmongUs.HTTP; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Reactor.Utilities; @@ -14,6 +15,9 @@ internal static class CustomServersPatch // Keep only the target IP from an official response so later lookups replace stale state. private static string? _officialFindGameIp; + [ThreadStatic] + private static RetryableWebRequest? _findGameResponseRequest; + private static bool IsCurrentServerOfficial() { return IsOfficialRegion(ServerManager.Instance.CurrentRegion); @@ -88,12 +92,66 @@ public static void Prefix([HarmonyArgument(1)] ref Il2CppSystem.Action"}"); + // A 401 retry updates the request header, but the game's callback still + // captures the token obtained before that retry. Forward the accepted token. + if (_findGameResponseRequest?.requestHeaders.TryGetValue("Authorization", out var authorization) == true && + authorization.StartsWith("Bearer ", StringComparison.Ordinal) && authorization.Length > 7) + { + var acceptedToken = authorization[7..]; + if (!string.Equals(matchmakerToken, acceptedToken, StringComparison.Ordinal)) + { + Info("Using refreshed matchmaking token from the successful code lookup request"); + } + + matchmakerToken = acceptedToken; + } + // Invitation callbacks can start joining immediately, so cache first. originalCallback.Invoke(response!, matchmakerToken); }); } } + [HarmonyPatch] + public static class CaptureFindGameResponsePatch + { + public static MethodBase TargetMethod() + { + return Il2CppStateMachineWrapper.GetStateMachineMoveNext(nameof(HttpMatchmakerManager.CoSendRequest))!; + } + + public static void Prefix(Il2CppObjectBase __instance) + { + var stateMachine = new Il2CppStateMachineWrapper(__instance); + if (stateMachine.State != 0) return; + + var request = stateMachine.GetParameter("request"); + const string LookupPath = "/api/games/"; + if (request.Method != "GET" || !Uri.TryCreate(request.Url, UriKind.Absolute, out var uri) || + !uri.AbsolutePath.StartsWith(LookupPath, StringComparison.Ordinal) || + !int.TryParse(uri.AbsolutePath[LookupPath.Length..], out _)) return; + + var originalCallback = request.successCallback; + if (originalCallback == null) return; + + request.SetOrReplaceSuccessCallback((Action) (response => + { + // Keep the successful request in scope only during its synchronous callback. + // Saving/restoring the context also handles nested callbacks and exceptions. + var previousRequest = _findGameResponseRequest; + _findGameResponseRequest = request; + try + { + originalCallback.Invoke(response); + } + finally + { + _findGameResponseRequest = previousRequest; + } + })); + } + } + [HarmonyPatch] public static class EnableUdpPatch {