From 087d9a900e4305c47e496199fa11026d96ab29dd Mon Sep 17 00:00:00 2001 From: zombieb Date: Sat, 16 Aug 2025 21:04:26 -0400 Subject: [PATCH] Internals rewritten. PHoton config can now be fetched from the server, and if it fails, fallback to the local configuration. --- Core/Config/Config.cs | 4 + .../CustomRecNet/CustomEmotes/RecNetEmotes.cs | 37 ++-- .../CustomRecNet/CustomPhoton/CustomPhoton.cs | 67 +++++++ .../CustomRecNet/CustomRecNet.cs | 10 +- .../GameManager/GameAssetConfigs.cs | 2 +- .../GameManager/GameConfigurator.cs | 2 +- .../GameManager/GameManSpawns.cs | 2 +- Core/Init.cs | 29 ++- Core/Util.cs | 19 +- Patches/GameManager/FreeSpawns.cs | 2 +- Patches/GameManager/GameConfigurator.cs | 2 +- Patches/{ => Internals}/PostAuthentication.cs | 7 +- Patches/Internals/PostGetMyAccount.cs | 32 ++++ Patches/{ => Internals}/PostNameserver.cs | 8 +- Patches/Photon/NameserverTest.cs | 18 ++ Patches/Photon/PUNNetworkManager.cs | 108 +++++++++++ Patches/Photon/PhotonPatch.cs | 93 +++++++++ Patches/PhotonPatch.cs | 180 ------------------ Plugin.cs | 2 +- undead-universal-patch-il2cpp.csproj | 3 +- 20 files changed, 393 insertions(+), 234 deletions(-) rename Core/{ => Content}/CustomRecNet/CustomEmotes/RecNetEmotes.cs (57%) create mode 100644 Core/Content/CustomRecNet/CustomPhoton/CustomPhoton.cs rename Core/{ => Content}/CustomRecNet/CustomRecNet.cs (77%) rename Core/{ => Content}/GameManager/GameAssetConfigs.cs (98%) rename Core/{ => Content}/GameManager/GameConfigurator.cs (76%) rename Core/{ => Content}/GameManager/GameManSpawns.cs (73%) rename Patches/{ => Internals}/PostAuthentication.cs (88%) create mode 100644 Patches/Internals/PostGetMyAccount.cs rename Patches/{ => Internals}/PostNameserver.cs (78%) create mode 100644 Patches/Photon/NameserverTest.cs create mode 100644 Patches/Photon/PUNNetworkManager.cs create mode 100644 Patches/Photon/PhotonPatch.cs delete mode 100644 Patches/PhotonPatch.cs diff --git a/Core/Config/Config.cs b/Core/Config/Config.cs index f22aefe..bfd6f65 100644 --- a/Core/Config/Config.cs +++ b/Core/Config/Config.cs @@ -35,10 +35,12 @@ namespace undead_universal_patch_il2cpp.Core.Config public static class ServerPatchesConfig { public static ConfigEntry CustomEmotes; + public static ConfigEntry CustomPhoton; } public static class ServerPatchesConfigDefaults { public static bool CustomEmotes = false; + public static bool CustomPhoton = false; } public static class GameManagerConfig { @@ -69,6 +71,7 @@ namespace undead_universal_patch_il2cpp.Core.Config public static ConfigEntry SelfHosted; public static ConfigEntry ServerAddress; public static ConfigEntry ServerPort; + public static ConfigEntry ConnectionProtocol; } public static class PhotonConfigDefaults { @@ -79,5 +82,6 @@ namespace undead_universal_patch_il2cpp.Core.Config public static bool SelfHosted = false; public static string ServerAddress = "127.0.0.1"; public static int ServerPort = 5055; + public static byte ConnectionProtocol = 0; } } diff --git a/Core/CustomRecNet/CustomEmotes/RecNetEmotes.cs b/Core/Content/CustomRecNet/CustomEmotes/RecNetEmotes.cs similarity index 57% rename from Core/CustomRecNet/CustomEmotes/RecNetEmotes.cs rename to Core/Content/CustomRecNet/CustomEmotes/RecNetEmotes.cs index 1420434..c4994e2 100644 --- a/Core/CustomRecNet/CustomEmotes/RecNetEmotes.cs +++ b/Core/Content/CustomRecNet/CustomEmotes/RecNetEmotes.cs @@ -4,9 +4,9 @@ using System.Collections.Concurrent; using Mapster; using System; using System.Linq; -using undead_universal_patch_il2cpp.Core.Config; +using BestHTTP; -namespace undead_universal_patch_il2cpp.Core.CustomRecNet.CustomEmotes; +namespace undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomEmotes; public class EmoteConfigDTO { @@ -18,9 +18,15 @@ public class EmoteConfigDTO public bool OnlyBroadcastToTeam { get; set; } } -public class EmoteConfig +public class CustomEmotes : MonoBehaviour { - public static void Patch(List emotes) + + public void Start() + { + RecNetInteractions.postLocalAccountActions.Add(DownloadCustomEmotes); + } + + void Patch(HTTPResponse _res, List emotes) { UniversalPatchPlugin.Log.LogInfo("Setting new emotes"); @@ -36,29 +42,10 @@ public class EmoteConfig Util.ConditionalDebug($"{emotes.Count} new emote configurations"); } -} - -public class CustomEmotes : MonoBehaviour -{ - private static readonly ConcurrentQueue mainThreadQueue = new ConcurrentQueue(); - - public void Start() - { - if (ServerPatchesConfig.CustomEmotes.Value) - RecNetInteractions.postAuthenticationActions.Add(DownloadCustomEmotes); - } - - void Update() - { - while (mainThreadQueue.TryDequeue(out var action)) - { - try { action(); } - catch (Exception ex) { UniversalPatchPlugin.Log.LogError(ex); } - } - } void DownloadCustomEmotes() { - RecNetInteractions.SendRequest>(BestHTTP.HTTPMethods.Get, RecNet.Service.API, "/api/undead/v1/emotes", EmoteConfig.Patch); + RecNetInteractions.SendRequest>(HTTPMethods.Get, RecNet.Service.API, "/api/undead/v1/emotes", Patch); } + } \ No newline at end of file diff --git a/Core/Content/CustomRecNet/CustomPhoton/CustomPhoton.cs b/Core/Content/CustomRecNet/CustomPhoton/CustomPhoton.cs new file mode 100644 index 0000000..f89e852 --- /dev/null +++ b/Core/Content/CustomRecNet/CustomPhoton/CustomPhoton.cs @@ -0,0 +1,67 @@ +using System; +using BestHTTP; +using Il2CppInterop.Runtime; +using undead_universal_patch_il2cpp.Core.Config; +using undead_universal_patch_il2cpp.Patches.Photon; +using UnityEngine; + +namespace undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomPhoton; + +public class PhotonConfigDTO +{ + public bool Logging { get; set; } + public string AppID { get; set; } + public string VoiceAppID { get; set; } + public bool SelfHosted { get; set; } + public string ServerAddress { get; set; } + public int ServerPort { get; set; } + public byte ConnectionProtocol { get; set; } +} + +public class CustomPhoton : MonoBehaviour +{ + public static bool ServerConfigFailed { get; set; } = false; + + public void Start() + { + RecNetInteractions.postLocalAccountActions.Add(DownloadServerConfig); + } + + void OnServerConfigFailed(HTTPResponse res) + { + ServerConfigFailed = true; + if (PhotonConfig.PatchPhotonIds.Value) + { + UniversalPatchPlugin.Log.LogInfo("Attempting Photon patch from local configuration"); + PhotonPatch.Patch(new PhotonConfigDTO + { + Logging = PhotonConfig.PunLogging.Value, + AppID = PhotonConfig.AppID.Value, + VoiceAppID = PhotonConfig.VoiceAppID.Value, + SelfHosted = PhotonConfig.SelfHosted.Value, + ServerAddress = PhotonConfig.ServerAddress.Value, + ServerPort = PhotonConfig.ServerPort.Value, + ConnectionProtocol = PhotonConfig.ConnectionProtocol.Value + }); + } + } + void ApplyPhotonConfig(HTTPResponse res, PhotonConfigDTO photonConfig) + { + if (!ServerPatchesConfig.CustomPhoton.Value) return; + + try + { + UniversalPatchPlugin.Log.LogInfo("Attempting Photon patch from server configuration"); + PhotonPatch.Patch(photonConfig); + } + catch (Exception err) + { + UniversalPatchPlugin.Log.LogError($"Failed to apply Photon configuration from server: {err}"); + OnServerConfigFailed(res); + } + } + void DownloadServerConfig() + { + RecNetInteractions.SendRequest(HTTPMethods.Get, RecNet.Service.API, "/api/undead/v1/photon", ApplyPhotonConfig, OnServerConfigFailed); + } +} \ No newline at end of file diff --git a/Core/CustomRecNet/CustomRecNet.cs b/Core/Content/CustomRecNet/CustomRecNet.cs similarity index 77% rename from Core/CustomRecNet/CustomRecNet.cs rename to Core/Content/CustomRecNet/CustomRecNet.cs index 96714e7..f9f641a 100644 --- a/Core/CustomRecNet/CustomRecNet.cs +++ b/Core/Content/CustomRecNet/CustomRecNet.cs @@ -4,16 +4,17 @@ using System.Text.Json; using BestHTTP; using Il2CppInterop.Runtime; using RecNet; -using RecRoom.Async; -namespace undead_universal_patch_il2cpp.Core.CustomRecNet; +namespace undead_universal_patch_il2cpp.Core.Content.CustomRecNet; public class RecNetInteractions { + public static int AccountId { get; set; } public static string AccessToken { get; set; } public static List postNameServerActions = []; public static List postAuthenticationActions = []; + public static List postLocalAccountActions = []; public static Il2CppSystem.Uri CreateServiceUri(Service service, string pathAndQuery) { @@ -28,7 +29,7 @@ public class RecNetInteractions return RecNet.Core.ServiceUris.Count > 1; } - public static void SendRequest(HTTPMethods method, Service service, string requestUri, Action reqFinished) + public static void SendRequest(HTTPMethods method, Service service, string requestUri, Action reqFinished, Action? reqFailed = null) { var res = RecNet.Core.SendRequest(method, service, requestUri); @@ -37,11 +38,12 @@ public class RecNetInteractions try { var data = JsonSerializer.Deserialize(res.DataAsText); - reqFinished(data); + reqFinished(res, data); } catch (Exception ex) { UniversalPatchPlugin.Log.LogError($"'{requestUri}' failed\n{ex}"); + if (reqFailed != null) reqFailed(res); } })); } diff --git a/Core/GameManager/GameAssetConfigs.cs b/Core/Content/GameManager/GameAssetConfigs.cs similarity index 98% rename from Core/GameManager/GameAssetConfigs.cs rename to Core/Content/GameManager/GameAssetConfigs.cs index 29f1699..da2379c 100644 --- a/Core/GameManager/GameAssetConfigs.cs +++ b/Core/Content/GameManager/GameAssetConfigs.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using RecRoom.Tools; -namespace undead_universal_patch_il2cpp.Core.UndeadGameManager; +namespace undead_universal_patch_il2cpp.Core.Content.UndeadGameManager; public class AutoHealSettingsDTO { diff --git a/Core/GameManager/GameConfigurator.cs b/Core/Content/GameManager/GameConfigurator.cs similarity index 76% rename from Core/GameManager/GameConfigurator.cs rename to Core/Content/GameManager/GameConfigurator.cs index de982e2..e214bce 100644 --- a/Core/GameManager/GameConfigurator.cs +++ b/Core/Content/GameManager/GameConfigurator.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace undead_universal_patch_il2cpp.Core.UndeadGameManager; +namespace undead_universal_patch_il2cpp.Core.Content.UndeadGameManager; public static class GameConfigurator { diff --git a/Core/GameManager/GameManSpawns.cs b/Core/Content/GameManager/GameManSpawns.cs similarity index 73% rename from Core/GameManager/GameManSpawns.cs rename to Core/Content/GameManager/GameManSpawns.cs index 8c47104..6bea64b 100644 --- a/Core/GameManager/GameManSpawns.cs +++ b/Core/Content/GameManager/GameManSpawns.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace undead_universal_patch_il2cpp.Core.UndeadGameManager; +namespace undead_universal_patch_il2cpp.Core.Content.UndeadGameManager; public static class GameFreeSpawns { diff --git a/Core/Init.cs b/Core/Init.cs index 850a139..d5d1d45 100644 --- a/Core/Init.cs +++ b/Core/Init.cs @@ -4,8 +4,10 @@ using System.Linq; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Mapster; using undead_universal_patch_il2cpp.Core.Config; -using undead_universal_patch_il2cpp.Core.CustomRecNet.CustomEmotes; -using undead_universal_patch_il2cpp.Core.UndeadGameManager; +using undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomEmotes; +using undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomPhoton; +using undead_universal_patch_il2cpp.Core.Content.UndeadGameManager; +using UnityEngine; namespace undead_universal_patch_il2cpp.Core; @@ -13,8 +15,6 @@ public class Initialization { public static void Initialize() { - Util.LocalInstanceGuid = Guid.NewGuid().ToString(); - AddMapsterGlobalTypeConfigs(); FetchConfigurations(); AttachGameObjects(); @@ -55,7 +55,9 @@ public class Initialization private static void AttachGameObjects() { UniversalPatchPlugin.Log.LogInfo("Attaching game objects"); - UniversalPatchPlugin.Instance.AddComponent(); + + if (ServerPatchesConfig.CustomEmotes.Value) UniversalPatchPlugin.Instance.AddComponent(); + UniversalPatchPlugin.Instance.AddComponent(); } private static void FetchConfigurations() @@ -70,7 +72,7 @@ public class Initialization "The game expects a certain certificate from rec.net when making HTTPS requests. Enable this to allow any valid certificate."); PatchConfig.HilePatch = UniversalPatchPlugin.Instance.Config.Bind("Generic", "HilePatch", PatchConfigDefaults.HilePatch, "The game will close after a short period of time if BepInEx is found. Enable to stop this from happening." + - "This will also enable the AGRoomRuntimeConfig patch. See the README for more info."); + "\nThis will also enable the AGRoomRuntimeConfig patch. See the README for more info."); PatchConfig.SignalRHandshakeFix = UniversalPatchPlugin.Instance.Config.Bind("Generic", "SignalRHandshakeFix", PatchConfigDefaults.SignalRHandshakeFix, "Replace apostrophes with quotes in the initial SignalR handshake."); PatchConfig.ImageSignaturePatch = UniversalPatchPlugin.Instance.Config.Bind("Generic", "ImageSignaturePatch", PatchConfigDefaults.ImageSignaturePatch, @@ -83,6 +85,10 @@ public class Initialization ServerPatchesConfig.CustomEmotes = UniversalPatchPlugin.Instance.Config.Bind("ServerPatches", "CustomEmotes", ServerPatchesConfigDefaults.CustomEmotes, "Modify the game's emote text with a configuration from the server. Requires a custom server."); + ServerPatchesConfig.CustomPhoton = UniversalPatchPlugin.Instance.Config.Bind("ServerPatches", "CustomPhoton", ServerPatchesConfigDefaults.CustomPhoton, + "Patch ServerSettings values with a configuration from the server." + + "\nWhen the server fails to provide a valid configuration, values from the local config will be used." + + "\nPhoton.PatchPhotonIds must be enabled for this fallback to work."); GameManagerConfig.AnyGameFreeSpawn = UniversalPatchPlugin.Instance.Config.Bind("GameManagerConfig", "AnyGameFreeSpawn", GameManagerConfigDefaults.AnyGameFreeSpawn, $"Use patches from '{GameFreeSpawns.config.path}' to spawn in any valid player spawnpoint in specified games. See README.md"); @@ -103,9 +109,14 @@ public class Initialization "The new target Voice App ID from the Photon dashboard." + "\nWhen self-hosting, this value is ignored, since Photon voice in Rec Room is based on PUN."); PhotonConfig.ServerAddress = UniversalPatchPlugin.Instance.Config.Bind("Photon", "ServerAddress", PhotonConfigDefaults.ServerAddress, - "Address of the Photon master server (ignored if not using self-hosted)"); + "Address of the Photon target server (ignored if not using self-hosted)"); PhotonConfig.ServerPort = UniversalPatchPlugin.Instance.Config.Bind("Photon", "ServerPort", PhotonConfigDefaults.ServerPort, - "Photon master server UDP port (ignored if not using self-hosted)"); + "Photon target server port (ignored if not using self-hosted)." + + "\nYou can set this port to the matching protocol port from the server, e.g. 5055 for UDP, 9091 for WebSockets"); + PhotonConfig.ConnectionProtocol = UniversalPatchPlugin.Instance.Config.Bind("Photon", "ConnectionProtocol", PhotonConfigDefaults.ConnectionProtocol, + "Connection protocol to use when connecting to the target port (ignored if not using self-hosted)." + + "\n0: UDP, 1: TCP;" + + "\nWebSockets are not supported by Photon in this build."); NameserverConfig.Rewrite = UniversalPatchPlugin.Instance.Config.Bind("Nameserver", "Rewrite", NameserverConfigDefaults.Rewrite, "Enable/disable rewriting the URL for nameserver requests."); @@ -118,9 +129,11 @@ public class Initialization TypeAdapterConfig.GlobalSettings .ForType, Il2CppStructArray>() .MapWith(src => new Il2CppStructArray(src.Select(x => x.Adapt()).ToArray())); + TypeAdapterConfig.GlobalSettings .ForType, Il2CppReferenceArray>() .MapWith(src => new Il2CppReferenceArray(src.Select(x => x.Adapt()).ToArray())); + TypeAdapterConfig.GlobalSettings .ForType() .Map(dest => dest.emoteUniqueName, source => source.UniqueName) diff --git a/Core/Util.cs b/Core/Util.cs index 0b4ebaf..1791694 100644 --- a/Core/Util.cs +++ b/Core/Util.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using BepInEx.Configuration; +using CodeStage.AntiCheat.ObscuredTypes; using HarmonyLib; using undead_universal_patch_il2cpp.Core.Config; @@ -16,6 +17,22 @@ namespace undead_universal_patch_il2cpp.Core public bool Success; } + public class RecNetUtil + { + public static ObscuredInt GetAccountId() + { + PropertyInfo selfAccountProp = AccessTools.Property("RecNet.Accounts:LocalAccount"); + + object acc = selfAccountProp.GetValue(null); + Type selfAccountType = acc.GetType(); + + PropertyInfo accountIdProp = AccessTools.Property(selfAccountType, "AccountId"); + ObscuredInt val = (ObscuredInt)accountIdProp.GetValue(acc); + + return val; + } + } + public class Util { private static readonly PatchTypesResult UnsuccessfulPatchResult = new() @@ -25,8 +42,6 @@ namespace undead_universal_patch_il2cpp.Core Success = false }; - public static string LocalInstanceGuid { set; get; } - public static void ConditionalDebug(string msg) { if (GenericConfig.PatchDebug.Value) UniversalPatchPlugin.Log.LogDebug(msg); diff --git a/Patches/GameManager/FreeSpawns.cs b/Patches/GameManager/FreeSpawns.cs index 96743e1..c76f9f2 100644 --- a/Patches/GameManager/FreeSpawns.cs +++ b/Patches/GameManager/FreeSpawns.cs @@ -4,7 +4,7 @@ using System.Reflection; using HarmonyLib; using undead_universal_patch_il2cpp.Core; using undead_universal_patch_il2cpp.Core.Config; -using undead_universal_patch_il2cpp.Core.UndeadGameManager; +using undead_universal_patch_il2cpp.Core.Content.UndeadGameManager; namespace undead_universal_patch_il2cpp.Patches.UndeadGameManager; diff --git a/Patches/GameManager/GameConfigurator.cs b/Patches/GameManager/GameConfigurator.cs index 93be8ea..151678a 100644 --- a/Patches/GameManager/GameConfigurator.cs +++ b/Patches/GameManager/GameConfigurator.cs @@ -6,7 +6,7 @@ using Mapster; using RecRoom.Core.GameManagement; using undead_universal_patch_il2cpp.Core.Config; using undead_universal_patch_il2cpp.Core; -using undead_universal_patch_il2cpp.Core.UndeadGameManager; +using undead_universal_patch_il2cpp.Core.Content.UndeadGameManager; namespace undead_universal_patch_il2cpp.Patches.UndeadGameManager; diff --git a/Patches/PostAuthentication.cs b/Patches/Internals/PostAuthentication.cs similarity index 88% rename from Patches/PostAuthentication.cs rename to Patches/Internals/PostAuthentication.cs index 0b96e87..dd7bef1 100644 --- a/Patches/PostAuthentication.cs +++ b/Patches/Internals/PostAuthentication.cs @@ -1,11 +1,9 @@ using System.Reflection; using HarmonyLib; -using Il2CppInterop.Runtime; -using RecRoom.Async; using undead_universal_patch_il2cpp.Core; -using undead_universal_patch_il2cpp.Core.CustomRecNet; +using undead_universal_patch_il2cpp.Core.Content.CustomRecNet; -namespace undead_universal_patch_il2cpp.Patches; +namespace undead_universal_patch_il2cpp.Patches.Internals; [HarmonyPatch] public class AuthenticationEventPatch @@ -24,7 +22,6 @@ public class AuthenticationEventPatch static MethodBase TargetMethod() => patchResult.Method; - private static bool RanPostActions { get; set; } = false; static void Postfix(ref string accessToken) { diff --git a/Patches/Internals/PostGetMyAccount.cs b/Patches/Internals/PostGetMyAccount.cs new file mode 100644 index 0000000..7c50b23 --- /dev/null +++ b/Patches/Internals/PostGetMyAccount.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using BestHTTP; +using HarmonyLib; +using Il2CppInterop.Runtime; +using RecRoom.Async; +using undead_universal_patch_il2cpp.Core; +using undead_universal_patch_il2cpp.Core.Content.CustomRecNet; + +namespace undead_universal_patch_il2cpp.Patches.Internals; + +[HarmonyPatch] +public class GetMyAccountEventPatch +{ + static PatchTypesResult patchResult = Util.PreparePatchTypes( + "RecNet GetLocalAccount event patch", + "RecNet.Accounts", + "GetLocalAccount" + ); + + static bool Prepare() => patchResult.Success; + + static MethodBase TargetMethod() => patchResult.Method; + + static void Postfix(ref IPromise __result) + { + __result.Then(DelegateSupport.ConvertDelegate(() => + { + UniversalPatchPlugin.Log.LogInfo("Running post-GetLocalAccount actions"); + foreach (var action in RecNetInteractions.postLocalAccountActions) action(); + })); + } +} \ No newline at end of file diff --git a/Patches/PostNameserver.cs b/Patches/Internals/PostNameserver.cs similarity index 78% rename from Patches/PostNameserver.cs rename to Patches/Internals/PostNameserver.cs index afd2974..fd9e324 100644 --- a/Patches/PostNameserver.cs +++ b/Patches/Internals/PostNameserver.cs @@ -1,11 +1,13 @@ using System.Reflection; +using HarmonyLib; using Il2CppInterop.Runtime; using RecRoom.Async; using undead_universal_patch_il2cpp.Core; -using undead_universal_patch_il2cpp.Core.CustomRecNet; +using undead_universal_patch_il2cpp.Core.Content.CustomRecNet; -namespace undead_universal_patch_il2cpp.Patches; +namespace undead_universal_patch_il2cpp.Patches.Internals; +[HarmonyPatch] public class NameserverConnectEventPatch { static PatchTypesResult patchResult = Util.PreparePatchTypes( @@ -25,7 +27,7 @@ public class NameserverConnectEventPatch if (RecNetInteractions.HasNameserverConnected()) { UniversalPatchPlugin.Log.LogInfo("Running post-nameserver actions"); - foreach (var action in RecNetInteractions.postAuthenticationActions) action(); + foreach (var action in RecNetInteractions.postNameServerActions) action(); } else Util.ConditionalDebug("The nameserver request did not resolve successfully, skipping post-nameserver actions."); })); diff --git a/Patches/Photon/NameserverTest.cs b/Patches/Photon/NameserverTest.cs new file mode 100644 index 0000000..06d53be --- /dev/null +++ b/Patches/Photon/NameserverTest.cs @@ -0,0 +1,18 @@ +using System.Reflection; +using HarmonyLib; +using undead_universal_patch_il2cpp.Core; + +namespace undead_universal_patch_il2cpp.Patches.Photon; + +[HarmonyPatch] +public class NameserverTest +{ + static PatchTypesResult patchTypesResult = Util.PreparePatchTypes( + "Photon nameserver test", + "NetworkingPeer", + "GetNameServerAddress" + ); + + static bool Prepare() => patchTypesResult.Success; + static MethodBase TargetMethod() => patchTypesResult.Method; +} \ No newline at end of file diff --git a/Patches/Photon/PUNNetworkManager.cs b/Patches/Photon/PUNNetworkManager.cs new file mode 100644 index 0000000..512fc22 --- /dev/null +++ b/Patches/Photon/PUNNetworkManager.cs @@ -0,0 +1,108 @@ +using System.Reflection; +using HarmonyLib; +using undead_universal_patch_il2cpp.Core; +using undead_universal_patch_il2cpp.Core.Config; +using undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomPhoton; + +namespace undead_universal_patch_il2cpp.Patches.Photon; + +[HarmonyPatch] +public class ForceSelfHostedPhoton +{ + class HarmonyState + { + public object code; + } + + static PatchTypesResult patchResult = Util.ConfigsPreparePatchTypes( + [ + PhotonConfig.PatchPhotonIds, + PhotonConfig.SelfHosted + ], + "Force JoinOrCreateRoom when connected to master", + "PUNNetworkManager", + "OnConnectedToMaster" + ); + + static bool Prepare() => patchResult.Success; + + static MethodBase TargetMethod() => patchResult.Method; + + static void Prefix(ref PUNNetworkManager __instance, ref HarmonyState __state) + { + if (ServerPatchesConfig.CustomPhoton.Value && !CustomPhoton.ServerConfigFailed) + { + Util.ConditionalDebug("Skipping Selfhost Photon target server regionId roundtrip, CustomPhoton is enabled."); + return; + } + + __state = new HarmonyState(); + PropertyInfo targetGameSessionProperty = __instance.GetType().GetRuntimeProperty("targetGameSession"); + if (targetGameSessionProperty == null) + { + UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: targetGameSessionProperty was null."); + return; + } + var targetGameSession = targetGameSessionProperty.GetValue(__instance); + if (targetGameSession == null) + { + UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: targetGameSession was null."); + return; + } + PropertyInfo photonRegionIdProperty = targetGameSession.GetType().GetRuntimeProperty("PhotonRegionId"); + if (photonRegionIdProperty == null) + { + UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: photonRegionIdProperty was null."); + return; + } + + __state.code = photonRegionIdProperty.GetValue(targetGameSession); + photonRegionIdProperty.SetValue(targetGameSession, 4); + Util.ConditionalDebug("Selfhost Photon target server regionId pre-roundtrip"); + } + + static void Postfix(ref PUNNetworkManager __instance, ref HarmonyState __state) + { + PropertyInfo targetGameSessionProperty = __instance.GetType().GetRuntimeProperty("targetGameSession"); + if (targetGameSessionProperty == null) + { + UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver postfix: targetGameSessionProperty was null."); + return; + } + var targetGameSession = targetGameSessionProperty.GetValue(__instance); + if (targetGameSession == null) + { + UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver postfix: targetGameSession was null."); + return; + } + PropertyInfo photonRegionIdProperty = targetGameSession.GetType().GetRuntimeProperty("PhotonRegionId"); + if (photonRegionIdProperty == null) + { + UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: photonRegionIdProperty was null."); + return; + } + + photonRegionIdProperty.SetValue(targetGameSession, __state.code); + Util.ConditionalDebug("Selfhost Photon target server regionId post-roundtrip"); + } + +} + +// Fixes some reflection used by Rec Room that, when TCP is used in the Photon patch config, +// incorrectly assumes that ENetPeer (UDP) is in use +// Allows TPeer (TCP) to be specified by PhotonConfig.ConnectionProtocol +[HarmonyPatch] +public class PhotonThrottlingPatch +{ + static PatchTypesResult patchTypesResult = Util.ConfigPreparePatchTypes( + PhotonConfig.SelfHosted, + "ENet incorrect assumption patch", + "PUNNetworkManager", + "UpdatePhotonThrottling" + ); + + static bool Prepare() => patchTypesResult.Success; + static MethodBase TargetMethod() => patchTypesResult.Method; + + static bool Prefix() => PhotonConfig.ConnectionProtocol.Value != 1; +} \ No newline at end of file diff --git a/Patches/Photon/PhotonPatch.cs b/Patches/Photon/PhotonPatch.cs new file mode 100644 index 0000000..6f8b362 --- /dev/null +++ b/Patches/Photon/PhotonPatch.cs @@ -0,0 +1,93 @@ +using System; +using System.Reflection; +using HarmonyLib; +using undead_universal_patch_il2cpp.Core; +using undead_universal_patch_il2cpp.Core.Config; +using undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomPhoton; + +namespace undead_universal_patch_il2cpp.Patches.Photon; + +public class PhotonPatch +{ + public static void Patch(PhotonConfigDTO photonConfig) + { + // It's fine to reference enums after we're pretty sure the relevant types exist + // i think + Type photonNetworkType = AccessTools.TypeByName("PhotonNetwork"); + Type serverSettingsType = AccessTools.TypeByName("ServerSettings"); + + if (photonNetworkType == null || serverSettingsType == null) + { + UniversalPatchPlugin.Log.LogError("Cannot patch Photon: PhotonNetwork or ServerSettings types were not found. Is this build supported?"); + return; + } + + PropertyInfo photonServerSettingsProperty = photonNetworkType.GetRuntimeProperty("PhotonServerSettings"); + object serverSettings = photonServerSettingsProperty.GetValue(serverSettingsType); + + if (PhotonConfig.PunLogging.Value) + { + PropertyInfo loggingProperty = serverSettingsType.GetRuntimeProperty("PunLogging"); + loggingProperty.SetValue(serverSettings, PhotonLogLevel.Full); + PropertyInfo networkLoggingProperty = serverSettingsType.GetRuntimeProperty("NetworkLogging"); + networkLoggingProperty.SetValue(serverSettings, ExitGames.Client.Photon.DebugLevel.ALL); + } + + PropertyInfo appIdProperty = serverSettingsType.GetRuntimeProperty("AppID"); + PropertyInfo voiceAppIdProperty = serverSettingsType.GetRuntimeProperty("VoiceAppID"); + + Util.ConditionalDebug($"New Photon AppID: '{photonConfig.AppID}'"); + Util.ConditionalDebug($"New Photon AppID: '{photonConfig.VoiceAppID}'"); + appIdProperty.SetValue(serverSettings, photonConfig.AppID); + voiceAppIdProperty.SetValue(serverSettings, photonConfig.VoiceAppID); + + if (photonConfig.SelfHosted) + { + MethodInfo useMyServerMethod = serverSettingsType.GetMethod("UseMyServer"); + useMyServerMethod.Invoke(serverSettings, [ + photonConfig.ServerAddress, + photonConfig.ServerPort, + photonConfig.AppID, + ]); + + Type authValuesType = AccessTools.TypeByName("AuthenticationValues"); + if (authValuesType != null) + { + PropertyInfo networkingPeerProperty = photonNetworkType.GetRuntimeProperty("networkingPeer"); + if (networkingPeerProperty == null) + { + UniversalPatchPlugin.Log.LogError("Cannot continue to patch Photon: networkingPeerProperty was null. Is this build supported?"); + return; + } + + if (networkingPeerProperty.GetValue(null) == null) + { + UniversalPatchPlugin.Log.LogError("Cannot continue to patch Photon: networkingPeerInstance was null. Is this build supported?"); + return; + } + + int id = RecNetUtil.GetAccountId(); + PhotonNetwork.AuthValues = new AuthenticationValues + { + UserId = id.ToString() + }; + + Util.ConditionalDebug($"Set the authValues userId to {id}"); + } + + Type connectionProtocolType = AccessTools.TypeByName("ExitGames.Client.Photon.ConnectionProtocol"); + PropertyInfo protocolProp = serverSettingsType.GetRuntimeProperty("Protocol"); + if (connectionProtocolType != null && protocolProp != null) + { + protocolProp.SetValue(serverSettings, photonConfig.ConnectionProtocol); + } + else + { + UniversalPatchPlugin.Log.LogError("Cannot continue to patch Photon: connectionProtocolType or protocolProp was null"); + return; + } + } + + UniversalPatchPlugin.Log.LogInfo($"Photon patch ({(photonConfig.SelfHosted ? "self-hosted" : "cloud")}) succeeded."); + } +} diff --git a/Patches/PhotonPatch.cs b/Patches/PhotonPatch.cs deleted file mode 100644 index b9c5cb2..0000000 --- a/Patches/PhotonPatch.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System; -using System.Reflection; -using HarmonyLib; -using undead_universal_patch_il2cpp.Core; -using undead_universal_patch_il2cpp.Core.Config; - -namespace undead_universal_patch_il2cpp.Patches -{ - [HarmonyPatch] - public class PhotonPatchEvent - { - static PatchTypesResult patchResult = Util.ConfigPreparePatchTypes( - PhotonConfig.PatchPhotonIds, - "Photon ConnectUsingSettings event path", - "PhotonNetwork", - "ConnectUsingSettings" - ); - - static bool Prepare() => patchResult.Success; - - static MethodInfo TargetMethod() => patchResult.Method; - - static void Prefix() - { - Util.ConditionalDebug("Attempting Photon patch"); - PhotonPatch.Patch(); - } - } - - [HarmonyPatch] - public class ForceSelfHostedPhoton - { - class HarmonyState - { - public object code; - } - - static PatchTypesResult patchResult = Util.ConfigsPreparePatchTypes( - [ - PhotonConfig.PatchPhotonIds, - PhotonConfig.SelfHosted - ], - "Force JoinOrCreateRoom when connected to master", - "PUNNetworkManager", - "OnConnectedToMaster" - ); - - static bool Prepare() => patchResult.Success; - - static MethodBase TargetMethod() => patchResult.Method; - - static void Prefix(ref PUNNetworkManager __instance, ref HarmonyState __state) - { - __state = new HarmonyState(); - PropertyInfo targetGameSessionProperty = __instance.GetType().GetRuntimeProperty("targetGameSession"); - if (targetGameSessionProperty == null) - { - UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: targetGameSessionProperty was null."); - return; - } - var targetGameSession = targetGameSessionProperty.GetValue(__instance); - if (targetGameSession == null) - { - UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: targetGameSession was null."); - return; - } - PropertyInfo photonRegionIdProperty = targetGameSession.GetType().GetRuntimeProperty("PhotonRegionId"); - if (photonRegionIdProperty == null) - { - UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: photonRegionIdProperty was null."); - return; - } - - __state.code = photonRegionIdProperty.GetValue(targetGameSession); - photonRegionIdProperty.SetValue(targetGameSession, 4); - Util.ConditionalDebug("Forcing masterserver"); - } - - static void Postfix(ref PUNNetworkManager __instance, ref HarmonyState __state) - { - PropertyInfo targetGameSessionProperty = __instance.GetType().GetRuntimeProperty("targetGameSession"); - if (targetGameSessionProperty == null) - { - UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver postfix: targetGameSessionProperty was null."); - return; - } - var targetGameSession = targetGameSessionProperty.GetValue(__instance); - if (targetGameSession == null) - { - UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver postfix: targetGameSession was null."); - return; - } - PropertyInfo photonRegionIdProperty = targetGameSession.GetType().GetRuntimeProperty("PhotonRegionId"); - if (photonRegionIdProperty == null) - { - UniversalPatchPlugin.Log.LogFatal("Cannot force masterserver: photonRegionIdProperty was null."); - return; - } - - photonRegionIdProperty.SetValue(targetGameSession, __state.code); - Util.ConditionalDebug("Masterserver regionId roundtrip"); - } - - } - - public class PhotonPatch - { - public static void Patch() - { - // It's fine to reference enums after we're pretty sure the relevant types exist - // i think - Type photonNetworkType = AccessTools.TypeByName("PhotonNetwork"); - Type serverSettingsType = AccessTools.TypeByName("ServerSettings"); - - if (photonNetworkType == null || serverSettingsType == null) - { - UniversalPatchPlugin.Log.LogError("Cannot patch Photon: PhotonNetwork or ServerSettings types were not found. Is this build supported?"); - return; - } - - PropertyInfo photonServerSettingsProperty = photonNetworkType.GetRuntimeProperty("PhotonServerSettings"); - object serverSettings = photonServerSettingsProperty.GetValue(serverSettingsType); - - if (PhotonConfig.PunLogging.Value) - { - PropertyInfo loggingProperty = serverSettingsType.GetRuntimeProperty("PunLogging"); - loggingProperty.SetValue(serverSettings, PhotonLogLevel.Full); - PropertyInfo networkLoggingProperty = serverSettingsType.GetRuntimeProperty("NetworkLogging"); - networkLoggingProperty.SetValue(serverSettings, ExitGames.Client.Photon.DebugLevel.ALL); - } - - PropertyInfo appIdProperty = serverSettingsType.GetRuntimeProperty("AppID"); - PropertyInfo voiceAppIdProperty = serverSettingsType.GetRuntimeProperty("VoiceAppID"); - - Util.ConditionalDebug($"New Photon AppID: '{PhotonConfig.AppID.Value}'"); - Util.ConditionalDebug($"New Photon AppID: '{PhotonConfig.VoiceAppID.Value}'"); - appIdProperty.SetValue(serverSettings, PhotonConfig.AppID.Value); - voiceAppIdProperty.SetValue(serverSettings, PhotonConfig.VoiceAppID.Value); - - if (PhotonConfig.SelfHosted.Value) - { - MethodInfo useMyServerMethod = serverSettingsType.GetMethod("UseMyServer"); - useMyServerMethod.Invoke(serverSettings, [ - PhotonConfig.ServerAddress.Value, - PhotonConfig.ServerPort.Value, - PhotonConfig.AppID.Value, - ]); - - Type authValuesType = AccessTools.TypeByName("AuthenticationValues"); - if (authValuesType != null) - { - PropertyInfo networkingPeerProperty = photonNetworkType.GetRuntimeProperty("networkingPeer"); - if (networkingPeerProperty == null) - { - UniversalPatchPlugin.Log.LogError("Cannot patch Photon: networkingPeerProperty was null. Is this build supported?"); - return; - } - - if (networkingPeerProperty.GetValue(null) == null) - { - UniversalPatchPlugin.Log.LogError("Cannot patch Photon: networkingPeerInstance was null. Is this build supported?"); - return; - } - - string id = Util.LocalInstanceGuid; - Util.ConditionalDebug($"Instance GUID is {id}"); - - PhotonNetwork.AuthValues = new AuthenticationValues - { - UserId = id - }; - - Util.ConditionalDebug($"Set the authValues userId to {id}"); - } - } - - UniversalPatchPlugin.Log.LogInfo($"Photon patch ({(PhotonConfig.SelfHosted.Value ? "self-hosted" : "cloud")}) succeeded."); - } - } -} diff --git a/Plugin.cs b/Plugin.cs index 625c198..4a7c9e8 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -6,7 +6,7 @@ using undead_universal_patch_il2cpp.Core; namespace undead_universal_patch_il2cpp; -[BepInPlugin("dev.proxnet.recroom.universalpatch.noneac.il2cpp", "Undead Universal Patch", "2.0.0")] +[BepInPlugin("dev.proxnet.recroom.universalpatch.noneac.il2cpp", "Undead Universal Patch", "2.1.0")] public class UniversalPatchPlugin : BasePlugin { public static new readonly ManualLogSource Log = Logger.CreateLogSource("UUPatch"); diff --git a/undead-universal-patch-il2cpp.csproj b/undead-universal-patch-il2cpp.csproj index d396b41..d3a755c 100644 --- a/undead-universal-patch-il2cpp.csproj +++ b/undead-universal-patch-il2cpp.csproj @@ -4,7 +4,7 @@ net6.0 undead_universal_patch_il2cpp Non-EAC, IL2CPP build patcher for Rec Room (Late 2018*-*April 2020) - 2.0.0 + 2.1.0 true latest @@ -18,6 +18,7 @@ +