- SignalR handshake fix

- Self-hosted PhotonSocketServer support
- Split photon and hile event patch
- Deprecated amplitude redirect patch
This commit is contained in:
2025-04-13 02:11:08 -04:00
parent 155dda33b9
commit 8d1bac8201
13 changed files with 304 additions and 99 deletions

View File

@@ -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("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) if (GalvanicConfig.Enabled.Value)
{ {
string[] applyHeader = [ string[] applyHeader = [
"authservice/cachedlogin/forplatformid", "/cachedlogin/forplatformid",
"accountservice/account/create", "/account/create",
"authservice/connect/token" "/connect/token"
]; ];
foreach (string header in applyHeader) foreach (string header in applyHeader)
{ {
if (newUri.PathAndQuery.Contains(header)) if (newUri.PathAndQuery.Contains(header))
{ {
// refresh the token if it expired // 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(); Galvanic.GalvanicWebAuth.TokenExpiry();
Type httpRequestType = request.GetType(); Type httpRequestType = request.GetType();

View File

@@ -1,11 +1,34 @@
using System.Collections.Generic; using System.Reflection;
using System.Reflection;
using HarmonyLib; using HarmonyLib;
using Il2CppSystem;
using UnityEngine; using UnityEngine;
namespace undead_universal_patch_il2cpp.Patches 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 class HilePatch
{ {
public static void Patch() public static void Patch()

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib; using HarmonyLib;
using System.Reflection; using System.Reflection;

207
BasePatches/PhotonPatch.cs Normal file
View File

@@ -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.");
}
}
}

View File

@@ -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.");
}
}
}

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib; using HarmonyLib;
namespace undead_universal_patch_il2cpp.Patches namespace undead_universal_patch_il2cpp.Patches

View File

@@ -15,6 +15,7 @@ namespace undead_universal_patch_il2cpp.Patches
static bool Prepare() static bool Prepare()
{ {
if (!GenericConfig.CertificatePatch.Value) return false;
if (targetMethod == null) if (targetMethod == null)
{ {
Plugin.Log.LogWarning($"'{Description}' disabled. The type for this patch was not found."); 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() static bool Prefix()
{ {
return !(GenericConfig.CertificatePatch.Value); return false;
} }
} }

View File

@@ -8,7 +8,6 @@ namespace undead_universal_patch_il2cpp
public static ConfigEntry<bool> LogAllRequests; public static ConfigEntry<bool> LogAllRequests;
public static ConfigEntry<bool> CertificatePatch; public static ConfigEntry<bool> CertificatePatch;
public static ConfigEntry<bool> HilePatch; public static ConfigEntry<bool> HilePatch;
public static ConfigEntry<bool> AmplitudeRedirectPatch;
public static ConfigEntry<bool> SignalRHandshakeFix; public static ConfigEntry<bool> SignalRHandshakeFix;
public static ConfigEntry<bool> ImageSignaturePatch; public static ConfigEntry<bool> ImageSignaturePatch;
} }
@@ -17,8 +16,7 @@ namespace undead_universal_patch_il2cpp
public static bool LogAllRequests = false; public static bool LogAllRequests = false;
public static bool CertificatePatch = false; public static bool CertificatePatch = false;
public static bool HilePatch = false; public static bool HilePatch = false;
public static bool AmplitudeRedirectPatch = true; public static bool SignalRHandshakeFix = false;
public static bool SignalRHandshakeFix = true;
public static bool ImageSignaturePatch = false; public static bool ImageSignaturePatch = false;
} }
public static class NameserverConfig public static class NameserverConfig
@@ -34,14 +32,22 @@ namespace undead_universal_patch_il2cpp
public static class PhotonConfig public static class PhotonConfig
{ {
public static ConfigEntry<bool> PatchPhotonIds; public static ConfigEntry<bool> PatchPhotonIds;
public static ConfigEntry<bool> PunLogging;
public static ConfigEntry<bool> SelfHosted;
public static ConfigEntry<string> AppID; public static ConfigEntry<string> AppID;
public static ConfigEntry<string> VoiceAppID; public static ConfigEntry<string> VoiceAppID;
public static ConfigEntry<string> ServerAddress;
public static ConfigEntry<int> ServerPort;
} }
public static class PhotonConfigDefaults public static class PhotonConfigDefaults
{ {
public static bool PatchPhotonIds = false; 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 AppID = "replace-me-please";
public static string VoiceAppID = "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 public static class GalvanicConfig
@@ -59,4 +65,5 @@ namespace undead_universal_patch_il2cpp
public static string Export = "IDoNotWantToExportMyKeys"; public static string Export = "IDoNotWantToExportMyKeys";
public static bool Import = false; public static bool Import = false;
} }
} }

View File

@@ -205,7 +205,7 @@ namespace undead_universal_patch_il2cpp.Galvanic
{ {
byte[] buffer = new byte[128]; byte[] buffer = new byte[128];
new System.Random().NextBytes(buffer); new System.Random().NextBytes(buffer);
return System.Convert.ToBase64String(buffer).Substring(0, 128); return Convert.ToBase64String(buffer).Substring(0, 128);
// god help me // god help me
} }

View File

@@ -1,4 +1,4 @@
using AmplitudeAnalytics; using System;
using BepInEx; using BepInEx;
using BepInEx.Logging; using BepInEx.Logging;
using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP;
@@ -6,7 +6,7 @@ using HarmonyLib;
namespace undead_universal_patch_il2cpp; 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 class Plugin : BasePlugin
{ {
public static new readonly ManualLogSource Log = Logger.CreateLogSource("UUPatch"); public static new readonly ManualLogSource Log = Logger.CreateLogSource("UUPatch");
@@ -22,26 +22,39 @@ public class Plugin : BasePlugin
public override void Load() 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, GenericConfig.LogAllRequests = Config.Bind("Generic", "LogAllRequests", GenericConfigDefaults.LogAllRequests,
"Log all HTTP requests sent by the game."); "Log all HTTP requests sent by the game.");
GenericConfig.CertificatePatch = Config.Bind("Generic", "CertificatePatch", GenericConfigDefaults.CertificatePatch, 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."); "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, 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."); "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, GenericConfig.SignalRHandshakeFix = Config.Bind("Generic", "SignalRHandshakeFix", GenericConfigDefaults.SignalRHandshakeFix,
"Replace apostrophes with quotes in the initial SignalR handshake."); "Replace apostrophes with quotes in the initial SignalR handshake.");
GenericConfig.ImageSignaturePatch = Config.Bind("Generic", "ImageSignaturePatch", GenericConfigDefaults.ImageSignaturePatch, GenericConfig.ImageSignaturePatch = Config.Bind("Generic", "ImageSignaturePatch", GenericConfigDefaults.ImageSignaturePatch,
"When enabled, all image signatures will be valid." + "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)"); "\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, PhotonConfig.PatchPhotonIds = Config.Bind("Photon", "PatchPhotonIds", PhotonConfigDefaults.PatchPhotonIds,
"Enable/disable changing the target IDs in PhotonServerSettings." + "Patch Photon configuration.");
"\nCustom server settings are not yet supported."); 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, 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, 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, NameserverConfig.Rewrite = Config.Bind("Nameserver", "Rewrite", NameserverConfigDefaults.Rewrite,
"Enable/disable rewriting the URL for nameserver requests."); "Enable/disable rewriting the URL for nameserver requests.");
NameserverConfig.NewUrl = Config.Bind("Nameserver", "NewUrl", NameserverConfigDefaults.NewUrl, 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.Export.Value == "IWantToExportMyKeys") Galvanic.GalvanicAuth.Export();
if (GalvanicConfig.Import.Value) Galvanic.GalvanicAuth.Import(); if (GalvanicConfig.Import.Value) Galvanic.GalvanicAuth.Import();
if (GalvanicConfig.Enabled.Value) Galvanic.GalvanicWebAuth.GetToken(); if (GalvanicConfig.Enabled.Value) Galvanic.GalvanicWebAuth.GetToken();
Log.LogInfo("Undead Universal IL2CPP Patch for Rec Room by @zombieb; loaded.");
} }
} }

View File

@@ -17,8 +17,21 @@ Currently a work-in-progress and is not implemented
Unlike the Mono patch, some assemblies must be referenced. 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: 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/Il2Cppmscorlib.dll`
* `BepInEx/interop/Il2CppSystem.dll` * `BepInEx/interop/Il2CppSystem.dll`
* (from [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL)) `cpp2il_out/UnityEngine.CoreModule.dll` * `BepInEx/interop/Photon3Unity3D.dll`
* (from [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL)) `cpp2il_out/Assembly-CSharp.dll` * `BepInEx/interop/RecRoom.Datastructures.Runtime`
* (from [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL)) `cpp2il_out/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

11
Util.cs Normal file
View File

@@ -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; }
}
}

View File

@@ -4,7 +4,7 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyName>undead_universal_patch_il2cpp</AssemblyName> <AssemblyName>undead_universal_patch_il2cpp</AssemblyName>
<Description>Non-EAC, IL2CPP build patcher for Rec Room (Late 2018*-*April-2020) </Description> <Description>Non-EAC, IL2CPP build patcher for Rec Room (Late 2018*-*April-2020) </Description>
<Version>1.2.0</Version> <Version>1.3.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources> <RestoreAdditionalProjectSources>
@@ -29,6 +29,9 @@
<Reference Include="Il2CppSystem"> <Reference Include="Il2CppSystem">
<HintPath>AssemblyReferences\Il2CppSystem.dll</HintPath> <HintPath>AssemblyReferences\Il2CppSystem.dll</HintPath>
</Reference> </Reference>
<Reference Include="Photon3Unity3D">
<HintPath>AssemblyReferences\Photon3Unity3D.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Datastructures.Runtime"> <Reference Include="RecRoom.Datastructures.Runtime">
<HintPath>AssemblyReferences\RecRoom.Datastructures.Runtime.dll</HintPath> <HintPath>AssemblyReferences\RecRoom.Datastructures.Runtime.dll</HintPath>
</Reference> </Reference>