3 Commits

Author SHA1 Message Date
devin 6a62f0cc1d Suppress DUID mismatch check resulting in create account hang (#3)
* test duuid mismatch failures

* add variables to trigger or suppress DUID mismatch

* update docs

* turn on by default
2026-07-14 13:36:52 -04:00
devin a6e4555a00 add link 2026-07-10 17:14:44 -04:00
Devin Zuczek 326188c7a0 Initial RecNetPlugin 2026-07-08 17:11:58 -04:00
20 changed files with 1869 additions and 517 deletions
+3 -5
View File
@@ -1,5 +1,3 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
GamePath.props
obj
bin
+130
View File
@@ -0,0 +1,130 @@
# CLAUDE.md
Guidance for working in this repo. This is a **BepInEx 6 (IL2CPP)** Harmony plugin that points the
Rec Room client at a self-hosted server. Read the README for the user-facing overview; this file is
the stuff you only learn by getting burned.
## Build & deploy
```sh
dotnet build -c Debug -p:GamePath="C:\Games\depots\471711\23191908"
```
- `GamePath` points at the Rec Room install root. It's normally set in the gitignored
`GamePath.props` (see `GamePath.props.example`); the `-p:GamePath=...` override is handy for one-offs.
- The project references ~150 interop DLLs from `$(GamePath)\BepInEx\interop`. Those are generated by
Il2CppInterop the first time the game runs under BepInEx — if they're missing, launch the game once.
- A post-build `DeployPlugin` target copies `RecNetPlugin.dll` into `$(GamePath)\BepInEx\plugins\`.
**The copy fails while Rec Room is running** (the DLL is locked) — that's an MSB3027 error, not a
compile error. Close the game and rebuild. The DLL is also always left in `bin/Debug/net6.0/`.
## Hard-won gotchas (read before patching anything)
1. **Interop assemblies are stubs. The real code is native.** The DLLs under `BepInEx/interop` are
Il2CppInterop proxies — method bodies just marshal into `GameAssembly.dll`. dnSpy / managed
decompilation of the interop shows *no real logic*. You cannot read the actual algorithms
statically; you learn behavior by patching + logging at runtime.
2. **Obfuscated names differ per game build.** Rec Room's type/method names are obfuscated
(`PGECJHKNIEN`, `MDBMGOBECDJ`, `cm_did_ppk`, etc.). A dnSpy dump from *some* build will not
necessarily match the interop you compile against. Real example from the DUID work: a dnSpy dump
called the method `CheckForMismatch` and the pref field `DBAIOPIEJNC`, but in our interop the
method is `CheckForDUIDMismatch` and neither `DBAIOPIEJNC` nor `MDBMGOBECDJ` exist at all. **Always
resolve members against the interop DLLs you actually build against** (see the Cecil snippet below),
never against a dump from an unknown build.
3. **Patch the concrete class, not the IL2CPP "interface".** Il2CppInterop renders IL2CPP interfaces
as abstract classes deriving from `Il2CppObjectBase`. Harmony will happily patch an abstract method
and throw no error, but the prefix **never runs** because the game dispatches to the concrete
implementation. This cost us a whole "shipped fix" that did nothing. Concrete impls live in
`Assembly-CSharp.dll`. Example: patch `CheatManager.CheckForDUIDMismatch`, *not* the interface
`PGECJHKNIEN.CheckForDUIDMismatch`. Verify with Cecil that `IsAbstract == false` before trusting a
patch.
4. **Obfuscated members live in the global namespace** and are referenced unqualified in this codebase
(e.g. `typeof(JAPJPGNBMNM)`, `PGECJHKNIEN`). No `using` needed.
5. **Harmony prefix conventions here:** force a value + `return false` to skip the original (see
`Patches/EACPatches.cs`). For out-params, take a `ref` parameter named exactly as the interop shows
it (e.g. `ref string ALOMDLLNIMD`), plus `ref bool __result`.
## Inspecting the game
Use **Mono.Cecil** for static metadata/signature checks (accessibility, abstract-ness, exact param
names, which assembly a concrete impl lives in). Mark-of-the-web will block loading `Mono.Cecil.dll`
directly — copy it somewhere local, `Unblock-File`, then load via bytes:
```powershell
$dst = "$scratch\Mono.Cecil.dll"
Copy-Item "$interop\Mono.Cecil.dll" $dst; Unblock-File $dst
[System.Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes($dst)) | Out-Null
$asm = [Mono.Cecil.AssemblyDefinition]::ReadAssembly("$interop\Assembly-CSharp.dll")
# then walk $asm.MainModule.GetTypes(), inspect .Methods / .Fields / .IsAbstract / .IsStatic ...
```
Notes:
- Windows PowerShell 5.1 has **no** `?.` null-conditional operator — use explicit `$x -eq $null` checks.
- String literals (pref keys, endpoints, GUIDs) are in `RecRoom_Data/il2cpp_data/Metadata/global-metadata.dat`.
`grep -a -o -E '[ -~]{4,}' global-metadata.dat | grep -i <thing>` extracts them.
- The BepInEx runtime log is `$(GamePath)/BepInEx/LogOutput.log`. Our plugin logs under the
`RecNet Plugin` source. `[HTTP]`, `[DUID]`, `[DUID-PROBE]`, `[DEVICEID]`, `[CORRUPT]` are our tags.
- PlayerPrefs on Windows live in the registry at `HKCU\Software\Against Gravity\Rec Room`, value names
are `<key>_h<unityHash>`, values are `REG_BINARY`. CodeStage AntiCheat stores strings *obscured*
(XOR-encrypted), so a stored id will not appear as plaintext in registry or files.
## Case study: the Create Account / DUID hang
The gnarliest bug so far; the diagnostic tooling for it still lives in the repo. Summary:
- **Symptom:** on some machines Create Account hangs. Log shows a `PlayerReporting/v1/deviceId` POST
returning `200 {"success":true}`, after which the client never calls the `create_account` OAuth and
never persists the id (`WriteDUIDs` never runs).
- **Trigger:** a device-id **mismatch**. `CheatManager.CheckForDUIDMismatch(out string)` returns true
when the stored id differs from `SystemInfo.deviceUniqueIdentifier`. True → migration path → POST →
hang. Machines whose stored id matches never take the path.
- **Stored id:** PlayerPrefs key `cm_did_ppk` (registry `cm_did_ppk_h3478365449`), CodeStage
ObscuredString-encoded. `CheatManager.WriteDUIDs()` writes it, `ClearDUIDs()` deletes it. In our
interop these are **instance** methods (a dnSpy dump showed them static — build difference again).
- **Two surprises:**
1. Deleting the registry value did **not** change the `oldDeviceId` in the POST, and the probe
showed no `cm_did_ppk` read that session — i.e. the "old" id is **not sourced from local
PlayerPrefs** on the failing path. Consequence: **a registry-reset script does not fix it.**
2. `game callback attached = True` on that request → the client is genuinely waiting on the
response. But the real server already returns `{"success":true}` at 200 and it still hangs, so the
accepting response shape (if one exists) is something more specific.
> ### ⚠️ OPEN QUESTION — where does the old DUID actually live?
> We have **not** found the source of the `oldDeviceId` value. It survives a full delete of the
> `HKCU\Software\Against Gravity\Rec Room` registry key, it does not appear as plaintext anywhere in
> `AppData/LocalLow/Against Gravity/Rec Room`, and on the failing run the `cm_did_ppk` PlayerPref is
> never read. So `cm_did_ppk` is *a* copy but not the one that seeds the migration POST. Leading (but
> unconfirmed) theory: it's held server-side by the archival server, which recorded it from prior
> POSTs, and/or cached in memory from a server response. **Until this is found, the only reliable fix
> is `Suppress` (client) or correcting the value server-side — not clearing local storage.** Next
> steps to try: inspect what the recflare backend stores/returns for the account's device id; dump the
> `HTTPCache` entries decoded (not plaintext); trace who sets the field the POST body reads from.
- **Working client-side workaround:** force `CheckForDUIDMismatch` → false (skips the migration path
entirely). Config: `Suppress DUID Mismatch = true`.
- **Proper root-cause fix:** server-side — make the endpoint stop reporting a stale `old` id (so
`old == new`, no mismatch) or return whatever the client needs to proceed.
### Diagnostic knobs (all in `[Advanced]`)
`Suppress DUID Mismatch` defaults **true** (it's the shipped fix). Everything else defaults **false**
those are investigation tools, not normal config. See the patch files for details.
| Config key | Patch file | What it does |
| --- | --- | --- |
| `Suppress DUID Mismatch` | `DUIDMismatchPatch.cs` | **The fix (default true).** Force `CheckForDUIDMismatch` → false. |
| `Simulate DUID Mismatch` | `DUIDMismatchPatch.cs` | Force it → true. Reproduce the hang without a corrupt value. |
| `Corrupt Stored DUID` | `CorruptDUIDPatch.cs` | One-shot: write a truncated id via `WriteDUIDs` (spoofing `SystemInfo.deviceUniqueIdentifier`) to create a *genuinely* corrupt stored value. |
| `Restore Stored DUID` | `CorruptDUIDPatch.cs` | One-shot undo: `WriteDUIDs` with the real id. |
| `DeviceId Response Override` / `Status` | `DeviceIdResponsePatch.cs` | Rewrite the `deviceId` response body/status in-flight to probe what shape the client will accept. |
| (probe) | `DUIDProbePatch.cs` | Logs every PlayerPrefs get/set and the `WriteDUIDs`/`ClearDUIDs` calls — `WriteDUIDs() called` is the "client accepted the response" signal. |
`DUIDMismatchPatch` has three modes: `Simulate` → force true, `Suppress` → force false, neither →
pass through to the real check (needed to observe a genuinely corrupt stored value).
**The diagnostic patches should not ship in a release build** — strip them (or at least confirm their
knobs default false: `Simulate`, `Corrupt`, `Restore`, `DeviceId Response Override`) before cutting a
release. `Suppress DUID Mismatch` is the real fix and defaults **true**, so it stays on.
-16
View File
@@ -1,16 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CannedNet.Client", "CannedNet.Client\CannedNet.Client.csproj", "{B6704474-3934-43AE-9E89-E5851F6A266B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B6704474-3934-43AE-9E89-E5851F6A266B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6704474-3934-43AE-9E89-E5851F6A266B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6704474-3934-43AE-9E89-E5851F6A266B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6704474-3934-43AE-9E89-E5851F6A266B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
-454
View File
@@ -1,454 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>CannedNet.Client</AssemblyName>
<Product>My first plugin</Product>
<Version>1.0.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources>
https://api.nuget.org/v3/index.json;
https://nuget.bepinex.dev/v3/index.json;
https://nuget.samboy.dev/v3/index.json
</RestoreAdditionalProjectSources>
<RootNamespace>CannedNet.Client</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.*" IncludeAssets="compile"/>
<PackageReference Include="BepInEx.PluginInfoProps" Version="2.*"/>
</ItemGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Assembly-CSharp-firstpass.dll</HintPath>
</Reference>
<Reference Include="AstarPathfindingProject">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\AstarPathfindingProject.dll</HintPath>
</Reference>
<Reference Include="Bitpacker">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Bitpacker.dll</HintPath>
</Reference>
<Reference Include="Circuits">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Circuits.dll</HintPath>
</Reference>
<Reference Include="CSCore">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\CSCore.dll</HintPath>
</Reference>
<Reference Include="EasyAntiCheat.Client">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\EasyAntiCheat.Client.dll</HintPath>
</Reference>
<Reference Include="Google.Protobuf">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Google.Protobuf.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="Il2CppMono.Security">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppMono.Security.dll</HintPath>
</Reference>
<Reference Include="Il2Cppmscorlib">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2Cppmscorlib.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppSystem.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Configuration">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppSystem.Configuration.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppSystem.Core.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Drawing">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppSystem.Drawing.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppSystem.Runtime.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Runtime.Serialization">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppSystem.Runtime.Serialization.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Xml">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Il2CppSystem.Xml.dll</HintPath>
</Reference>
<Reference Include="Oculus.Platform">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Oculus.Platform.dll</HintPath>
</Reference>
<Reference Include="Oculus.VR">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Oculus.VR.dll</HintPath>
</Reference>
<Reference Include="OSA">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\OSA.dll</HintPath>
</Reference>
<Reference Include="Pathfinding.ClipperLib">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Pathfinding.ClipperLib.dll</HintPath>
</Reference>
<Reference Include="Pathfinding.Ionic.Zip.Reduced">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Pathfinding.Ionic.Zip.Reduced.dll</HintPath>
</Reference>
<Reference Include="Pathfinding.Poly2Tri">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Pathfinding.Poly2Tri.dll</HintPath>
</Reference>
<Reference Include="Photon3Unity3D">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Photon3Unity3D.dll</HintPath>
</Reference>
<Reference Include="PhotonChat">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonChat.dll</HintPath>
</Reference>
<Reference Include="PhotonRealtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonRealtime.dll</HintPath>
</Reference>
<Reference Include="PhotonUnityNetworking">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonUnityNetworking.dll</HintPath>
</Reference>
<Reference Include="PhotonUnityNetworking.Utilities">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonUnityNetworking.Utilities.dll</HintPath>
</Reference>
<Reference Include="PhotonVoice">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonVoice.dll</HintPath>
</Reference>
<Reference Include="PhotonVoice.API">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonVoice.API.dll</HintPath>
</Reference>
<Reference Include="PhotonVoice.PUN">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonVoice.PUN.dll</HintPath>
</Reference>
<Reference Include="PhotonWebSocket">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\PhotonWebSocket.dll</HintPath>
</Reference>
<Reference Include="Pngcs">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Pngcs.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Agdxgidisplays.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Agdxgidisplays.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Agmobilear.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Agmobilear.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Assetbundles.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Assetbundles.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Async">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Async.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Circuitsv2.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Circuitsv2.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.CultureUtil.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.CultureUtil.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Datastructures.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Datastructures.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.EditorHelpers.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.EditorHelpers.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.iOSNative.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.iOSNative.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.DataTypes.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Networking.DataTypes.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.Mocks">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Networking.Mocks.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.NetworkedObjects.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Networking.NetworkedObjects.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.RPC.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Networking.RPC.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.SynchronizedFields.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Networking.SynchronizedFields.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ProBuilderExtensions">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.ProBuilderExtensions.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Promises.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Promises.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ResourceManagement.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.ResourceManagement.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Scheduler.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Scheduler.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Streamingaudio.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Streamingaudio.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Tmp_overrides.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Tmp_overrides.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Tweening.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Tweening.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Unityextensions.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\RecRoom.Unityextensions.Runtime.dll</HintPath>
</Reference>
<Reference Include="SA.Foundation">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\SA.Foundation.dll</HintPath>
</Reference>
<Reference Include="SA.Foundation.Network">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\SA.Foundation.Network.dll</HintPath>
</Reference>
<Reference Include="SA.Foundation.Tests">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\SA.Foundation.Tests.dll</HintPath>
</Reference>
<Reference Include="SA.iOS">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\SA.iOS.dll</HintPath>
</Reference>
<Reference Include="SA.iOS.XCode">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\SA.iOS.XCode.dll</HintPath>
</Reference>
<Reference Include="SteamVR">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\SteamVR.dll</HintPath>
</Reference>
<Reference Include="SteamVR_Actions">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\SteamVR_Actions.dll</HintPath>
</Reference>
<Reference Include="Unity.Addressables">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.Addressables.dll</HintPath>
</Reference>
<Reference Include="Unity.Burst">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.Burst.dll</HintPath>
</Reference>
<Reference Include="Unity.Burst.Unsafe">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.Burst.Unsafe.dll</HintPath>
</Reference>
<Reference Include="Unity.Collections">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.Collections.dll</HintPath>
</Reference>
<Reference Include="Unity.Jobs">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.Jobs.dll</HintPath>
</Reference>
<Reference Include="Unity.Mathematics">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.Mathematics.dll</HintPath>
</Reference>
<Reference Include="Unity.Postprocessing.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.Postprocessing.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipeline.Universal.ShaderLibrary">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.RenderPipeline.Universal.ShaderLibrary.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Core.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Universal.Runtime">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.RenderPipelines.Universal.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.ResourceManager">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.ResourceManager.dll</HintPath>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.ARFoundation">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.XR.ARFoundation.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.ARSubsystems">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.XR.ARSubsystems.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.Management">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Unity.XR.Management.dll</HintPath>
</Reference>
<Reference Include="UnityEngine">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AccessibilityModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.AccessibilityModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AIModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.AIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AndroidJNIModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.AndroidJNIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AnimationModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.AnimationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ARModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ARModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AssetBundleModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.AssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.AudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClothModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ClothModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterInputModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ClusterInputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterRendererModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ClusterRendererModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CrashReportingModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.CrashReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DirectorModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.DirectorModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DSPGraphModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.DSPGraphModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GameCenterModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.GameCenterModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GridModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.GridModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.HotReloadModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.HotReloadModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ImageConversionModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ImageConversionModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.IMGUIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.InputLegacyModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.InputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.JSONSerializeModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.JSONSerializeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.LocalizationModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.LocalizationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ParticleSystemModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ParticleSystemModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PerformanceReportingModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.PerformanceReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.Physics2DModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.Physics2DModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.PhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ProfilerModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ProfilerModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ScreenCaptureModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.ScreenCaptureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SharedInternalsModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.SharedInternalsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpatialTracking">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.SpatialTracking.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteMaskModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.SpriteMaskModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteShapeModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.SpriteShapeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.StreamingModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.StreamingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SubstanceModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.SubstanceModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SubsystemsModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.SubsystemsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.TerrainModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainPhysicsModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.TerrainPhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextCoreModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.TextCoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.TextRenderingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TilemapModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.TilemapModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TLSModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.TLSModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UI.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIElementsModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UIElementsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UmbraModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UmbraModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UNETModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UNETModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityAnalyticsModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityAnalyticsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityConnectModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityConnectModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityTestProtocolModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityTestProtocolModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityWebRequestModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VehiclesModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.VehiclesModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VFXModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.VFXModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VideoModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.VideoModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.VRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.WindModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.WindModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XR.LegacyInputHelpers">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.XR.LegacyInputHelpers.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XRModule">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\UnityEngine.XRModule.dll</HintPath>
</Reference>
<Reference Include="Valve.Newtonsoft.Json">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\Valve.Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="XboxUWP">
<HintPath>..\..\..\Downloads\Radium_PC_20260220_KbdgFG\BepInEx\interop\XboxUWP.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
@@ -1,19 +0,0 @@
using BestHTTP;
using HarmonyLib;
namespace CannedNet.Client.Patches;
public class SendRequestPatch
{
[HarmonyPatch(typeof(HTTPManager), "SendRequest", [typeof(HTTPRequest)])]
public class ConnectToRecNetPatch
{
private static void Prefix(ref HTTPRequest request)
{
Plugin.Log.LogInfo($"hi {request.Uri.Host}");
if (request.Uri.Host.ToString().Contains("ns.rec.net"))
request.Uri = new Il2CppSystem.Uri("https://ns.lapis.codes");
}
}
}
-20
View File
@@ -1,20 +0,0 @@
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
namespace CannedNet.Client;
[BepInPlugin("lapis.cannednet.client", "CannedNet Client", "1.0.0")]
public class Plugin : BasePlugin
{
internal static new ManualLogSource Log;
public override void Load()
{
Log = base.Log;
Log.LogInfo($"I JUST HIT THE JACKPOTTTT!!!!! YUH YUH YUH!");
Harmony.CreateAndPatchAll(typeof(Plugin).Assembly);
}
}
+10
View File
@@ -0,0 +1,10 @@
<Project>
<!-- Copy this file to "GamePath.props" (same folder) and set GamePath to your local
Rec Room install. GamePath.props is gitignored, so your local path stays out of the repo.
The install must have been launched once under BepInEx 6 (IL2CPP) so that
BepInEx/interop/ is populated with the proxy assemblies this project references. -->
<PropertyGroup>
<GamePath>C:\Path\To\RecRoom</GamePath>
</PropertyGroup>
</Project>
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 djdevin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+77
View File
@@ -0,0 +1,77 @@
using HarmonyLib;
using UnityEngine;
namespace RecNetPlugin.Patches;
// Test tool: persist a genuinely corrupt STORED device id on this machine, matching the friend's
// condition (stored id truncated, current id healthy).
//
// We can't hand-craft the stored value: it lives in PlayerPrefs under an obfuscated key, encoded as a
// CodeStage ObscuredString, and both the key and the encode method are renamed per game build. So we
// let the game write it: WriteDUIDs() stores ObscuredString(SystemInfo.deviceUniqueIdentifier) under
// the right key. We temporarily spoof deviceUniqueIdentifier to a truncated value around that one
// call, so the game encrypts+stores a bad id with its own (unknown-to-us) key. Afterwards the spoof
// is off, so the current id reads healthy again -> stored != current -> real mismatch on next launch.
[HarmonyPatch]
public static class CorruptDUIDPatch
{
// Only true for the duration of the WriteDUIDs() call below, so the SystemInfo getter is spoofed
// exactly there and nowhere else.
private static bool _spoofActive;
private static string _spoofValue = "";
[HarmonyPrefix]
[HarmonyPatch(typeof(SystemInfo), "get_deviceUniqueIdentifier")]
private static bool DeviceIdGetterPrefix(ref string __result)
{
if (!_spoofActive)
return true;
__result = _spoofValue;
return false;
}
// Returns true if the corruption was written (so the caller marks it done and won't repeat).
public static bool CorruptStored(GameObject cheatMgrGo)
{
var cm = cheatMgrGo.GetComponent<CheatManager>() ?? cheatMgrGo.GetComponentInChildren<CheatManager>();
if (cm == null)
{
Plugin.Log.LogError("[CORRUPT] could not find CheatManager component; nothing written");
return false;
}
var real = SystemInfo.deviceUniqueIdentifier; // spoof off -> real id
var bad = real is { Length: >= 7 } ? real.Substring(0, 7) : "badduid";
_spoofValue = bad;
_spoofActive = true;
try
{
cm.WriteDUIDs(); // encodes+stores ObscuredString(bad) under the real key
}
finally
{
_spoofActive = false;
}
Plugin.Log.LogWarning($"[CORRUPT] wrote truncated stored DUID = \"{bad}\" (real id = \"{real}\"). " +
"Set 'Corrupt Stored DUID' back to false and relaunch to drive the real mismatch path.");
return true;
}
// Undo: overwrite the stored value with the real id by calling WriteDUIDs with the spoof off.
public static bool RestoreStored(GameObject cheatMgrGo)
{
var cm = cheatMgrGo.GetComponent<CheatManager>() ?? cheatMgrGo.GetComponentInChildren<CheatManager>();
if (cm == null)
{
Plugin.Log.LogError("[CORRUPT] could not find CheatManager component; nothing restored");
return false;
}
cm.WriteDUIDs(); // spoof off -> stores ObscuredString(real deviceUniqueIdentifier)
Plugin.Log.LogWarning($"[CORRUPT] restored stored DUID to real id = \"{SystemInfo.deviceUniqueIdentifier}\". " +
"Set 'Restore Stored DUID' back to false.");
return true;
}
}
+45
View File
@@ -0,0 +1,45 @@
using HarmonyLib;
namespace RecNetPlugin.Patches;
// Controls CheatManager.CheckForDUIDMismatch, which returns true when the machine's stored device id
// differs from the freshly-derived one. A true result sends the client down the migration path that
// POSTs PlayerReporting/v1/deviceId and then stalls on Create Account.
//
// Three modes, chosen by config:
// Simulate = true -> force TRUE (fake a mismatch to reproduce the hang without a corrupt value)
// Suppress = true -> force FALSE (the workaround fix: never migrate, never hang)
// both false -> pass through, let the REAL check run against the actual stored value
// (needed to observe a genuinely corrupt stored id, e.g. after Corrupt Stored DUID)
//
// Patch the concrete CheatManager method, NOT the abstract PGECJHKNIEN interface, or the prefix
// never runs.
[HarmonyPatch]
public static class DUIDMismatchPatch
{
private const string SimulatedStoredDeviceId = "491e8b9";
[HarmonyPrefix]
[HarmonyPatch(typeof(CheatManager), "CheckForDUIDMismatch")]
private static bool Prefix(ref string ALOMDLLNIMD, ref bool __result)
{
if (Plugin.SimulateDUIDMismatch.Value)
{
ALOMDLLNIMD = SimulatedStoredDeviceId;
__result = true;
Plugin.Log.LogWarning($"[DUID] simulating mismatch, stored id = {SimulatedStoredDeviceId}");
return false;
}
if (Plugin.SuppressDUIDMismatch.Value)
{
ALOMDLLNIMD = string.Empty;
__result = false;
Plugin.Log.LogInfo("[DUID] mismatch check forced to false (suppressed)");
return false;
}
// Pass through to the real check.
return true;
}
}
+58
View File
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using HarmonyLib;
using UnityEngine;
namespace RecNetPlugin.Patches;
// Diagnostic only. Two jobs:
// 1. Show where the stored device id lives, by logging PlayerPrefs reads/writes.
// 2. Show how far the DUID migration branch gets, by logging CheatManager's other DUID methods.
// If WriteDUIDs() never fires after the deviceId POST, the flow stalls before it.
[HarmonyPatch]
public static class DUIDProbePatch
{
// PlayerPrefs.GetString is called constantly, so log each key only once — except device/DUID
// keys, which we always log so we can watch them change across the migration.
private static readonly HashSet<string> SeenKeys = new();
private static bool IsInteresting(string key) =>
key != null && (key.Contains("DUID") || key.Contains("Duid") || key.Contains("duid")
|| key.Contains("Device") || key.Contains("device")
|| key.Contains("Anon") || key.Contains("anon"));
private static void Note(string op, string key, string value)
{
if (IsInteresting(key))
Plugin.Log.LogWarning($"[DUID-PROBE] {op} {key} = \"{value}\"");
else if (SeenKeys.Add($"{op}:{key}"))
Plugin.Log.LogInfo($"[DUID-PROBE] {op} {key} = \"{value}\"");
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerPrefs), nameof(PlayerPrefs.GetString), [typeof(string)])]
private static void GetStringPostfix(string key, string __result) => Note("get", key, __result);
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerPrefs), nameof(PlayerPrefs.GetString), [typeof(string), typeof(string)])]
private static void GetStringDefaultPostfix(string key, string __result) => Note("get", key, __result);
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerPrefs), nameof(PlayerPrefs.SetString))]
private static void SetStringPrefix(string key, string value) => Note("SET", key, value);
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerPrefs), nameof(PlayerPrefs.DeleteKey))]
private static void DeleteKeyPrefix(string key) => Note("DEL", key, "<deleted>");
[HarmonyPrefix]
[HarmonyPatch(typeof(CheatManager), "WriteDUIDs")]
private static void WriteDUIDsPrefix() => Plugin.Log.LogWarning("[DUID-PROBE] WriteDUIDs() called");
[HarmonyPostfix]
[HarmonyPatch(typeof(CheatManager), "WriteDUIDs")]
private static void WriteDUIDsPostfix() => Plugin.Log.LogWarning("[DUID-PROBE] WriteDUIDs() returned");
[HarmonyPrefix]
[HarmonyPatch(typeof(CheatManager), "ClearDUIDs")]
private static void ClearDUIDsPrefix() => Plugin.Log.LogWarning("[DUID-PROBE] ClearDUIDs() called");
}
+61
View File
@@ -0,0 +1,61 @@
using System;
using System.Text;
using BestHTTP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
namespace RecNetPlugin.Patches;
// Experiment harness for the Create Account hang.
//
// On a device-id mismatch the client POSTs PlayerReporting/v1/deviceId, the server answers
// 200 {"success":true}, and then the client stops: CheatManager.WriteDUIDs() is never called, so the
// new id is never persisted and the flow never reaches create_account. That means the client can't
// proceed on what it got back.
//
// This rewrites that one response body before the game sees it, so response shapes can be tried
// without redeploying the server. WriteDUIDs() appearing in the log (see DUIDProbePatch) is the
// pass signal: it means the client accepted the response and resumed the migration.
[HarmonyPatch]
public static class DeviceIdResponsePatch
{
private const string Endpoint = "/PlayerReporting/v1/deviceId";
[HarmonyPrefix]
[HarmonyPatch(typeof(HTTPManager), "SendRequest", [typeof(HTTPRequest)])]
private static void Prefix(HTTPRequest request)
{
if (!request.Uri.AbsoluteUri.Contains(Endpoint, StringComparison.OrdinalIgnoreCase))
return;
var original = request.Callback;
// Whether the game attached a completion callback at all. If this logs False, the client is
// not waiting on this request through the callback API and the "stuck on the response" model
// is wrong -- that would be worth knowing before chasing response shapes any further.
Plugin.Log.LogWarning($"[DEVICEID] request seen; game callback attached = {original != null}");
var body = Plugin.DeviceIdResponseOverride.Value;
if (string.IsNullOrEmpty(body))
return;
var status = Plugin.DeviceIdResponseStatus.Value;
request.Callback = DelegateSupport.ConvertDelegate<OnRequestFinishedDelegate>(
(Action<HTTPRequest, HTTPResponse>)((req, resp) =>
{
if (resp != null)
{
// Set both: DataAsText is computed from Data but cached in dataAsText once read,
// and our own HTTP logger may already have read it.
resp.Data = new Il2CppStructArray<byte>(Encoding.UTF8.GetBytes(body));
resp.dataAsText = body;
resp.StatusCode = status;
Plugin.Log.LogWarning($"[DEVICEID] response overridden -> {status} {body}");
}
original?.Invoke(req, resp);
}));
}
}
@@ -1,9 +1,13 @@
using HarmonyLib;
using Org.BouncyCastle.Crypto.Tls;
namespace CannedNet.Client.Patches;
namespace RecNetPlugin.Patches;
public class FuckOffTLS
/**
Disables TLS certificate pinning. Even though we connect over SSL it seems some certificates
might be pinned.
*/
public class DisableTLSPinning
{
[HarmonyPatch(typeof(LegacyTlsAuthentication), "NotifyServerCertificate")]
public class TlsPatch
@@ -13,4 +17,4 @@ public class FuckOffTLS
return false;
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
using HarmonyLib;
using RecRoom.AntiCheat;
using System.Text;
using Il2CppSystem;
namespace RecNetPlugin.Patches;
[HarmonyPatch]
public static class EACPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(EACManager), "FJLMLEPOKGE")]
private static bool IsReadyPatch(ref bool __result)
{
__result = true;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(EACManager), "GenerateChallengeResponse")]
private static bool GenerateChallengeResponsePatch(string PGCINMIEBJP, ref string __result)
{
if (!string.IsNullOrEmpty(PGCINMIEBJP))
__result = Convert.ToBase64String(Encoding.UTF8.GetBytes(PGCINMIEBJP));
else
__result = Convert.ToBase64String(Encoding.UTF8.GetBytes("nothing"));
return false;
}
}
+34
View File
@@ -0,0 +1,34 @@
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Realtime;
namespace RecNetPlugin.Patches;
/**
Patches Photon to use the App IDs and server hostname/port specified in the plugin config.
*/
[HarmonyPatch(typeof(GPFPFDBGCEK), "AMOHMPKKGHL")]
public class PhotonPatches
{
[HarmonyPostfix]
private static void Postfix(ref AppSettings __result)
{
if (__result != null)
{
__result.AppIdRealtime = Plugin.AppIdRT.Value;
__result.AppIdVoice = Plugin.AppIdVoice.Value;
__result.AppIdChat = Plugin.AppIdChat.Value;
__result.FixedRegion = "us";
__result.UseNameServer = true;
__result.Protocol = ConnectionProtocol.Udp;
if (Plugin.EnableAdvancedSettings.Value)
{
__result.Server = Plugin.PhotonHostname.Value;
__result.Port = Plugin.PhotonPort.Value == 0
? 4533
: Plugin.PhotonPort.Value;
}
}
}
}
+19
View File
@@ -0,0 +1,19 @@
using HarmonyLib;
namespace RecNetPlugin.Patches;
/**
* This allows the global-metadata.dat to be different on the client
* patched to allow a different modulus so we can sign images.
*/
[HarmonyPatch(typeof(JAPJPGNBMNM), "JOKECJKBJGD")]
public static class PromisePatch
{
public static bool Prefix(out HPHDJAFFHCN<JAPJPGNBMNM.AOFCCEACNNA> __result)
{
var result = JAPJPGNBMNM.AOFCCEACNNA.JGIHNLEFJEL();
var promise = HAAHJPGNIMD.NMOOLKAJDOC(result);
__result = promise;
return false;
}
}
+186
View File
@@ -0,0 +1,186 @@
using System;
using BestHTTP;
using HarmonyLib;
using Il2CppInterop.Runtime;
namespace RecNetPlugin.Patches;
/**
Intercept a variety of HTTP requests and rewrite them to point to our own custom server.
*/
public class SendRequestPatch
{
// Official name server host to redirect away from, swapped for the custom server.
private const string OfficialNameServer = "ns.rec.net";
// Skip when HTTP-logging so we don't spam the logs.
private static readonly string[] LogIgnoreSubstrings =
{
"/api/gamesight/event",
"/data/heartbeat",
"/identify",
"/httpapi",
"/data/event",
};
private static bool IsIgnoredForLogging(string url)
{
foreach (var s in LogIgnoreSubstrings)
if (url.Contains(s, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
// Cap logged bodies so a large response/request doesn't flood the log.
private const int MaxLoggedBodyLength = 10000;
private static string Truncate(string s)
{
if (string.IsNullOrEmpty(s) || s.Length <= MaxLoggedBodyLength)
return s;
return s.Substring(0, MaxLoggedBodyLength) + $"... <truncated {s.Length - MaxLoggedBodyLength} chars>";
}
[HarmonyPatch(typeof(HTTPManager), "SendRequest", [typeof(HTTPRequest)])]
public class ConnectToRecNetPatch
{
private static void Prefix(ref HTTPRequest request)
{
var debug = Plugin.Debug.Value && !IsIgnoredForLogging(request.Uri.AbsoluteUri);
if (debug)
{
var entityBody = request.GetEntityBody();
string body;
if (entityBody == null)
body = "<none>";
else if (IsBinaryContentType(request.GetFirstHeaderValue("content-type")) || LooksBinary(entityBody))
body = BinaryPreview(entityBody);
else
body = System.Text.Encoding.UTF8.GetString(entityBody);
Plugin.Log.LogInfo($"[HTTP] {request.MethodType} {request.Uri.AbsoluteUri} body={Truncate(body)}");
}
var host = request.Uri.Host;
if (host == OfficialNameServer)
{
// Redirect the nameserver lookup to the custom server, swapping only the host.
var newHost = new System.Uri(Plugin.ServerHostname.Value).Host;
var builder = new Il2CppSystem.UriBuilder(request.Uri) { Host = newHost };
request.Uri = builder.Uri;
if (debug)
Plugin.Log.LogInfo($"[HTTP] intercepted {host} -> {newHost}");
}
if (debug)
LogResponseWhenDone(request);
}
}
// Wraps the request's completion callback so we log the response (status + body) when it
// finishes, then forwards to the game's original callback. This is how we see *which*
// request comes back empty (RecNet throws "Response was empty" on a blank body).
private static void LogResponseWhenDone(HTTPRequest request)
{
try
{
var original = request.Callback;
var url = request.Uri.AbsoluteUri;
request.Callback = DelegateSupport.ConvertDelegate<OnRequestFinishedDelegate>(
(Action<HTTPRequest, HTTPResponse>)((req, resp) =>
{
if (resp == null)
Plugin.Log.LogWarning($"[HTTP] <- {url} NO RESPONSE (state={req.State})");
else
{
string text;
if (IsBinaryContentType(resp.GetFirstHeaderValue("content-type")))
text = "<binary>";
else
{
text = resp.DataAsText;
if (string.IsNullOrEmpty(text)) text = "<empty>";
}
var msg = $"[HTTP] <- {resp.StatusCode} {url} body={Truncate(text)}";
if (resp.StatusCode is >= 200 and < 300)
Plugin.Log.LogInfo(msg);
else
Plugin.Log.LogError(msg);
}
original?.Invoke(req, resp);
}));
}
catch (Exception e)
{
Plugin.Log.LogError($"[HTTP] failed to attach response logger: {e}");
}
}
// Content-Type prefixes/keywords we treat as textual; anything else is logged as <binary> so we
// don't dump image/asset bytes into the log.
private static readonly string[] TextContentTypes =
{
"text/", "application/json", "application/xml", "application/javascript",
"application/x-www-form-urlencoded", "+json", "+xml",
};
// True if the body is (probably) binary and shouldn't be logged as text. Defaults to text when
// there's no Content-Type, so we err toward logging rather than hiding.
private static bool IsBinaryContentType(string contentType)
{
if (string.IsNullOrEmpty(contentType)) return false;
foreach (var t in TextContentTypes)
if (contentType.Contains(t, StringComparison.OrdinalIgnoreCase))
return false;
return true;
}
// Render the leading bytes of a binary body as text so structured framing (e.g. multipart form
// boundaries and part headers) stays readable, while raw bytes are shown as \xNN escapes. Capped
// at MaxLoggedBodyLength since the interesting framing is at the front.
private static string BinaryPreview(byte[] data)
{
if (data.Length == 0) return "<binary empty>";
var sb = new System.Text.StringBuilder(MaxLoggedBodyLength + 32);
sb.Append("<binary ").Append(data.Length).Append(" bytes> ");
var i = 0;
// Cap on rendered length, not byte count: escapes expand a byte to 4 chars, so this keeps the
// preview near MaxLoggedBodyLength and avoids a second pass by Truncate at the log site.
for (; i < data.Length && sb.Length < MaxLoggedBodyLength; i++)
{
var b = data[i];
if (b == 0x09 || b == 0x0A || b == 0x0D || (b >= 0x20 && b < 0x7F))
sb.Append((char)b);
else
sb.Append("\\x").Append(b.ToString("x2"));
}
if (i < data.Length)
sb.Append($"... <truncated {data.Length - i} bytes>");
return sb.ToString();
}
// Content sniff for raw request bytes — the Content-Type header isn't reliably set at
// SendRequest time (e.g. multipart form bodies set it lazily, and the body still embeds the
// raw image), so look at the bytes: a NUL byte, or a high ratio of non-text control bytes in
// the first chunk, means it's binary (or binary-mixed like a multipart upload).
private static bool LooksBinary(byte[] data)
{
if (data.Length == 0) return false;
var sample = Math.Min(data.Length, 4096);
var nonText = 0;
for (var i = 0; i < sample; i++)
{
var b = data[i];
if (b == 0) return true;
// Control chars other than tab/newline/carriage-return.
if (b < 0x20 && b != 0x09 && b != 0x0A && b != 0x0D) nonText++;
}
return nonText * 100 / sample > 10;
}
}
+81
View File
@@ -0,0 +1,81 @@
using System;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace RecNetPlugin;
[BepInPlugin("net.rec.plugin", "RecNet Plugin", "1.0.0")]
public class Plugin : BasePlugin
{
internal static new ManualLogSource Log;
public static ConfigEntry<string> AppIdRT { get; private set; }
public static ConfigEntry<string> AppIdVoice { get; private set; }
public static ConfigEntry<string> AppIdChat { get; private set; }
public static ConfigEntry<string> ServerHostname { get; private set; }
public static ConfigEntry<bool> EnableAdvancedSettings { get; private set; }
public static ConfigEntry<string> PhotonHostname { get; private set; }
public static ConfigEntry<int> PhotonPort { get; private set; }
public static ConfigEntry<bool> Debug { get; private set; }
public static ConfigEntry<bool> SimulateDUIDMismatch { get; private set; }
public static ConfigEntry<bool> SuppressDUIDMismatch { get; private set; }
public static ConfigEntry<bool> CorruptStoredDUID { get; private set; }
public static ConfigEntry<bool> RestoreStoredDUID { get; private set; }
public static ConfigEntry<string> DeviceIdResponseOverride { get; private set; }
public static ConfigEntry<int> DeviceIdResponseStatus { get; private set; }
private static bool _corruptDone;
public override void Load()
{
Log = base.Log;
AppIdRT = Config.Bind("Photon", "App Id Realtime", "", "Photon Realtime App ID");
AppIdVoice = Config.Bind("Photon", "App Id Voice", "", "Photon Voice App ID");
AppIdChat = Config.Bind("Photon", "App Id Chat", "", "Photon Chat App ID");
EnableAdvancedSettings = Config.Bind("Advanced", "Enabled Advanced Settings", false, "Allows other fields below in the advanced section to be modified.");
PhotonHostname = Config.Bind("Advanced", "Photon NameServer", "", "Custom Photon NameServer");
PhotonPort = Config.Bind("Advanced", "Photon NameServer Port", 0, "Custom Photon NameServer Port (if 0, it will be default)");
ServerHostname = Config.Bind("Server", "RecNet NameServer Host", "https://ns.rec.net", "Host for the RecNet NameServer.");
Debug = Config.Bind("Advanced", "Debug", false, "Show debug logs (HTTP tracing, etc. WARNING: will include sensitive information such as passwords and auth tokens in the logs, be careful when sharing them!)");
SimulateDUIDMismatch = Config.Bind("Advanced", "Simulate DUID Mismatch", false, "Force CheckForDUIDMismatch to return TRUE (fakes the comparison only). Reproduces the hang path but does not corrupt any stored value. Leave false for normal play.");
SuppressDUIDMismatch = Config.Bind("Advanced", "Suppress DUID Mismatch", true, "Force CheckForDUIDMismatch to return FALSE (the workaround fix, ON by default): the client never migrates and never takes the Create Account hang path. No-op on healthy machines (the real check returns false anyway); on mismatched machines it skips the hang. Set false only to observe the real mismatch behavior for debugging.");
CorruptStoredDUID = Config.Bind("Advanced", "Corrupt Stored DUID", false, "ONE-SHOT TEST: on next launch, write a truncated device id into the DUID pref via the game's own WriteDUIDs, producing a genuinely corrupt STORED value (real current id) — exactly the friend's condition. After it logs '[CORRUPT] wrote', set this back to false and relaunch to drive the real mismatch path. Use 'Restore Stored DUID' to undo.");
RestoreStoredDUID = Config.Bind("Advanced", "Restore Stored DUID", false, "ONE-SHOT UNDO: on next launch, call WriteDUIDs with the real device id, overwriting any corrupt stored value with a good one. Set back to false after it logs '[CORRUPT] restored'.");
DeviceIdResponseOverride = Config.Bind("Advanced", "DeviceId Response Override", "", "Replace the body of the PlayerReporting/v1/deviceId response with this text, to test what shape the client will accept. Empty = leave the server's response alone.");
DeviceIdResponseStatus = Config.Bind("Advanced", "DeviceId Response Status", 200, "HTTP status to force on the PlayerReporting/v1/deviceId response. Only applies when the override body is set.");
Harmony.CreateAndPatchAll(typeof(Plugin).Assembly);
SceneManager.sceneLoaded += (Action<Scene, LoadSceneMode>)OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// CheatManager boots us out of rooms when it runs, but it's ALSO the DUID service the DI
// container resolves for account creation / login (destroying it removes that service).
// So instead of destroying it, *deactivate* the GameObject: it stops running (no Update /
// coroutines, so no boot) while the component still exists, so the DI container can still
// resolve PGECJHKNIEN and call its DUID methods. It's recreated per scene, so deactivate
// each freshly-spawned (active) instance on every load. (GameObject.Find only returns active
// objects, so once deactivated it isn't found again.)
var cheatMgr = GameObject.Find("GameRoot/(Startup)(Clone)/Core Systems/[CheatManager]");
if (cheatMgr == null)
return;
// One-shot corruption for testing: must run while the component is still active (before we
// deactivate it below), because it calls the live CheatManager.WriteDUIDs().
if (CorruptStoredDUID.Value && !_corruptDone)
_corruptDone = Patches.CorruptDUIDPatch.CorruptStored(cheatMgr);
else if (RestoreStoredDUID.Value && !_corruptDone)
_corruptDone = Patches.CorruptDUIDPatch.RestoreStored(cheatMgr);
cheatMgr.SetActive(false);
Log.LogInfo("cheatmanager deactivated");
}
}
+160
View File
@@ -0,0 +1,160 @@
# RecNet Plugin
A [BepInEx 6](https://github.com/BepInEx/BepInEx) (IL2CPP) plugin that points the Rec Room client at a self-hosted / private server.
It does this entirely client-side with [Harmony](https://harmony.pardeike.net/) patches — no game files are modified on disk (except global-metadata.dat - needed for image signatures). The plugin rewrites the RecNet name-server lookups, swaps in your own Photon credentials, and disables the client-side guards (EasyAntiCheat, TLS certificate pinning) that would otherwise reject a non-official server.
> ⚠️ This disables anti-cheat and certificate validation on the client. Use at your own risk.
## Safety
Using BepInEx plugins may cause anti-virus scanners or Windows Defender to pick it up as a threat.
If you don't trust the complied .DLL, you can build it yourself.
See https://github.com/djdevin/recnet-plugin#from-source
## What it does
| Patch | File | Effect |
| --- | --- | --- |
| Name-server redirect | `Patches/SendRequestPatch.cs` | Intercepts `BestHTTP` requests and rewrites the host `ns.rec.net` → your configured server. Also provides optional HTTP request/response logging for development. |
| Photon override | `Patches/PhotonPatches.cs` | Replaces the Realtime / Voice / Chat App IDs (and optionally the Photon name server + port) with your own. |
| EAC bypass | `Patches/EACPatches.cs` | Forces EasyAntiCheat "ready" and stubs the challenge-response so the client connects without the official anti-cheat. |
| TLS bypass | `Patches/DisableTLSPinning.cs` | Skips server-certificate validation so a custom server's cert is accepted. |
| Promise stub | `Patches/PromisePatch.cs` | Allows custom global-metadata.dat files without the game crashing. |
| CheatManager handling | `Plugin.cs` | Deactivates the in-game `CheatManager` (which would otherwise boot you from rooms) while keeping it resolvable for account creation / login. |
| DUID mismatch workaround | `Patches/DUIDMismatchPatch.cs` | Forces the device-id mismatch check to "no mismatch" so the Create Account hang (below) is skipped. **On by default**; no-op on healthy machines. |
## The Create Account / DUID hang
Some machines hang forever on **Create Account**. This turned out to be a genuinely nasty one, so it's
worth documenting.
**What happens:** when the client's *stored* device id (DUID) differs from the one derived at runtime,
the client takes a "migration" path — it POSTs to `PlayerReporting/v1/deviceId`, the server answers
`200 {"success":true}`, and then the client **stalls**: it never makes the `create_account` OAuth call
and never persists the new id. Machines whose stored id already matches never take this path, which is
why the bug hits some players and not others (and is hard to reproduce if your own machine is fine).
**The decision point** is `CheatManager.CheckForDUIDMismatch`. Forcing it to return *true* reproduces
the hang on any machine; forcing it *false* skips the whole path. That false-forcing is the shipping
workaround, exposed as the `Suppress DUID Mismatch` config option, which is **on by default**. It's a
no-op on healthy machines (their real check already returns false) and skips the hang on affected ones.
**Still unsolved:** we have not found where the "old" device id in that POST actually comes from. It
survives deleting the entire `HKCU\Software\Against Gravity\Rec Room` registry key, and on the failing
run the local `cm_did_ppk` PlayerPref is never even read — so clearing local storage does **not** fix
it. The leading theory is that it's held server-side (recorded by the server from earlier reports)
and/or cached in memory from a server response, which would make the proper fix server-side. See
`CLAUDE.md` for the full investigation and the diagnostic tooling.
> ⚠️ `Suppress DUID Mismatch` is a workaround: it lets account creation through but does **not** repair
> a genuinely corrupt stored/served id — it just stops the client from acting on the mismatch.
## Requirements
- A Rec Room install set up with **BepInEx 6 (IL2CPP, bleeding-edge)**, launched at least once so the IL2CPP interop assemblies have been generated under `BepInEx/interop/`.
- **.NET 6 SDK** to build the plugin.
- Your own server endpoints: a RecNet name server, and [Photon](https://www.photonengine.com) app keys.
_Looking for a custom RecNet server?_ Try https://github.com/djdevin/recflare
## Installing
1. Download the game using https://github.com/SteamRE/DepotDownloader. The manifest ID is `7859140924515540835`.
Example: `depotdownloader -app 471710 -depot 471711 -manifest 7859140924515540835`
**You must use this specific version.**
3. Install BepInEx to the game. See https://docs.bepinex.dev/articles/user_guide/installation/index.html. **Note that you must use version 6!**
Alternatively, use the [RecFlare client](https://github.com/djdevin/recflare-client)
### From release
1. Download a release from [/releases](/releases)
2. Drop the `.dll` file into `BepInEx/plugins/`
### From source
The project references the game's interop DLLs, so the build needs to know where your Rec Room install lives. Set `GamePath` using any one of:
1. **A local props file** (recommended):
```sh
cp GamePath.props.example GamePath.props
```
then edit `GamePath` in `GamePath.props` to point at your Rec Room install root. This file is local-only and stays out of the repo.
2. **An environment variable:**
```sh
set RECROOM_PATH=C:\Path\To\RecRoom # cmd
$env:RECROOM_PATH = "C:\Path\To\RecRoom" # PowerShell
```
3. **On the command line:**
```sh
dotnet build -p:GamePath="C:\Path\To\RecRoom"
```
Then build:
```sh
dotnet build
```
The build validates that `GamePath` is set and that `$(GamePath)\BepInEx\interop` exists, and fails with a clear message otherwise.
A post-build step (the `DeployPlugin` target in the `.csproj`) automatically copies the built `RecNetPlugin.dll` into your Rec Room install's `BepInEx/plugins/` folder after every build. (The copy will fail if Rec Room is running, since the DLL is locked — close the game and rebuild.) Since `GamePath` already points at your install, you don't need to copy anything by hand — just `dotnet build` and launch the game.
If you need the DLL elsewhere, it's also left in `bin/Debug/net6.0/`.
## Configuration
Start the game for the first time. In `BepInEx` you should now see a `config` folder. If not, verify BepInEx installation and version.
Inside `config`, edit the `net.rec.plugin.cfg` file and update as needed:
**[Server]**
- `RecNet NameServer Host` — base URL of your RecNet name server (like `https://ns.rec.net`).
**[Photon]**
- `App Id Realtime` — Photon Realtime App ID.
- `App Id Voice` — Photon Voice App ID.
- `App Id Chat` — Photon Chat App ID.
**[Advanced]**
- `Enabled Advanced Settings` — must be `true` to apply the custom Photon name server / port below.
- `Photon NameServer` — custom Photon name server host.
- `Photon NameServer Port` — custom port (`0` uses the default, `4533`).
- `Debug` — verbose HTTP request/response logging (only needed for development)
> ⚠️ Debug logs include **sensitive data** (passwords, auth tokens). Be careful when sharing them.
- `Suppress DUID Mismatch` — skips the Create Account / DUID hang (see above). **On by default**; the
only DUID option meant for normal use. Set `false` only to observe the real mismatch for debugging.
The remaining `[Advanced]` DUID options — `Simulate DUID Mismatch`, `Corrupt Stored DUID`,
`Restore Stored DUID`, `DeviceId Response Override`, `DeviceId Response Status` — are **diagnostic
tools** used to investigate the hang. Leave them at their defaults unless you're debugging it; see
`CLAUDE.md` for what each one does.
## Project layout
| Path | Purpose |
| --- | --- |
| `Plugin.cs` | Plugin entry point, config bindings, Harmony bootstrap |
| `Patches/` | Harmony patches (HTTP, EAC, TLS, Photon, DUID) |
| `CLAUDE.md` | Developer notes: build gotchas, IL2CPP/interop caveats, and the full DUID-hang investigation |
| `RecNetPlugin.csproj` | Build config + interop references (driven by `GamePath`), and the `DeployPlugin` post-build copy |
| `GamePath.props.example` | Template for your local `GamePath.props` |
## FAQ
**Can I use this for my own Rec Room server?**
Yes. That's the point.
## Credits
Based on https://github.com/CannedNet/CannedNet.Client
## License
[MIT](LICENSE)
+948
View File
@@ -0,0 +1,948 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>RecNetPlugin</AssemblyName>
<Product>RecNetPlugin</Product>
<Version>1.0.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources>
https://api.nuget.org/v3/index.json;
https://nuget.bepinex.dev/v3/index.json;
https://nuget.samboy.dev/v3/index.json
</RestoreAdditionalProjectSources>
<RootNamespace>RecNetPlugin</RootNamespace>
</PropertyGroup>
<!-- GamePath = root of a Rec Room install whose BepInEx/interop/ has been populated.
It is intentionally NOT hardcoded here so the repo is distributable. Set it via ONE of:
1. a local GamePath.props file (copy GamePath.props.example -> GamePath.props; gitignored)
2. a RECROOM_PATH environment variable
3. the command line: dotnet build -p:GamePath="D:\Path\To\RecRoom" -->
<Import Project="$(MSBuildThisFileDirectory)GamePath.props" Condition="Exists('$(MSBuildThisFileDirectory)GamePath.props')" />
<PropertyGroup>
<GamePath Condition="'$(GamePath)' == '' and '$(RECROOM_PATH)' != ''">$(RECROOM_PATH)</GamePath>
</PropertyGroup>
<Target Name="ValidateGamePath" BeforeTargets="ResolveAssemblyReferences;Build">
<Error Condition="'$(GamePath)' == ''"
Text="GamePath is not set. Copy GamePath.props.example to GamePath.props and set your Rec Room install path (or set the RECROOM_PATH env var, or pass -p:GamePath=...). See CLAUDE.md." />
<Error Condition="'$(GamePath)' != '' and !Exists('$(GamePath)\BepInEx\interop')"
Text="GamePath '$(GamePath)' has no BepInEx\interop folder. Point it at a Rec Room install that has been launched once under BepInEx so the IL2CPP interop assemblies are generated." />
</Target>
<ItemGroup>
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.*" IncludeAssets="compile"/>
<PackageReference Include="BepInEx.PluginInfoProps" Version="2.*"/>
</ItemGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>$(GamePath)\BepInEx\interop\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>$(GamePath)\BepInEx\interop\Assembly-CSharp-firstpass.dll</HintPath>
</Reference>
<Reference Include="AstarPathfindingProject">
<HintPath>$(GamePath)\BepInEx\interop\AstarPathfindingProject.dll</HintPath>
</Reference>
<Reference Include="Backtrace.Unity">
<HintPath>$(GamePath)\BepInEx\interop\Backtrace.Unity.dll</HintPath>
</Reference>
<Reference Include="Circuits.All.Injection.Debugging">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.All.Injection.Debugging.dll</HintPath>
</Reference>
<Reference Include="Circuits.All.Injection.PhotonNetSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.All.Injection.PhotonNetSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.All.Injection.UnityEngine">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.All.Injection.UnityEngine.dll</HintPath>
</Reference>
<Reference Include="Circuits.All.Mock">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.All.Mock.dll</HintPath>
</Reference>
<Reference Include="Circuits.All.RecRoom">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.All.RecRoom.dll</HintPath>
</Reference>
<Reference Include="Circuits.Dynamic.Core.NetSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Dynamic.Core.NetSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Dynamic.Mock">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Dynamic.Mock.dll</HintPath>
</Reference>
<Reference Include="Circuits.Shared.Core.ByteCode">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Shared.Core.ByteCode.dll</HintPath>
</Reference>
<Reference Include="Circuits.Shared.CV2.Dependencies">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Shared.CV2.Dependencies.dll</HintPath>
</Reference>
<Reference Include="Circuits.Shared.RecRoom.Engine">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Shared.RecRoom.Engine.dll</HintPath>
</Reference>
<Reference Include="Circuits.Shared.RecRoom.Objects">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Shared.RecRoom.Objects.dll</HintPath>
</Reference>
<Reference Include="Circuits.Shared.Utilities">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Shared.Utilities.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.CompileSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.CompileSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.GraphSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.GraphSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.NetSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.NetSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.RequestReduce">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.RequestReduce.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.TreeSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.TreeSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.TypeCheckSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.TypeCheckSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.TypeSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.TypeSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Core.UnificationSystem">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Core.UnificationSystem.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.EV">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.EV.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.RecRoom">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.RecRoom.dll</HintPath>
</Reference>
<Reference Include="Circuits.Static.Utilities">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.Static.Utilities.dll</HintPath>
</Reference>
<Reference Include="Circuits.V2">
<HintPath>$(GamePath)\BepInEx\interop\Circuits.V2.dll</HintPath>
</Reference>
<Reference Include="Codestage.Anticheattoolkit.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\Codestage.Anticheattoolkit.Runtime.dll</HintPath>
</Reference>
<Reference Include="CSCore">
<HintPath>$(GamePath)\BepInEx\interop\CSCore.dll</HintPath>
</Reference>
<Reference Include="EasyAntiCheat.Client">
<HintPath>$(GamePath)\BepInEx\interop\EasyAntiCheat.Client.dll</HintPath>
</Reference>
<Reference Include="Google.Protobuf">
<HintPath>$(GamePath)\BepInEx\interop\Google.Protobuf.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib">
<HintPath>$(GamePath)\BepInEx\interop\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="Il2CppMicrosoft.Bcl.HashCode">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppMicrosoft.Bcl.HashCode.dll</HintPath>
</Reference>
<Reference Include="Il2CppMicrosoft.CognitiveServices.Speech.csharp">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppMicrosoft.CognitiveServices.Speech.csharp.dll</HintPath>
</Reference>
<Reference Include="Il2CppMicrosoft.Toolkit.HighPerformance">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppMicrosoft.Toolkit.HighPerformance.dll</HintPath>
</Reference>
<Reference Include="Il2CppMono.Security">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppMono.Security.dll</HintPath>
</Reference>
<Reference Include="Il2Cppmscorlib">
<HintPath>$(GamePath)\BepInEx\interop\Il2Cppmscorlib.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Buffers">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Buffers.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Configuration">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Configuration.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Core.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Data">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Data.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Drawing">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Drawing.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Memory">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Memory.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Numerics">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Numerics.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Numerics.Vectors">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Runtime.CompilerServices.Unsafe">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Runtime.Serialization">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Runtime.Serialization.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Xml">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Xml.dll</HintPath>
</Reference>
<Reference Include="Il2CppSystem.Xml.Linq">
<HintPath>$(GamePath)\BepInEx\interop\Il2CppSystem.Xml.Linq.dll</HintPath>
</Reference>
<Reference Include="Kyub.EmojiSearch">
<HintPath>$(GamePath)\BepInEx\interop\Kyub.EmojiSearch.dll</HintPath>
</Reference>
<Reference Include="Logger.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\Logger.Runtime.dll</HintPath>
</Reference>
<Reference Include="NewPlayerChallenges.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\NewPlayerChallenges.Runtime.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(GamePath)\BepInEx\interop\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Nito.Collections.Deque">
<HintPath>$(GamePath)\BepInEx\interop\Nito.Collections.Deque.dll</HintPath>
</Reference>
<Reference Include="Oculus.Platform">
<HintPath>$(GamePath)\BepInEx\interop\Oculus.Platform.dll</HintPath>
</Reference>
<Reference Include="Oculus.VR">
<HintPath>$(GamePath)\BepInEx\interop\Oculus.VR.dll</HintPath>
</Reference>
<Reference Include="OSA">
<HintPath>$(GamePath)\BepInEx\interop\OSA.dll</HintPath>
</Reference>
<Reference Include="Pathfinding.ClipperLib">
<HintPath>$(GamePath)\BepInEx\interop\Pathfinding.ClipperLib.dll</HintPath>
</Reference>
<Reference Include="Pathfinding.Ionic.Zip.Reduced">
<HintPath>$(GamePath)\BepInEx\interop\Pathfinding.Ionic.Zip.Reduced.dll</HintPath>
</Reference>
<Reference Include="Pathfinding.Poly2Tri">
<HintPath>$(GamePath)\BepInEx\interop\Pathfinding.Poly2Tri.dll</HintPath>
</Reference>
<Reference Include="Photon3Unity3D">
<HintPath>$(GamePath)\BepInEx\interop\Photon3Unity3D.dll</HintPath>
</Reference>
<Reference Include="PhotonChat">
<HintPath>$(GamePath)\BepInEx\interop\PhotonChat.dll</HintPath>
</Reference>
<Reference Include="PhotonRealtime">
<HintPath>$(GamePath)\BepInEx\interop\PhotonRealtime.dll</HintPath>
</Reference>
<Reference Include="PhotonUnityNetworking">
<HintPath>$(GamePath)\BepInEx\interop\PhotonUnityNetworking.dll</HintPath>
</Reference>
<Reference Include="PhotonUnityNetworking.Utilities">
<HintPath>$(GamePath)\BepInEx\interop\PhotonUnityNetworking.Utilities.dll</HintPath>
</Reference>
<Reference Include="PhotonVoice">
<HintPath>$(GamePath)\BepInEx\interop\PhotonVoice.dll</HintPath>
</Reference>
<Reference Include="PhotonVoice.API">
<HintPath>$(GamePath)\BepInEx\interop\PhotonVoice.API.dll</HintPath>
</Reference>
<Reference Include="PhotonVoice.PUN">
<HintPath>$(GamePath)\BepInEx\interop\PhotonVoice.PUN.dll</HintPath>
</Reference>
<Reference Include="Pngcs">
<HintPath>$(GamePath)\BepInEx\interop\Pngcs.dll</HintPath>
</Reference>
<Reference Include="RecNet.Interfaces">
<HintPath>$(GamePath)\BepInEx\interop\RecNet.Interfaces.dll</HintPath>
</Reference>
<Reference Include="RecNet.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecNet.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Agdxgidisplays.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Agdxgidisplays.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.AgInitialization.Interfaces">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.AgInitialization.Interfaces.dll</HintPath>
</Reference>
<Reference Include="RecRoom.AgInitialization.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.AgInitialization.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Agmobilear.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Agmobilear.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Analytics.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Analytics.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ApplicationLifecycle.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ApplicationLifecycle.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Assetbundles.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Assetbundles.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Async">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Async.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Attributes.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Attributes.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Audio.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Audio.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.AutomationTests.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.AutomationTests.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.BitPacker.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.BitPacker.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Build.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Build.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Challenges.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Challenges.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Chat.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Chat.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.CircuitsV1.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.CircuitsV1.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ClusterLods.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ClusterLods.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.CodeGen.Attributes">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.CodeGen.Attributes.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Commandline.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Commandline.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.CommonDataTypes.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.CommonDataTypes.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Configloader.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Configloader.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Connectables.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Connectables.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Content.Authoring.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Content.Authoring.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Creation.Interfaces.UX.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Creation.Interfaces.UX.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Creation.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Creation.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.CultureUtil.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.CultureUtil.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Datastructures.CollisionMesh.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Datastructures.CollisionMesh.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Datastructures.CullingGroupManager.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Datastructures.CullingGroupManager.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Datastructures.OverridableFields.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Datastructures.OverridableFields.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Datastructures.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Datastructures.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Datastructures.Singletons.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Datastructures.Singletons.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Debugging.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Debugging.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.EditorHelpers.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.EditorHelpers.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Encoding.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Encoding.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Experiments.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Experiments.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.FastLines.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.FastLines.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.FuzzySearch.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.FuzzySearch.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.GameSystems.Interfaces">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.GameSystems.Interfaces.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Imageutils.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Imageutils.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Imposters.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Imposters.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Instantiation.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Instantiation.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.iOSNative.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.iOSNative.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.JuniorAccountVisibility.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.JuniorAccountVisibility.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Keepsakes.Interfaces">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Keepsakes.Interfaces.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Keepsakes.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Keepsakes.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Keepsakes.UnityExtensions">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Keepsakes.UnityExtensions.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Localization.Service">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Localization.Service.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Maker.Core.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Maker.Core.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Maker.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Maker.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.MemoryStats.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.MemoryStats.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Minijson.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Minijson.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.MobileHome.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.MobileHome.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Nativemesh.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Nativemesh.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.DataTypes.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Networking.DataTypes.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.NetworkedObjects.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Networking.NetworkedObjects.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.PhotonImpl.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Networking.PhotonImpl.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.RoomLoading.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Networking.RoomLoading.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.RPC.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Networking.RPC.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Networking.SynchronizedFields.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Networking.SynchronizedFields.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.NoEngine.Algorithms.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.NoEngine.Algorithms.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.NoEngine.Common.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.NoEngine.Common.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.NoEngine.DataStructures.Performance.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.NoEngine.DataStructures.Performance.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.NoEngine.DataStructures.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.NoEngine.DataStructures.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.NoEngine.Debugging.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.NoEngine.Debugging.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.NoEngine.JetBrains.Annotations">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.NoEngine.JetBrains.Annotations.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Attributes.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Attributes.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.BitPacker.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.BitPacker.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.ComponentData.Generated.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.ComponentData.Generated.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.ComponentData.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.ComponentData.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.ConfigUI.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.ConfigUI.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Entities.Core.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Entities.Core.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Entities.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Entities.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Interfaces.ConfigUI.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Interfaces.ConfigUI.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Interfaces.Prefabs.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Interfaces.Prefabs.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Interfaces.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Interfaces.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Prefabs.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Prefabs.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Properties.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Properties.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Protobufs.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Protobufs.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.RendererV1.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.RendererV1.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Services.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Services.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Systems.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Systems.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Telemetry.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Telemetry.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Transmission.PUN.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Transmission.PUN.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectModel.Transmission.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectModel.Transmission.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ObjectPool.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ObjectPool.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Persistence.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Persistence.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.PlatformNotifications.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.PlatformNotifications.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Preferences.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Preferences.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.PrefParsers.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.PrefParsers.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ProgressionEvents.Interfaces">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ProgressionEvents.Interfaces.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ProgressionEvents.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ProgressionEvents.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Promises.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Promises.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Protobuf.Debugging.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Protobuf.Debugging.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Protobuf.Extensions.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Protobuf.Extensions.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Protobuf.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Protobuf.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Protobuf.UnityExtensions.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Protobuf.UnityExtensions.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Rbex.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Rbex.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ResourceManagement.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ResourceManagement.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.RoomLoading.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.RoomLoading.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.RoomPermissions.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.RoomPermissions.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Rranticheat.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Rranticheat.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.RRUI.Core.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.RRUI.Core.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.RRUI.Data.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.RRUI.Data.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.RRUI.Navigation.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.RRUI.Navigation.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.RRUI.Theme.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.RRUI.Theme.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Scheduling.Interface.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Scheduling.Interface.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Scheduling.Scheduler.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Scheduling.Scheduler.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Scheduling.Schedules.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Scheduling.Schedules.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.ShapeRendering.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.ShapeRendering.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Streamingaudio.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Streamingaudio.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Studio.Common.LocalTesting">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Studio.Common.LocalTesting.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Studio.Common.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Studio.Common.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.TagsAndLayers.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.TagsAndLayers.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Time.Interfaces">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Time.Interfaces.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Time.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Time.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Tweening.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Tweening.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.UIInteraction.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.UIInteraction.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Unityextensions.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Unityextensions.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.UrlHandler.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.UrlHandler.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Utf8json.Interfaces">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Utf8json.Interfaces.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Utf8json.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Utf8json.Runtime.dll</HintPath>
</Reference>
<Reference Include="RecRoom.Versioning.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\RecRoom.Versioning.Runtime.dll</HintPath>
</Reference>
<Reference Include="SA.Foundation">
<HintPath>$(GamePath)\BepInEx\interop\SA.Foundation.dll</HintPath>
</Reference>
<Reference Include="SA.Foundation.Network">
<HintPath>$(GamePath)\BepInEx\interop\SA.Foundation.Network.dll</HintPath>
</Reference>
<Reference Include="SA.iOS">
<HintPath>$(GamePath)\BepInEx\interop\SA.iOS.dll</HintPath>
</Reference>
<Reference Include="SA.iOS.XCode">
<HintPath>$(GamePath)\BepInEx\interop\SA.iOS.XCode.dll</HintPath>
</Reference>
<Reference Include="Singular">
<HintPath>$(GamePath)\BepInEx\interop\Singular.dll</HintPath>
</Reference>
<Reference Include="StansAssets.Foundation">
<HintPath>$(GamePath)\BepInEx\interop\StansAssets.Foundation.dll</HintPath>
</Reference>
<Reference Include="StansAssets.Plugins">
<HintPath>$(GamePath)\BepInEx\interop\StansAssets.Plugins.dll</HintPath>
</Reference>
<Reference Include="StatsigUnity.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\StatsigUnity.Runtime.dll</HintPath>
</Reference>
<Reference Include="SteamVR">
<HintPath>$(GamePath)\BepInEx\interop\SteamVR.dll</HintPath>
</Reference>
<Reference Include="SteamVR_Actions">
<HintPath>$(GamePath)\BepInEx\interop\SteamVR_Actions.dll</HintPath>
</Reference>
<Reference Include="TextureTool.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\TextureTool.Runtime.dll</HintPath>
</Reference>
<Reference Include="ToxMod">
<HintPath>$(GamePath)\BepInEx\interop\ToxMod.dll</HintPath>
</Reference>
<Reference Include="UJect.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\UJect.Runtime.dll</HintPath>
</Reference>
<Reference Include="UJect.UnityExtensions">
<HintPath>$(GamePath)\BepInEx\interop\UJect.UnityExtensions.dll</HintPath>
</Reference>
<Reference Include="Unity.Addressables">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Addressables.dll</HintPath>
</Reference>
<Reference Include="Unity.Burst">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Burst.dll</HintPath>
</Reference>
<Reference Include="Unity.Burst.Unsafe">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Burst.Unsafe.dll</HintPath>
</Reference>
<Reference Include="Unity.Collections">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Collections.dll</HintPath>
</Reference>
<Reference Include="Unity.Entities">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Entities.dll</HintPath>
</Reference>
<Reference Include="Unity.InputSystem">
<HintPath>$(GamePath)\BepInEx\interop\Unity.InputSystem.dll</HintPath>
</Reference>
<Reference Include="Unity.Jobs">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Jobs.dll</HintPath>
</Reference>
<Reference Include="Unity.Localization">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Localization.dll</HintPath>
</Reference>
<Reference Include="Unity.Mathematics">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Mathematics.dll</HintPath>
</Reference>
<Reference Include="Unity.ProBuilder">
<HintPath>$(GamePath)\BepInEx\interop\Unity.ProBuilder.dll</HintPath>
</Reference>
<Reference Include="Unity.Properties">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Properties.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipeline.Universal.ShaderLibrary">
<HintPath>$(GamePath)\BepInEx\interop\Unity.RenderPipeline.Universal.ShaderLibrary.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Core.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Universal.Runtime">
<HintPath>$(GamePath)\BepInEx\interop\Unity.RenderPipelines.Universal.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.ResourceManager">
<HintPath>$(GamePath)\BepInEx\interop\Unity.ResourceManager.dll</HintPath>
</Reference>
<Reference Include="Unity.Serialization">
<HintPath>$(GamePath)\BepInEx\interop\Unity.Serialization.dll</HintPath>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>$(GamePath)\BepInEx\interop\Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.ARFoundation">
<HintPath>$(GamePath)\BepInEx\interop\Unity.XR.ARFoundation.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.ARSubsystems">
<HintPath>$(GamePath)\BepInEx\interop\Unity.XR.ARSubsystems.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.Management">
<HintPath>$(GamePath)\BepInEx\interop\Unity.XR.Management.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.Oculus">
<HintPath>$(GamePath)\BepInEx\interop\Unity.XR.Oculus.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.OpenVR">
<HintPath>$(GamePath)\BepInEx\interop\Unity.XR.OpenVR.dll</HintPath>
</Reference>
<Reference Include="Unity.XR.PSVR">
<HintPath>$(GamePath)\BepInEx\interop\Unity.XR.PSVR.dll</HintPath>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AccessibilityModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.AccessibilityModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AIModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.AIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AndroidJNIModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.AndroidJNIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AnimationModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.AnimationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AssetBundleModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.AssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.AudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClothModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.ClothModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterInputModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.ClusterInputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterRendererModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.ClusterRendererModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CrashReportingModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.CrashReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DirectorModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.DirectorModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DSPGraphModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.DSPGraphModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GameCenterModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.GameCenterModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GIModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.GIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GridModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.GridModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.HotReloadModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.HotReloadModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ImageConversionModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.ImageConversionModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.IMGUIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.InputLegacyModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.InputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.JSONSerializeModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.JSONSerializeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.LocalizationModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.LocalizationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ParticleSystemModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.ParticleSystemModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PerformanceReportingModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.PerformanceReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.Physics2DModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.Physics2DModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.PhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ProfilerModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.ProfilerModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ScreenCaptureModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.ScreenCaptureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SharedInternalsModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.SharedInternalsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpatialTracking">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.SpatialTracking.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteMaskModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.SpriteMaskModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteShapeModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.SpriteShapeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.StreamingModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.StreamingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SubstanceModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.SubstanceModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SubsystemsModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.SubsystemsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.TerrainModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainPhysicsModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.TerrainPhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextCoreModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.TextCoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.TextRenderingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TilemapModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.TilemapModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TLSModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.TLSModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UI.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIElementsModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UIElementsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIElementsNativeModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UIElementsNativeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UmbraModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UmbraModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UNETModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UNETModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityAnalyticsModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityAnalyticsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityConnectModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityConnectModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityCurlModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityCurlModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityTestProtocolModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityTestProtocolModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityWebRequestModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VehiclesModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.VehiclesModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VFXModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.VFXModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VideoModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.VideoModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VirtualTexturingModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.VirtualTexturingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.VRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.WindModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.WindModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XRModule">
<HintPath>$(GamePath)\BepInEx\interop\UnityEngine.XRModule.dll</HintPath>
</Reference>
<Reference Include="Valve.Newtonsoft.Json">
<HintPath>$(GamePath)\BepInEx\interop\Valve.Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="XboxUWP">
<HintPath>$(GamePath)\BepInEx\interop\XboxUWP.dll</HintPath>
</Reference>
</ItemGroup>
<Target Name="DeployPlugin" AfterTargets="Build">
<Copy SourceFiles="$(OutputPath)$(AssemblyName).dll" DestinationFolder="$(GamePath)\BepInEx\plugins\" />
</Target>
</Project>