From e9a241d74295621949faf6326fc898d6b4e1196f Mon Sep 17 00:00:00 2001 From: zombieb Date: Sat, 22 Mar 2025 21:20:04 -0400 Subject: [PATCH] base vs future configurable patches, add amplitude redirect and signalr handshake patches, galvanic corrosion support, --- .gitignore | 4 +- {Patches => BasePatches}/BestHTTP.cs | 44 +++++- BasePatches/GenuinePatch.cs | 21 +++ BasePatches/HilePatch.cs | 34 +++++ BasePatches/PhotonPatch.cs | 61 ++++++++ BasePatches/SignalRHandshakeFix.cs | 45 ++++++ {Patches => BasePatches}/TLS.cs | 0 Config.cs | 19 ++- Galvanic.cs | 201 +++++++++++++++++++++++++++ Patches/HilePatch.cs | 63 --------- Patches/Photon.cs | 50 ------- Patches/RealPhotonPatchEvent.cs | 15 -- Plugin.cs | 26 +++- README.md | 18 ++- undead-universal-patch-il2cpp.csproj | 23 +-- 15 files changed, 472 insertions(+), 152 deletions(-) rename {Patches => BasePatches}/BestHTTP.cs (58%) create mode 100644 BasePatches/GenuinePatch.cs create mode 100644 BasePatches/HilePatch.cs create mode 100644 BasePatches/PhotonPatch.cs create mode 100644 BasePatches/SignalRHandshakeFix.cs rename {Patches => BasePatches}/TLS.cs (100%) create mode 100644 Galvanic.cs delete mode 100644 Patches/HilePatch.cs delete mode 100644 Patches/Photon.cs delete mode 100644 Patches/RealPhotonPatchEvent.cs diff --git a/.gitignore b/.gitignore index 9491a2f..12221bb 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,6 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd + +AssemblyReferences/ \ No newline at end of file diff --git a/Patches/BestHTTP.cs b/BasePatches/BestHTTP.cs similarity index 58% rename from Patches/BestHTTP.cs rename to BasePatches/BestHTTP.cs index 531bc6a..0718d4b 100644 --- a/Patches/BestHTTP.cs +++ b/BasePatches/BestHTTP.cs @@ -34,30 +34,60 @@ namespace undead_universal_patch_il2cpp.Patches static MethodBase TargetMethod() => targetMethod; [HarmonyPrefix] - static bool Prefix(ref object request) + static void Prefix(ref object request) { PropertyInfo uriProperty = AccessTools.Property(requestType, "Uri"); if (uriProperty == null) { Plugin.Log.LogFatal("BestHTTP_Unob failed: uriProperty was null."); - return false; + return; } var uriInstance = (Il2CppSystem.Uri)uriProperty.GetValue(request, null); if (uriInstance == null) { Plugin.Log.LogFatal("BestHTTP_Unob failed: uriInstance was null."); - return false; + return; } - if (GenericConfig.LogAllRequests.Value) Plugin.Log.LogInfo($"BestHTTP_Unob request (Before) URL: {uriInstance.ToString()}"); + if (GenericConfig.LogAllRequests.Value) Plugin.Log.LogInfo($"BestHTTP_Unob request b-URL: {uriInstance.ToString()}"); + if (!NameserverConfig.Rewrite.Value) return; - Il2CppSystem.Uri newUri = new(uriInstance.ToString().Contains("ns.rec.net") ? NameserverConfig.NewUrl.Value : uriInstance.ToString()); + Il2CppSystem.Uri newUri = new(uriInstance.ToString()); - if (GenericConfig.LogAllRequests.Value) Plugin.Log.LogInfo($"BestHTTP_Unob request (After) URL: {newUri.ToString()}"); + // Request changes below + 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" + ]; + foreach (string header in applyHeader) + { + if (newUri.PathAndQuery.Contains(header)) + { + Type httpRequestType = request.GetType(); + MethodInfo addHeaderMethod = httpRequestType.GetMethod("AddHeader"); + addHeaderMethod.Invoke(request, ["GalvanicAuth", GalvanicWebAuth.Token]); + + break; + } + } + } + + if (GenericConfig.LogAllRequests.Value) Plugin.Log.LogInfo($"BestHTTP_Unob request a-URL: {newUri.ToString()}"); uriProperty.SetValue(request, NameserverConfig.Rewrite.Value ? newUri : uriInstance, null); - return true; } } } diff --git a/BasePatches/GenuinePatch.cs b/BasePatches/GenuinePatch.cs new file mode 100644 index 0000000..c4ec2c3 --- /dev/null +++ b/BasePatches/GenuinePatch.cs @@ -0,0 +1,21 @@ +using HarmonyLib; +using System.Reflection; + +namespace undead_universal_patch_il2cpp.Patches +{ + [HarmonyPatch] + class GenuinePatch + { + static readonly MethodInfo methodType = AccessTools.Method(AccessTools.TypeByName("Application"), "get_productName"); + static bool Prepare() + { + if (methodType == null) return false; + else return true; + } + static MethodBase TargetMethod() => methodType; + static void Prefix(ref string __result) + { + __result = string.Format("{0} (Custom Server)", __result); + } + } +} diff --git a/BasePatches/HilePatch.cs b/BasePatches/HilePatch.cs new file mode 100644 index 0000000..9d562ca --- /dev/null +++ b/BasePatches/HilePatch.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Reflection; +using HarmonyLib; +using Il2CppSystem; +using UnityEngine; + +namespace undead_universal_patch_il2cpp.Patches +{ + public static class HilePatch + { + public static void Patch() + { + GameObject cheatManagerObject = GameObject.Find("[CheatManager]"); + HileManager hileManager = cheatManagerObject.GetComponent(); + + PropertyInfo knownDllsProperty = AccessTools.Property(typeof(HileManager), "KnownDlls"); + knownDllsProperty.SetValue(hileManager, new Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStringArray([ + "GameAssembly.dll", + "UnityPlayer.dll", + "WinPixEventRuntime.dll", + "steam_api64.dll", + "steam_api.dll", + "d3d11.dll", + "d3d9.dll", + "d3d8.dll", + "ddraw.dll", + "dxgi.dll", + "winhttp.dll" + ])); + + Plugin.Log.LogInfo("Hile patch succeeded."); + } + } +} diff --git a/BasePatches/PhotonPatch.cs b/BasePatches/PhotonPatch.cs new file mode 100644 index 0000000..0cf6ef6 --- /dev/null +++ b/BasePatches/PhotonPatch.cs @@ -0,0 +1,61 @@ +using HarmonyLib; +using System; +using System.ComponentModel; +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 new file mode 100644 index 0000000..1c2881a --- /dev/null +++ b/BasePatches/SignalRHandshakeFix.cs @@ -0,0 +1,45 @@ +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 +{ + [HarmonyPatch] + public class SignalRHandshakeFix + { + public static string TargetTypeName = "JsonProtocol"; + public static string TargetMethodName = "WithSeparator"; + public static string Description = "SignalR Handshake Fix (quotes vs apostrophes)"; + public static Type targetType = AccessTools.TypeByName(TargetTypeName); + public static MethodBase targetMethod = AccessTools.Method(targetType, TargetMethodName); + + public static bool Prepare() + { + 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; + } + public static MethodBase TargetMethod() => targetMethod; + public static void Prefix(ref string str) + { + if (str.Contains("protocol':")) + { + str = str.Replace("'", "\""); + } + } + } +} diff --git a/Patches/TLS.cs b/BasePatches/TLS.cs similarity index 100% rename from Patches/TLS.cs rename to BasePatches/TLS.cs diff --git a/Config.cs b/Config.cs index b4ea6a7..df329b9 100644 --- a/Config.cs +++ b/Config.cs @@ -1,4 +1,5 @@ -using BepInEx.Configuration; +using System; +using BepInEx.Configuration; namespace undead_universal_patch_il2cpp { @@ -7,12 +8,16 @@ 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 class GenericConfigDefaults { 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 class NameserverConfig { @@ -36,4 +41,16 @@ namespace undead_universal_patch_il2cpp public static string AppID = "replace-me-please"; public static string VoiceAppID = "replace-me-please"; } + + public static class GalvanicConfig + { + public static ConfigEntry Enabled; + public static ConfigEntry RegenerateKeypair; + } + + public static class GalvanicConfigDefaults + { + public static bool Enabled = false; + public static bool RegenerateKeypair = false; + } } diff --git a/Galvanic.cs b/Galvanic.cs new file mode 100644 index 0000000..6e476e6 --- /dev/null +++ b/Galvanic.cs @@ -0,0 +1,201 @@ +using System; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text; +using UnityEngine; +using System.Text.Json; + +// this entire file could be better + +namespace undead_universal_patch_il2cpp +{ + public class GalvanicAuth + { + public static void PrepareKeys() + { + PlayerPrefs.DeleteKey("GalvanicPublicKey"); + PlayerPrefs.DeleteKey("GalvanicPrivateKey"); + using (ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256)) + { + byte[] publicKeyBytes = ecdsa.ExportSubjectPublicKeyInfo(); + string publicKeyBase64 = Convert.ToBase64String(publicKeyBytes); + + byte[] privateKeyBytes = ecdsa.ExportPkcs8PrivateKey(); + string privateKeyBase64 = Convert.ToBase64String(privateKeyBytes); + + PlayerPrefs.SetString("GalvanicPublicKey", publicKeyBase64); + PlayerPrefs.SetString("GalvanicPrivateKey", privateKeyBase64); + PlayerPrefs.Save(); + + Plugin.Log.LogInfo("Created new keypair for Galvanic authentication."); + } + } + + public static string GetPubKey() + { + string publicKey = PlayerPrefs.GetString("GalvanicPublicKey", null); + if (string.IsNullOrEmpty(publicKey)) + { + Plugin.Log.LogInfo("No keypair found, generating new."); + PrepareKeys(); + return GetPubKey(); + } else return publicKey; + } + + private static ECDsa GetPrivKey() + { + string privateKey = PlayerPrefs.GetString("GalvanicPrivateKey", null); + if (string.IsNullOrEmpty(privateKey)) + { + Plugin.Log.LogInfo("No keypair found, generating new."); + PrepareKeys(); + return GetPrivKey(); + } + + byte[] privKeyBytes = Convert.FromBase64String(privateKey); + ECDsa crypt = ECDsa.Create(); + crypt.ImportPkcs8PrivateKey(privKeyBytes, out _); + return crypt; + } + + public static string SignPayload(string message) + { + using (ECDsa ecdsa = GetPrivKey()) + { + if (ecdsa == null) return null; + + byte[] payloadBytes = Encoding.UTF8.GetBytes(message); + byte[] signature = ecdsa.SignData(payloadBytes, HashAlgorithmName.SHA256); + + return Convert.ToBase64String(signature); + } + } + public static ServerInfoRes GetServerInfo() + { + UriBuilder nameserver = new(NameserverConfig.NewUrl.Value); + nameserver.Path = "/info"; + nameserver.Query = ""; + + using HttpClient client = new(); + var response = client.GetAsync(nameserver.ToString()).Result; + if (!response.IsSuccessStatusCode) return null; + + var str = response.Content.ReadAsStringAsync().Result; + var res = JsonSerializer.Deserialize(str); + + GalvanicCorrosion.Name = res.name; + GalvanicCorrosion.Id = res.id; + GalvanicCorrosion.Motd = res.motd; + GalvanicCorrosion.Patches = res.patches; + + return res; + } + } + + public static class GalvanicCorrosion + { + public static string Name { get; set; } = "Galvanic Corrosion"; + public static string Id { get; set; } = "galvanic-corrosion-default"; + public static string Motd { get; set; } = "A Galvanic Corrosion server."; + public static string[] Patches { get; set; } = []; + } + + public static class GalvanicWebAuth + { + public static string Token { get; private set; } = null; + + public static void GetToken() + { + string url = NameserverConfig.NewUrl.Value; + UriBuilder uri = new(url); + uri.Path = "/user/auth"; + uri.Query = ""; + + var info = GalvanicAuth.GetServerInfo(); + Plugin.Log.LogInfo($"Sending authentication request to server ID '{info.id}'"); + + UserAuthPayload payload = new UserAuthPayload + { + timestamp = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(), + nonce = Guid.NewGuid().ToString(), + server_id = info.id, + }; + + UserAuthRequest request = new UserAuthRequest + { + message = payload, + signature = GalvanicAuth.SignPayload($"{JsonSerializer.Serialize(payload)}"), + client_id = UniqueID.GetUniqueId(), + pubkey = GalvanicAuth.GetPubKey() + }; + + HttpClient client = new(); + HttpResponseMessage res = client.PostAsync(uri.ToString(), new StringContent(JsonSerializer.Serialize(request, + new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }), + Encoding.UTF8, + "application/json")).Result; + if (!res.IsSuccessStatusCode) + { + Plugin.Log.LogError("The GalvanicWeb auth response was not successful."); + return; + } + string str = res.Content.ReadAsStringAsync().Result; + TokenRes token = JsonSerializer.Deserialize(str); + + Token = token.token; + } + } + + public static class UniqueID + { + public static string GetUniqueId() + { + if (!PlayerPrefs.HasKey("UniqueId")) SaveUniqueId(GenerateUniqueId()); + return PlayerPrefs.GetString("UniqueId"); + } + public static void SaveUniqueId(string id) + { + PlayerPrefs.SetString("UniqueId", id); + PlayerPrefs.Save(); + } + public static string GenerateUniqueId() + { + byte[] buffer = new byte[128]; + new System.Random().NextBytes(buffer); + return System.Convert.ToBase64String(buffer).Substring(0, 128); + + // god help me + } + } + + public class ServerInfoRes + { + public string name { get; set; } + public string id { get; set; } + public string motd { get; set; } + public string[] patches { get; set; } + } + + class TokenRes + { + public string token { get; set; } + } + + class UserAuthPayload + { + public long timestamp { get; set; } + public string nonce { get; set; } + public string server_id { get; set; } + } + + class UserAuthRequest + { + public string client_id { get; set; } + public UserAuthPayload message { get; set; } + public string signature { get; set; } + public string pubkey { get; set; } + } +} \ No newline at end of file diff --git a/Patches/HilePatch.cs b/Patches/HilePatch.cs deleted file mode 100644 index d0fcc1a..0000000 --- a/Patches/HilePatch.cs +++ /dev/null @@ -1,63 +0,0 @@ -using HarmonyLib; -using System; -using System.Reflection; -using UnityEngine; - -namespace undead_universal_patch_il2cpp.Patches -{ - [HarmonyPatch] - public static class HilePatch - { - public static void Patch() - { - Plugin.Log.LogInfo("Attempting hile patch"); - - HileManager man = GameObject.Find("/GameRoot/Startup/Core Systems/[CheatManager]").GetComponent(); - - string[] asdf = [ - "GameAssembly.dll", - "UnityPlayer.dll", - "WinPixEventRuntime.dll", - "steam_api64.dll", - "steam_api.dll", - "d3d11.dll", - "d3d9.dll", - "d3d8.dll", - "ddraw.dll", - "dxgi.dll", - "winhttp.dll" - ]; - man.KnownDlls = asdf; - - Plugin.Log.LogInfo("Hile patch successful."); - } - - static string TargetTypeName = "BootSequence"; - static string TargetMethodName = "set_CurrentState"; - static string Description = "Add BepInEx to allowed DLLs"; - static Type targetType = AccessTools.TypeByName(TargetTypeName); - static MethodInfo targetMethod = AccessTools.Method(targetType, TargetMethodName); - - static bool Prepare() - { - if (targetMethod == null) - { - Plugin.Log.LogWarning($"'{Description}' disabled. The type for this patch was not found."); - return false; - } - - Plugin.Log.LogInfo($"'{Description}' succeeded validation."); - return true; - } - - static MethodBase TargetMethod() - { - return targetMethod; - } - - static void Prefix(ref BootSequence __instance) - { - if (GenericConfig.HilePatch.Value && __instance.CurrentState == BootSequence.BootSequenceState.RECNET_CONNECTION) Patch(); - } - } -} diff --git a/Patches/Photon.cs b/Patches/Photon.cs deleted file mode 100644 index fb73575..0000000 --- a/Patches/Photon.cs +++ /dev/null @@ -1,50 +0,0 @@ -using HarmonyLib; -using Il2CppInterop.Runtime; -using System; -using System.Collections.Generic; -using System.Reflection; -using UnityEngine; - -namespace undead_universal_patch_il2cpp.Patches -{ - [HarmonyPatch] - public class PhotonPatchEvent - { - static readonly string TargetTypeName = "RecNet.Core"; - static readonly string TargetMethodName = "ConnectToRecNet"; - static readonly string Description = "Photon Patch event method"; - static readonly MethodInfo connectMethod = AccessTools.Method(AccessTools.TypeByName(TargetTypeName), TargetMethodName); - - static bool Prepare() - { - if (connectMethod == null) - { - Plugin.Log.LogWarning($"'{Description}' disabled. The type 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) Photon.Patch(); - } - } - - public class Photon - { - public static void Patch() - { - /* - Normally, I would be using reflection and AccessTools to create a new instance of ServerSettings and set the value of PhotonNetwork.PhotonServerSettings, - but since it was difficult and confusing with IL2CPP, I ended up just using explicit references.. every targeted game build *should* have these types anyway. - Now I'm wondering if the patch runs faster when using explicit references rather than reflection - */ - - PhotonNetwork.PhotonServerSettings.AppID = PhotonConfig.AppID.Value; - PhotonNetwork.PhotonServerSettings.VoiceAppID = PhotonConfig.VoiceAppID.Value; - } - } -} diff --git a/Patches/RealPhotonPatchEvent.cs b/Patches/RealPhotonPatchEvent.cs deleted file mode 100644 index ceaa35f..0000000 --- a/Patches/RealPhotonPatchEvent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ExitGames.Client.Photon; -using HarmonyLib; -using System; - -namespace undead_universal_patch_il2cpp.Patches -{ - [HarmonyPatch(typeof(PhotonNetwork), nameof(PhotonNetwork.ConnectUsingSettings))] - public class RealPhotonPatchEvent - { - public static void Prefix() - { - Photon.Patch(); - } - } -} diff --git a/Plugin.cs b/Plugin.cs index 568f090..7e4e867 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -1,14 +1,16 @@ -using BepInEx; +using System; +using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; +using Il2CppSystem.Reflection; namespace undead_universal_patch_il2cpp; [BepInPlugin("dev.proxnet.recroom.universalpatch.noneac.il2cpp", "Undead Universal Patch", "1.0.0")] public class Plugin : BasePlugin { - public static new readonly ManualLogSource Log = BepInEx.Logging.Logger.CreateLogSource("UUPatch"); + public static new readonly ManualLogSource Log = Logger.CreateLogSource("UUPatch"); public static Harmony _hi = new("dev.proxnet.recroom.universalpatch.noneac.il2cpp"); @@ -24,9 +26,13 @@ public class Plugin : BasePlugin 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 SSL certificate from rec.net when making HTTPS requests. Enable this to allow any valid certificate."); + "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 send v1/hile and login+shutdown if winhttp.dll is found. Enable this to add winhttp.dll to the list of allowed DLLs."); + "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."); PhotonConfig.PatchPhotonIds = Config.Bind("Photon", "PatchPhotonIds", PhotonConfigDefaults.PatchPhotonIds, "Enable/disable changing the target IDs in PhotonServerSettings." + "\nCustom server settings are not yet supported."); @@ -38,7 +44,19 @@ public class Plugin : BasePlugin "Enable/disable rewriting the URL for nameserver requests."); NameserverConfig.NewUrl = Config.Bind("Nameserver", "NewUrl", NameserverConfigDefaults.NewUrl, "The new full URL to use when sending a nameserver request."); + GalvanicConfig.Enabled = Config.Bind("GalvanicCorrosion", "Enabled", GalvanicConfigDefaults.Enabled, + "Use Galvanic Corrosion features. Authenticates with a GC server." + + "\nThe host and protocol in Nameserver.NewUrl will be used for the server's base URL." + + "\nDo not enable if you either don't know what this does."); + GalvanicConfig.RegenerateKeypair = Config.Bind("GalvanicCorrosion", "RegenerateKeypair", GalvanicConfigDefaults.RegenerateKeypair, + "Regenerate the keypair upon startup. DO NOT ENABLE IF YOU WANT TO KEEP YOUR ACCOUNT."); _hi.PatchAll(); + if (GalvanicConfig.RegenerateKeypair.Value) + { + Log.LogInfo("Regenerating keypair"); + GalvanicAuth.PrepareKeys(); + } + if (GalvanicConfig.Enabled.Value) GalvanicWebAuth.GetToken(); } } \ No newline at end of file diff --git a/README.md b/README.md index b6894c6..6ffa184 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,23 @@ # Undead Universal Patch -Non-EAC, IL2CPP build patcher for Rec Room (Dec 2018\*-*Apr 2020) +Non-EAC, IL2CPP build patcher for Rec Room (Dec 2018\*-*Apr 3 2020) + +**Currently only supports non-obfuscated builds** Part two of two universal patches. The Mono patch is available at https://git.proxnet.dev/zombieb/undead-universal-patch-mono. ### BepInEx Development Build This patch requires that a certain build of BepInEx be used from their GitHub, available at https://github.com/BepInEx/BepInEx/actions/runs/9522461593. -**When submitting issues**, please submit your BepInEx log file with all log levels enabled. You can do this by setting `Logging.Disk.LogLevels` in `BepInEx.cfg` to `All`. \ No newline at end of file +**When submitting issues**, please submit your BepInEx log file with all log levels enabled. You can do this by setting `Logging.Disk.LogLevels` in `BepInEx.cfg` to `All`. + +### Patch IDs +* `removeInkLimit`: 'Remove' the limit of resources that can be used in a single room + +### Compiling +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/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` \ No newline at end of file diff --git a/undead-universal-patch-il2cpp.csproj b/undead-universal-patch-il2cpp.csproj index 20f4de1..79cdad6 100644 --- a/undead-universal-patch-il2cpp.csproj +++ b/undead-universal-patch-il2cpp.csproj @@ -1,9 +1,9 @@ - + net6.0 undead_universal_patch_il2cpp - My first plugin + Non-EAC, IL2CPP build patcher for Rec Room (Late 2018*-*April-2020) 1.0.0 true latest @@ -17,24 +17,29 @@ - - G:\rr\2019-10-25 (5120486837419374208)\BepInEx\interop\Assembly-CSharp.dll + AssemblyReferences\Assembly-CSharp.dll - G:\rr\2019-10-25 (5120486837419374208)\BepInEx\interop\Il2Cppmscorlib.dll + AssemblyReferences\Il2Cppmscorlib.dll + true - G:\rr\2019-10-25 (5120486837419374208)\BepInEx\interop\Il2CppSystem.dll + AssemblyReferences\Il2CppSystem.dll + true - - G:\rr\2019-10-25 (5120486837419374208)\cpp2il_out\Photon3Unity3D.dll + + G:\rr\2019-11-20 (7177272088743827206)\cpp2il_out\RecRoom.Datastructures.Runtime.dll - G:\rr\2019-10-25 (5120486837419374208)\BepInEx\interop\UnityEngine.CoreModule.dll + AssemblyReferences\UnityEngine.CoreModule.dll + + + +