From 8d1bac82017cc10b45b444b9949497d16719e864 Mon Sep 17 00:00:00 2001 From: zombieb Date: Sun, 13 Apr 2025 02:11:08 -0400 Subject: [PATCH] - SignalR handshake fix - Self-hosted PhotonSocketServer support - Split photon and hile event patch - Deprecated amplitude redirect patch --- BasePatches/BestHTTP.cs | 15 +- BasePatches/HilePatch.cs | 29 +++- BasePatches/ImageSignature.cs | 4 - BasePatches/PhotonPatch.cs | 207 +++++++++++++++++++++++++++ BasePatches/PhotonPatchEvents.cs | 60 -------- BasePatches/SignalRHandshakeFix.cs | 4 - BasePatches/TLS.cs | 3 +- Config.cs | 13 +- Galvanic.cs | 2 +- Plugin.cs | 31 ++-- README.md | 19 ++- Util.cs | 11 ++ undead-universal-patch-il2cpp.csproj | 5 +- 13 files changed, 304 insertions(+), 99 deletions(-) create mode 100644 BasePatches/PhotonPatch.cs delete mode 100644 BasePatches/PhotonPatchEvents.cs create mode 100644 Util.cs diff --git a/BasePatches/BestHTTP.cs b/BasePatches/BestHTTP.cs index d8a4242..3f4532b 100644 --- a/BasePatches/BestHTTP.cs +++ b/BasePatches/BestHTTP.cs @@ -59,26 +59,19 @@ namespace undead_universal_patch_il2cpp.Patches if (newUri.ToString().Contains("ns.rec.net")) newUri = new Il2CppSystem.Uri(NameserverConfig.NewUrl.Value); - if (newUri.ToString().Contains("api.amplitude")) - { - Il2CppSystem.UriBuilder replaceUri = new(new Il2CppSystem.Uri(NameserverConfig.NewUrl.Value)); - replaceUri.Path = "/amplitude"; - newUri = replaceUri.Uri; - } - if (GalvanicConfig.Enabled.Value) { string[] applyHeader = [ - "authservice/cachedlogin/forplatformid", - "accountservice/account/create", - "authservice/connect/token" + "/cachedlogin/forplatformid", + "/account/create", + "/connect/token" ]; foreach (string header in applyHeader) { if (newUri.PathAndQuery.Contains(header)) { // refresh the token if it expired - // this is somewhat inefficient, but we don't hook into many requests (see below) so it should be fine + // this is somewhat inefficient, but we don't hook into many requests (see above) so it should be fine Galvanic.GalvanicWebAuth.TokenExpiry(); Type httpRequestType = request.GetType(); diff --git a/BasePatches/HilePatch.cs b/BasePatches/HilePatch.cs index 9d562ca..f3cf6a5 100644 --- a/BasePatches/HilePatch.cs +++ b/BasePatches/HilePatch.cs @@ -1,11 +1,34 @@ -using System.Collections.Generic; -using System.Reflection; +using System.Reflection; using HarmonyLib; -using Il2CppSystem; using UnityEngine; namespace undead_universal_patch_il2cpp.Patches { + [HarmonyPatch] + public class ConnectToRecNetPatchEvent + { + static readonly string TargetTypeName = "RecNet.Core"; + static readonly string TargetMethodName = "ConnectToRecNet"; + static readonly string Description = "Hile Patch event method"; // It's convenient. Could patch at a different time. But this part was easy. + static readonly MethodInfo connectMethod = AccessTools.Method(AccessTools.TypeByName(TargetTypeName), TargetMethodName); + + static bool Prepare() + { + if (connectMethod == null) + { + Plugin.Log.LogWarning($"'{Description}' disabled. The method for this patch was not found."); + return false; + } + + Plugin.Log.LogInfo($"'{Description}' succeeded validation."); + return true; + } + static MethodBase TargetMethod() => connectMethod; + static void Prefix() + { + if (GenericConfig.HilePatch.Value) HilePatch.Patch(); + } + } public static class HilePatch { public static void Patch() diff --git a/BasePatches/ImageSignature.cs b/BasePatches/ImageSignature.cs index 6b69d33..e501192 100644 --- a/BasePatches/ImageSignature.cs +++ b/BasePatches/ImageSignature.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using HarmonyLib; using System.Reflection; diff --git a/BasePatches/PhotonPatch.cs b/BasePatches/PhotonPatch.cs new file mode 100644 index 0000000..6ff76a8 --- /dev/null +++ b/BasePatches/PhotonPatch.cs @@ -0,0 +1,207 @@ +using System; +using System.Reflection; +using BepInEx.Configuration; +using HarmonyLib; +using Il2CppSystem.Security.Cryptography; +using RecNet; +using UnityEngine; + +namespace undead_universal_patch_il2cpp.Patches +{ + [HarmonyPatch] + public class PhotonPatchEvent + { + static readonly string TargetTypeName = "PhotonNetwork"; + static readonly string TargetMethodName = "ConnectUsingSettings"; + static readonly string Description = "Photon ConnectUsingSettings patch event"; + static readonly Type targetType = AccessTools.TypeByName(TargetTypeName); + static readonly MethodInfo targetMethod = AccessTools.Method(targetType, TargetMethodName); + + static bool Prepare() + { + if (!PhotonConfig.PatchPhotonIds.Value) return false; + if (targetType == null) + { + Plugin.Log.LogWarning($"'{Description}' disabled. The type for this patch was not found."); + return false; + } + if (targetMethod == null) + { + Plugin.Log.LogWarning($"'{Description}' disabled. The method for this patch was not found."); + return false; + } + + Plugin.Log.LogInfo($"'{Description}' succeeded validation."); + return true; + } + + static MethodInfo TargetMethod() => targetMethod; + + static void Prefix() + { + PhotonPatch.Patch(); + } + } + + [HarmonyPatch] + public class ForceSelfHostedPhoton + { + class HarmonyState + { + public object code; + } + + static readonly string TargetTypeName = "PUNNetworkManager"; + static readonly string TargetMethodName = "OnConnectedToMaster"; + static readonly string Description = "Force JoinOrCreateRoom when connected to master"; + static readonly Type targetType = AccessTools.TypeByName(TargetTypeName); + static readonly MethodInfo targetMethod = AccessTools.Method(targetType, TargetMethodName); + + static bool Prepare() + { + if (!(PhotonConfig.PatchPhotonIds.Value && PhotonConfig.SelfHosted.Value)) return false; + if (targetType == null) + { + Plugin.Log.LogWarning($"'{Description}' disabled. The type for this patch was not found."); + return false; + } + if (targetMethod == null) + { + Plugin.Log.LogWarning($"'{Description}' disabled. The method for this patch was not found."); + return false; + } + + Plugin.Log.LogInfo($"'{Description}' succeeded validation."); + return true; + } + + static MethodBase TargetMethod() => targetMethod; + + static void Prefix(ref PUNNetworkManager __instance, ref HarmonyState __state) + { + __state = new HarmonyState(); + PropertyInfo targetGameSessionProperty = __instance.GetType().GetRuntimeProperty("targetGameSession"); + if (targetGameSessionProperty == null) + { + Plugin.Log.LogFatal("Cannot force masterserver: targetGameSessionProperty was null."); + return; + } + var targetGameSession = targetGameSessionProperty.GetValue(__instance); + if (targetGameSession == null) + { + Plugin.Log.LogFatal("Cannot force masterserver: targetGameSession was null."); + return; + } + PropertyInfo photonRegionIdProperty = targetGameSession.GetType().GetRuntimeProperty("PhotonRegionId"); + if (photonRegionIdProperty == null) + { + Plugin.Log.LogFatal("Cannot force masterserver: photonRegionIdProperty was null."); + return; + } + + __state.code = photonRegionIdProperty.GetValue(targetGameSession); + photonRegionIdProperty.SetValue(targetGameSession, 4); + Plugin.Log.LogDebug("Forcing masterserver"); + } + + static void Postfix(ref PUNNetworkManager __instance, ref HarmonyState __state) + { + PropertyInfo targetGameSessionProperty = __instance.GetType().GetRuntimeProperty("targetGameSession"); + if (targetGameSessionProperty == null) + { + Plugin.Log.LogFatal("Cannot force masterserver postfix: targetGameSessionProperty was null."); + return; + } + var targetGameSession = targetGameSessionProperty.GetValue(__instance); + if (targetGameSession == null) + { + Plugin.Log.LogFatal("Cannot force masterserver postfix: targetGameSession was null."); + return; + } + PropertyInfo photonRegionIdProperty = targetGameSession.GetType().GetRuntimeProperty("PhotonRegionId"); + if (photonRegionIdProperty == null) + { + Plugin.Log.LogFatal("Cannot force masterserver: photonRegionIdProperty was null."); + return; + } + + photonRegionIdProperty.SetValue(targetGameSession, __state.code); + Plugin.Log.LogDebug("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) + { + Plugin.Log.LogError("Cannot patch Photon: PhotonNetwork or ServerSettings types were not found. Is this build supported?"); + return; + // safe to reference enums proceeding this + } + + 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"); + + 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 userIdProperty = authValuesType.GetRuntimeProperty("UserId"); + + PropertyInfo networkingPeerProperty = photonNetworkType.GetRuntimeProperty("networkingPeer"); + if (networkingPeerProperty == null) + { + Plugin.Log.LogError("Cannot patch Photon: networkingPeerProperty was null. Is this build supported?"); + return; + } + + if (networkingPeerProperty.GetValue(null) == null) + { + Plugin.Log.LogError("Cannot patch Photon: networkingPeerInstance was null. Is this build supported?"); + return; + } + + string id = Util.LocalInstanceGuid; + Plugin.Log.LogDebug($"Instance GUID is {id}"); + + PhotonNetwork.AuthValues = new AuthenticationValues(); + PhotonNetwork.AuthValues.UserId = id; + + Plugin.Log.LogDebug($"Set the authValues userId to {id}"); + } + + Plugin.Log.LogInfo($"Photon patch ({(PhotonConfig.SelfHosted.Value ? "self-hosted" : "cloud")}) succeeded."); + } + } +} diff --git a/BasePatches/PhotonPatchEvents.cs b/BasePatches/PhotonPatchEvents.cs deleted file mode 100644 index 234e471..0000000 --- a/BasePatches/PhotonPatchEvents.cs +++ /dev/null @@ -1,60 +0,0 @@ -using HarmonyLib; -using System; -using System.Reflection; - -namespace undead_universal_patch_il2cpp.Patches -{ - [HarmonyPatch] - public class ConnectToRecNetPatchEvent - { - static readonly string TargetTypeName = "RecNet.Core"; - static readonly string TargetMethodName = "ConnectToRecNet"; - static readonly string Description = "Photon/Hile Patch event method"; // It's convenient. Could patch at a different time. But this part was easy. - static readonly MethodInfo connectMethod = AccessTools.Method(AccessTools.TypeByName(TargetTypeName), TargetMethodName); - - static bool Prepare() - { - if (connectMethod == null) - { - Plugin.Log.LogWarning($"'{Description}' disabled. The method for this patch was not found."); - return false; - } - - Plugin.Log.LogInfo($"'{Description}' succeeded validation."); - return true; - } - static MethodBase TargetMethod() => connectMethod; - static void Prefix() - { - if (PhotonConfig.PatchPhotonIds.Value) PhotonPatch.Patch(); - if (GenericConfig.HilePatch.Value) HilePatch.Patch(); - } - } - - public class PhotonPatch - { - public static void Patch() - { - Type photonNetworkType = AccessTools.TypeByName("PhotonNetwork"); - Type serverSettingsType = AccessTools.TypeByName("ServerSettings"); - - if (photonNetworkType == null || serverSettingsType == null) - { - Plugin.Log.LogError("Cannot patch Photon: PhotonNetwork or ServerSettings types were not found. Is this build supported?"); - return; - } - - // The property PhotonServerSettings (which should be a field) didn't appear when I didn't specify `Runtime`. Not sure why. But this works. - PropertyInfo photonServerSettingsProperty = photonNetworkType.GetRuntimeProperty("PhotonServerSettings"); - object serverSettings = photonServerSettingsProperty.GetValue(serverSettingsType); - - PropertyInfo appIdField = serverSettingsType.GetRuntimeProperty("AppID"); - PropertyInfo voiceAppIdField = serverSettingsType.GetRuntimeProperty("VoiceAppID"); - - appIdField.SetValue(serverSettings, PhotonConfig.AppID.Value); - voiceAppIdField.SetValue(serverSettings, PhotonConfig.VoiceAppID.Value); - - Plugin.Log.LogInfo("Photon patch succeeded."); - } - } -} diff --git a/BasePatches/SignalRHandshakeFix.cs b/BasePatches/SignalRHandshakeFix.cs index 8efd643..71b810d 100644 --- a/BasePatches/SignalRHandshakeFix.cs +++ b/BasePatches/SignalRHandshakeFix.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; using HarmonyLib; namespace undead_universal_patch_il2cpp.Patches diff --git a/BasePatches/TLS.cs b/BasePatches/TLS.cs index 0b79fee..0af88fe 100644 --- a/BasePatches/TLS.cs +++ b/BasePatches/TLS.cs @@ -15,6 +15,7 @@ namespace undead_universal_patch_il2cpp.Patches static bool Prepare() { + if (!GenericConfig.CertificatePatch.Value) return false; if (targetMethod == null) { Plugin.Log.LogWarning($"'{Description}' disabled. The type for this patch was not found."); @@ -34,7 +35,7 @@ namespace undead_universal_patch_il2cpp.Patches static bool Prefix() { - return !(GenericConfig.CertificatePatch.Value); + return false; } } diff --git a/Config.cs b/Config.cs index 453f3e4..a458332 100644 --- a/Config.cs +++ b/Config.cs @@ -8,7 +8,6 @@ namespace undead_universal_patch_il2cpp public static ConfigEntry LogAllRequests; public static ConfigEntry CertificatePatch; public static ConfigEntry HilePatch; - public static ConfigEntry AmplitudeRedirectPatch; public static ConfigEntry SignalRHandshakeFix; public static ConfigEntry ImageSignaturePatch; } @@ -17,8 +16,7 @@ namespace undead_universal_patch_il2cpp public static bool LogAllRequests = false; public static bool CertificatePatch = false; public static bool HilePatch = false; - public static bool AmplitudeRedirectPatch = true; - public static bool SignalRHandshakeFix = true; + public static bool SignalRHandshakeFix = false; public static bool ImageSignaturePatch = false; } public static class NameserverConfig @@ -34,14 +32,22 @@ namespace undead_universal_patch_il2cpp public static class PhotonConfig { public static ConfigEntry PatchPhotonIds; + public static ConfigEntry PunLogging; + public static ConfigEntry SelfHosted; public static ConfigEntry AppID; public static ConfigEntry VoiceAppID; + public static ConfigEntry ServerAddress; + public static ConfigEntry ServerPort; } public static class PhotonConfigDefaults { public static bool PatchPhotonIds = false; + public static bool PunLogging = false; + public static bool SelfHosted = false; public static string AppID = "replace-me-please"; public static string VoiceAppID = "replace-me-please"; + public static string ServerAddress = "127.0.0.1"; + public static int ServerPort = 5055; } public static class GalvanicConfig @@ -59,4 +65,5 @@ namespace undead_universal_patch_il2cpp public static string Export = "IDoNotWantToExportMyKeys"; public static bool Import = false; } + } diff --git a/Galvanic.cs b/Galvanic.cs index fd3a240..97c6a9f 100644 --- a/Galvanic.cs +++ b/Galvanic.cs @@ -205,7 +205,7 @@ namespace undead_universal_patch_il2cpp.Galvanic { byte[] buffer = new byte[128]; new System.Random().NextBytes(buffer); - return System.Convert.ToBase64String(buffer).Substring(0, 128); + return Convert.ToBase64String(buffer).Substring(0, 128); // god help me } diff --git a/Plugin.cs b/Plugin.cs index 003de7c..8c6f9b0 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -1,4 +1,4 @@ -using AmplitudeAnalytics; +using System; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; @@ -6,7 +6,7 @@ using HarmonyLib; namespace undead_universal_patch_il2cpp; -[BepInPlugin("dev.proxnet.recroom.universalpatch.noneac.il2cpp", "Undead Universal Patch", "1.2.0")] +[BepInPlugin("dev.proxnet.recroom.universalpatch.noneac.il2cpp", "Undead Universal Patch", "1.3.0")] public class Plugin : BasePlugin { public static new readonly ManualLogSource Log = Logger.CreateLogSource("UUPatch"); @@ -22,26 +22,39 @@ public class Plugin : BasePlugin public override void Load() { + Util.LocalInstanceGuid = Guid.NewGuid().ToString(); + + Log.LogInfo("It's time to Room some Rec and chew bubble gum, and I'm all out of gum"); + GenericConfig.LogAllRequests = Config.Bind("Generic", "LogAllRequests", GenericConfigDefaults.LogAllRequests, "Log all HTTP requests sent by the game."); GenericConfig.CertificatePatch = Config.Bind("Generic", "CertificatePatch", GenericConfigDefaults.CertificatePatch, "The game expects a certain certificate from rec.net when making HTTPS requests. Enable this to allow any valid certificate."); GenericConfig.HilePatch = Config.Bind("Generic", "HilePatch", GenericConfigDefaults.HilePatch, "The game will close after a short period of time if BepInEx is found. Enable to stop this from happening."); - GenericConfig.AmplitudeRedirectPatch = Config.Bind("Generic", "AmplitudeRedirectPatch", GenericConfigDefaults.AmplitudeRedirectPatch, - "Redirect all Amplitude API requests to the nameserver on '/amplitude'."); GenericConfig.SignalRHandshakeFix = Config.Bind("Generic", "SignalRHandshakeFix", GenericConfigDefaults.SignalRHandshakeFix, "Replace apostrophes with quotes in the initial SignalR handshake."); GenericConfig.ImageSignaturePatch = Config.Bind("Generic", "ImageSignaturePatch", GenericConfigDefaults.ImageSignaturePatch, "When enabled, all image signatures will be valid." + "\nWorks only if the server appends a properly formatted signature header (signature does not need to be valid)"); + GenericConfig PhotonConfig.PatchPhotonIds = Config.Bind("Photon", "PatchPhotonIds", PhotonConfigDefaults.PatchPhotonIds, - "Enable/disable changing the target IDs in PhotonServerSettings." + - "\nCustom server settings are not yet supported."); + "Patch Photon configuration."); + PhotonConfig.PunLogging = Config.Bind("Photon", "PunLogging", PhotonConfigDefaults.PunLogging, + "Enable all logging sent by Photon/PUN/Voice (useful for server debugging)"); + PhotonConfig.SelfHosted = Config.Bind("Photon", "IsSelfHosted", PhotonConfigDefaults.SelfHosted, + "When enabled, use a self-hosted 'OnPremises' PhotonSocketServer. (EXPERIMENTAL)" + + "\nWhen disabled, AppID and VoiceAppID are sent to Photon Cloud and a cloud masterserver is used."); PhotonConfig.AppID = Config.Bind("Photon", "AppID", PhotonConfigDefaults.AppID, - "The new target (PUN) App ID from the Photon dashboard."); + "The new target (PUN) App ID from the Photon dashboard." + + "\nWhen self-hosting, this should be the name of your application (either 'Master' or 'Game')"); PhotonConfig.VoiceAppID = Config.Bind("Photon", "VoiceAppID", PhotonConfigDefaults.VoiceAppID, - "The new target Voice App ID from the Photon dashboard."); + "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 = Config.Bind("Photon", "ServerAddress", PhotonConfigDefaults.ServerAddress, + "Address of the Photon master server (ignored if not using self-hosted)"); + PhotonConfig.ServerPort = Config.Bind("Photon", "ServerPort", PhotonConfigDefaults.ServerPort, + "Photon master server UDP port (ignored if not using self-hosted)"); NameserverConfig.Rewrite = Config.Bind("Nameserver", "Rewrite", NameserverConfigDefaults.Rewrite, "Enable/disable rewriting the URL for nameserver requests."); NameserverConfig.NewUrl = Config.Bind("Nameserver", "NewUrl", NameserverConfigDefaults.NewUrl, @@ -71,5 +84,7 @@ public class Plugin : BasePlugin if (GalvanicConfig.Export.Value == "IWantToExportMyKeys") Galvanic.GalvanicAuth.Export(); if (GalvanicConfig.Import.Value) Galvanic.GalvanicAuth.Import(); if (GalvanicConfig.Enabled.Value) Galvanic.GalvanicWebAuth.GetToken(); + + Log.LogInfo("Undead Universal IL2CPP Patch for Rec Room by @zombieb; loaded."); } } \ No newline at end of file diff --git a/README.md b/README.md index 3702d63..d63b810 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,21 @@ Currently a work-in-progress and is not implemented Unlike the Mono patch, some assemblies must be referenced. Run BepInEx interop on your build and add the following assemblies to new folder `.\AssemblyReferences\` in this project directory: +* `BepInEx/interop/Assembly-CSharp.dll` * `BepInEx/interop/Il2Cppmscorlib.dll` * `BepInEx/interop/Il2CppSystem.dll` -* (from [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL)) `cpp2il_out/UnityEngine.CoreModule.dll` -* (from [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL)) `cpp2il_out/Assembly-CSharp.dll` -* (from [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL)) `cpp2il_out/RecRoom.Datastructures.Runtime` \ No newline at end of file +* `BepInEx/interop/Photon3Unity3D.dll` +* `BepInEx/interop/RecRoom.Datastructures.Runtime` +* `BepInEx/interop/UnityEngine.CoreModule.dll` + +### Linux users +Wine currently has problems generating the keypair used in Galvanic authentication. + +You can use a real Windows system to generate the keys for the first startup. + +If you're unsure how to start your build on linux: +* Install Steam +* Add Rec Room as a non-steam game +* Set the compatibility mode to the latest stable version of proton +* Use protontricks to add the doorstop DLL +* Start Rec Room through Steam \ No newline at end of file diff --git a/Util.cs b/Util.cs new file mode 100644 index 0000000..fb080ef --- /dev/null +++ b/Util.cs @@ -0,0 +1,11 @@ +using System; +using System.Reflection; +using HarmonyLib; + +namespace undead_universal_patch_il2cpp +{ + public class Util + { + public static string LocalInstanceGuid { set; get; } + } +} diff --git a/undead-universal-patch-il2cpp.csproj b/undead-universal-patch-il2cpp.csproj index 42d4a1e..bd2a222 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) - 1.2.0 + 1.3.0 true latest @@ -29,6 +29,9 @@ AssemblyReferences\Il2CppSystem.dll + + AssemblyReferences\Photon3Unity3D.dll + AssemblyReferences\RecRoom.Datastructures.Runtime.dll