diff --git a/.gitignore b/.gitignore index 72b693a..11f8aef 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ Tests/**/TestResults/ /2023Patch !/2023Patch/ /2023Patch/* +!/2023Patch/README.md !/2023Patch/RadicadeAugustClient/ /2023Patch/RadicadeAugustClient/bin/ /2023Patch/RadicadeAugustClient/obj/ diff --git a/2023Patch/README.md b/2023Patch/README.md new file mode 100644 index 0000000..ca8155c --- /dev/null +++ b/2023Patch/README.md @@ -0,0 +1,78 @@ +# 2023Patch — client-side plugins + +BepInEx 6 (IL2CPP) plugins that make an installed 2023 Rec Room build talk to a +Radicade server instead of Rec Room's own services. All projects target `net6.0`, use +Harmony, and `AllowUnsafeBlocks` for the metadata/image-key work. + +## Projects + +| Folder | Plugin | Notes | +| --- | --- | --- | +| `RadicadePatch/` | `com.radicade.patch` "RadicadePatch" 3.0.0 | Current plugin, aimed at the September 2023 build (`20230831`). The server address is compiled in (`Plugin.ServerAddress`, `https://ns.radicade.app/`). | +| `RadicadeAugustClient/` | `com.radicade.eacbypass` "Radicade August Client" 2.0.0 | Earlier plugin for the August 2023 build (`20230825`). Requires the metadata/interop repair in `Tools/AugustInterop` first. See its own README. | +| `RadicadeEacBypass/` | — | Shared `.cs` sources, not a project. Linked into both plugins (and into `ServerTests`). The root `.gitignore` ignores the folder and then re-allows each shared file individually, so add an entry there when you add a file. `VoiceChatRestrictionPatches.cs` is neither tracked nor present. | + +## Building a plugin + +Both plugin csproj files reference BepInEx assemblies through a `BepInExRoot` +property that defaults to a machine-local path. Point it at the game's `BepInEx` +folder: + +```bash +dotnet build 2023Patch/RadicadePatch/RadicadePatch.csproj -c Release \ + -p:BepInExRoot='/c/Program Files/RecRoom/BepInEx' +``` + +Then copy the produced `RadicadePatch.dll` into `BepInEx/plugins`. The two plugins +share a configuration identity, so **install only one at a time** — disable or back up +the other DLL. + +> `RadicadePatch` currently fails to compile because it links +> `RadicadeEacBypass/VoiceChatRestrictionPatches.cs`, which is not in this +> repository. Supply that file (or drop the `` and the calls to it +> from `Plugin.cs`/`VoiceChatBanFlagPatches.cs`) before building it. +> `RadicadeAugustClient` does not reference that file. + +## What the patches do + +- **Nameserver redirect** (`NameserverRedirect.cs`) — maps the service hosts the + client learns from any nameserver onto the equivalent paths on the Radicade server + (`auth.…` → `/auth`, `rooms.…` → `/rooms`, and ~40 more), including WebSocket paths. +- **Connection gate / nameserver patches** — pin the client to the configured server + and reject other hosts. +- **Embedded TLS** (`EmbeddedTlsPatches.cs`) — replaces the trust decision for the + configured host only, so no certificate is installed system-wide and no other host + becomes trusted. +- **Hub socket auth** (`HubSocketAuth.cs`) — carries the session token onto the notify + WebSocket. +- **Response repair / HTTP tolerance** (`ResponseRepair.cs`, `HttpTolerancePatches.cs`) + — normalises responses the client is strict about and tolerates the shapes the + reimplementation produces. +- **Image signature key** (`ImageSignatureRequest.cs`) — detects the key id from the + game's metadata so signed image requests can be verified. +- **Client text rewrites**, UI prefab repair, collider-cast guard, keepsake tool, + invite-online-friends and voice-chat flag patches — the smaller gameplay/UI fixes + in `RadicadePatch/`. + +## Configuration + +The August plugin writes `BepInEx/config/com.radicade.eacbypass.cfg`: + +```ini +[Connection] +ServerUrl = https://localhost:3030 +[TLS] +AllowedHosts = localhost +``` + +TLS exceptions are limited to `AllowedHosts`. The current `RadicadePatch` compiles +its server address in and warns at startup if the install's +`RecRoom_Data/radicade-patch.json` marker points at a different server. + +## Release build + +The release pipeline obfuscates the plugin with **Obfuscar** (`RadicadePatch/obfuscar.xml`, +tool pinned in the root `dotnet-tools.json`). Because obfuscation renames members that +the plugin looks up by name (Harmony patches, reflection, interop), run +`Tools/check-obfuscation-safety.py` first — it fails when a renamed member is still +reached by string. diff --git a/README.md b/README.md new file mode 100644 index 0000000..33c6a1e --- /dev/null +++ b/README.md @@ -0,0 +1,167 @@ +# Radicade + +Radicade is an unofficial, community-built private-server toolchain for **archived +Rec Room 2023 clients**. It is two halves that meet in the middle: + +- **A server** (`Server/`) — an ASP.NET Core reimplementation of the RecNet API that + the 2023 client expects: nameserver discovery, auth/account creation, players, + rooms, matchmaking, clubs, gifts, storefront/commerce, moderation, a CDN + object + storage, an admin panel API and a SignalR notification hub. +- **Client patches** (`2023Patch/`) — BepInEx 6 (IL2CPP) plugins that point a locally + installed 2023 build at your own server: they rewrite the addresses the client + hardcodes into its IL2CPP string table, redirect the service hosts the nameserver + advertises, and replace the embedded TLS trust decision for one configured host + only. + +> **Not affiliated with Rec Room Inc. or Against Gravity.** This project is for +> preservation and private testing with game clients you already own. Do not point +> it at, or use it against, official Rec Room services. +> +> There is **no `LICENSE` file** in this repository yet — add one before publishing. + +## Repository layout + +| Path | What it is | +| --- | --- | +| [`Server/`](Server) | ASP.NET Core 9 (`.NET 9`) server, ~545 C# files. Entity Framework Core + SQL Server, JWT bearer auth, ImageSharp for image transforms, SignalR notify hub. See [`Server/README.md`](Server/README.md). | +| [`ServerTests/`](ServerTests) | xUnit test suite (~1,600 tests) run against EF Core InMemory/SQLite providers. See [`ServerTests/README.md`](ServerTests/README.md). | +| [`2023Patch/`](2023Patch) | BepInEx client plugins and the shared patch sources they compile. See [`2023Patch/README.md`](2023Patch/README.md). | +| [`Tools/`](Tools) | Python, PowerShell and C#/Node developer tooling: metadata patching, install triage, interop repair, packaging. See [`Tools/README.md`](Tools/README.md). | +| [`RoomImports/`](RoomImports) | Captured room saves and asset bundles used by the room-import tests. Third-party game content. | +| [`VPS/`](VPS) | Deployment and maintenance scripts for the hosted server. See [`VPS/README.md`](VPS/README.md). | +| [`docs/`](docs) | Dated engineering notes (working logs, not polished docs). | +| [`Caddyfile`](Caddyfile) | Reverse-proxy config used in front of the server in production. | +| [`RadNet.sln`](RadNet.sln) | Solution: `Server`, `Server.Tests`, `RadicadePatch`. | +| [`dotnet-tools.json`](dotnet-tools.json) | Pins the `obfuscar.globaltool` used to obfuscate the client plugin. | + +## How a patched client reaches the server + +1. The 2023 client hardcodes its bootstrap and service URLs. `Tools/patch-0831-client-metadata.py` + (and the Quest equivalent) rewrites the matching records in the game's + `global-metadata.dat`, so the client resolves *your* host instead of `rec.net`. +2. At runtime the plugin serves `GET /ns` on your server — a nameserver document that + advertises where every service lives. `NameserverRedirect` maps any service host the + client learned elsewhere onto the equivalent path on your server. +3. The plugin's embedded TLS stack accepts a certificate for the configured host only + (`TLS:AllowedHosts`), so no system-wide trust change is needed. +4. The server answers those calls out of SQL Server plus its local CDN/data folders, and + pushes realtime traffic over the notify hub (`/notify/hub/v1`). + +## Requirements + +- **.NET SDK 9** for the server, tests and client plugin. (Built here with SDK 10.0.401.) +- **SQL Server** for actually running the server (the dev connection string targets + `.\SQLEXPRESS`). The test suite does not need it. +- **Node.js** for `Tools/AugustInterop/normalize.cjs`. +- **BepInEx 6.x (IL2CPP)** installed in a 2023 game folder, to build the client plugins. +- **Python 3** and **PowerShell** for the scripts in `Tools/`. + +## Build + +```bash +dotnet build Server/Server.csproj +dotnet build ServerTests/Server.Tests.csproj +``` + +The solution `RadNet.sln` includes the client plugin too, which needs the BepInEx +assemblies and one source file that is not in this repository — see +[Current status](#current-status). + +## Run the server + +```bash +dotnet run --project Server +``` + +In **Development** it listens on `https://localhost:3030` and `http://127.0.0.1:3031` +and generates a self-signed dev certificate under `RadicadeServer-Data/Persistent/`. +In **Production** it listens on `http://0.0.0.0:3030` and expects TLS to be terminated +in front of it — that is what the bundled `Caddyfile` (`ns.radicade.app` → +`127.0.0.1:3030`) does. + +Database migrations are applied automatically at startup (`Database.Migrate()` plus a +set of idempotent `Ensure…Column`/`Ensure…Table` helpers for tables added after the +last migration). + +Local data lives under `RadicadeServer-Data/` by default: `CDN/{Images,Videos,Storage,Custom}` +and `Persistent/Logs`, `Persistent/DataProtection-Keys`. Both the public and private +data roots are overridable with `Radicade:PublicDataPath` / `Radicade:PrivateDataPath`. + +## Configuration + +Settings are read from `appsettings.json`, `appsettings.{Environment}.json`, environment +variables and user secrets, in ASP.NET Core's usual order. The checked-in files carry +placeholders (`USEYOURS`); supply real values locally. + +| Key | Purpose | +| --- | --- | +| `ConnectionStrings:DefaultConnection` | SQL Server connection string. Required. | +| `Radicade:ServerUrl` | Public base URL the server advertises. | +| `Radicade:ListenUrls` | Semicolon-separated Kestrel URLs; falls back to the environment defaults above. | +| `Radicade:JwtKey` | Signing key for session/access tokens. Required in Production. | +| `Radicade:AdminApiKey` | Shared secret for the `/admin` API and maintenance scripts. Required in Production. | +| `Radicade:SupportApiKey` | Second shared secret accepted by admin endpoints. | +| `Radicade:SteamApiKey` | Steam Web API key for identity proof. Required in Production unless `TrustUnverifiedSteamIdentity`. | +| `Radicade:TrustUnverifiedSteamIdentity` | Dev switch: accept unproved platform identity (defaulted on in Development when no key is set). | +| `Radicade:Photon:{RealtimeAppId,VoiceAppId,ChatAppId,Region}` | Photon app IDs handed to clients for realtime, voice and chat. | +| `Radicade:MailApiKey`, `MailApiUrl`, `MailFrom`, `MailFromName` | Outbound mail (Resend-compatible HTTP API). | +| `Radicade:AdminPanelUrl` | Admin panel origin the API points at. | +| `Radicade:Admins[]` | `{ "Username", "Password" }` panel login accounts. | +| `Radicade:TestInfluencerAccountIds`, `TestCoOwnerAccountIds` | Seeded test accounts. | +| `Radicade:PublicDataPath`, `Radicade:PrivateDataPath` | Override the data roots. | +| `AllowedHosts` | Host filtering. | +| `Discord:Webhooks:*` | Discord webhook URLs. Copy `Server/Discord/Webhooks/webhooks.example.json` to `webhooks.json` (git-ignored). | + +`Server/Program.cs` refuses to start in Production while `AdminApiKey`, `JwtKey` or +`SteamApiKey` are blank, so set them as environment variables rather than committing +them: + +```bash +export ConnectionStrings__DefaultConnection='Server=...;Database=RadicadeServer;...' +export Radicade__JwtKey='' +export Radicade__AdminApiKey='' +export Radicade__SteamApiKey='' +``` + +## Tests + +```bash +dotnet test ServerTests/Server.Tests.csproj +``` + +The suite runs on `Microsoft.EntityFrameworkCore.InMemory` and SQLite, seeds its own +fixtures, and also compiles two shared client sources +(`NameserverRedirect.cs`, `ImageSignatureRequest.cs`) so the client/server routing +contract is tested in one place. + +## Configuration data + +`Server/RadicadeServer-Config/` holds the catalogs the server serves: storefront +captures (`storefronts/sf*.json`, embedded as resources so a wiped database or a +build without the folder still works), avatar/equipment/consumables catalogs, +`levelup-rewards.json`, `keepsake-rooms.json` and `steamwhitelist.json`. Dropping a +new `sf{id}.json` in overrides the embedded copy without a rebuild. + +## Current status + +This is a working-in-progress preservation project, not a finished product: + +- `Server` and `ServerTests` compile cleanly; the full test suite currently reports + **~1,605 passing, 1 skipped and a handful failing** on this checkout (storefront + capture count, a few catalog/locker and weekly-challenge rollover tests). Re-run + locally to see the current list. +- `2023Patch/RadicadePatch` (the current September client plugin) references + `RadicadeEacBypass/VoiceChatRestrictionPatches.cs`, which is not part of this + repository, so `dotnet build RadNet.sln` fails on that project until the file is + supplied. `Server`, `ServerTests` and `RadicadeAugustClient` are unaffected. +- Client-side progress is tracked in [`docs/`](docs): login, Photon multiplayer, watch + UI and full room loading are the areas still being ported. + +## Documentation + +| Doc | Contents | +| --- | --- | +| [`docs/2023-AUGUST-BUILD.md`](docs/2023-AUGUST-BUILD.md) | August 2023 (build 20230825) client migration log. | +| [`docs/2023-0831-CLIENT-SWITCH.md`](docs/2023-0831-CLIENT-SWITCH.md) | Switching the client to September 2023 (build 20230831) and the per-service host split. | +| [`docs/QUEST-20230831-TEST.md`](docs/QUEST-20230831-TEST.md), [`docs/QUEST-20230907-TEST.md`](docs/QUEST-20230907-TEST.md) | Quest client TLS/instrumentation experiments. | +| [`docs/2023-ONBOARDING-FORCE-NOTES.md`](docs/2023-ONBOARDING-FORCE-NOTES.md), [`docs/GIFT-CONTEXT-DUMP.md`](docs/GIFT-CONTEXT-DUMP.md), [`docs/KEEPSAKES-STATUS.md`](docs/KEEPSAKES-STATUS.md), [`docs/WEEKLY-CHALLENGES.md`](docs/WEEKLY-CHALLENGES.md) | Feature-specific notes and enum dumps. | diff --git a/RadNet.sln b/RadNet.sln index 1486ad0..49215a5 100644 --- a/RadNet.sln +++ b/RadNet.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 17.14.36705.20 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{9DDA5EBF-9DCD-45C3-83ED-2AC383109529}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Tests", "Server.Tests\Server.Tests.csproj", "{C108E570-E61B-49CF-8590-CF8B983ED6B0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Tests", "ServerTests\Server.Tests.csproj", "{C108E570-E61B-49CF-8590-CF8B983ED6B0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023Patch", "2023Patch", "{05A810AF-0524-D221-1DA2-A901B183F7A6}" EndProject diff --git a/Server/README.md b/Server/README.md new file mode 100644 index 0000000..4fa1fd4 --- /dev/null +++ b/Server/README.md @@ -0,0 +1,86 @@ +# Server + +ASP.NET Core 9 (`net9.0`) implementation of the RecNet API surface that archived +2023 Rec Room clients call. Root namespace `RadicadeServer`; one process serves both +the game API and the public web/share endpoints. + +## Running + +```bash +dotnet run --project Server +``` + +Development listens on `https://localhost:3030` and `http://127.0.0.1:3031` (see +`appsettings.Development.json`); Production defaults to `http://0.0.0.0:3030` for a +TLS-terminating proxy in front (see the repo-level `Caddyfile`). + +`Program.cs` builds the host, loads configuration into `DBConfig`, then: + +- fails fast in Production if `AdminApiKey`, `JwtKey` or `SteamApiKey` are blank; +- registers the EF Core `RecDBContext` (SQL Server, retry-on-failure, pending-model + warnings ignored); +- wires JWT bearer auth (signing key from `Radicade:JwtKey`; the `access_token` query + parameter is accepted on notify-hub paths for WebSocket clients) plus a token + version check that revokes sessions; +- configures ImageSharp (with a `cropSquare` command used by avatar images); +- applies migrations and idempotent schema fix-ups at startup; +- maps controllers and the `NotifyHub` SignalR hubs. + +## Layout + +| Folder | Contents | +| --- | --- | +| `Program.cs` | Host, DI, auth, middleware pipeline and startup schema work. | +| `DB/` | `DBConfig` (all runtime configuration + paths), `RecDBContext`, `Defaults`, factory and bootstrap helpers. | +| `Migrations/` | EF Core migrations. | +| `NameServer/` | `GET /`, `/ns`, `/nameserver` — the document that tells a client where every service lives. | +| `Auth/`, `AccountCreation/` | Login, token issuance, onboarding flows, platform identity proofing. | +| `Players/`, `PlayersBanned/`, `Relationships/`, `Messages/`, `Chat/`, `Notifications/` | Player records, presence, friends, direct messages, chat and notifications. | +| `Rooms/`, `Clubs/`, `Board/`, `PlayerEvents/`, `Inventions/`, `Keepsakes/`, `Studio/`, `Royale/` | User-generated and social content surfaces. | +| `Avatars/`, `CustomAvatarItems/`, `Images/`, `CDN/`, `Storage/`, `LoadingScreens/`, `Launcher/` | Cosmetics, image/video CDN routes, stored blobs, launcher manifest. | +| `Commerce/`, `Storefront/`, `Gift/`, `Promotions/`, `PlayerCheer/`, `ProgressionEvents/` | Economy: storefronts, purchases, gifts, promotions, progression rewards. | +| `Matchmaking/`, `Activities/`, `Leaderboards/`, `Compatibility/`, `Health/` | Session/matchmaking, activity feed, leaderboards, version compatibility checks, health endpoints. | +| `Moderation/`, `PlayerReporting/`, `PlayerWarnings/`, `PlayerReputation/`, `BanAppeal/` | Moderation and safety tooling. | +| `Config/` | Client configuration/split-test endpoints and player settings. | +| `Analytics/`, `BugReporting/` | Game log ingestion and bug reports. | +| `Admin/` | Admin panel API. Endpoints authenticate with `AdminApiKey`/`SupportApiKey` (see `AdminUtils`). | +| `Web/` | Public web/share API (`/web/...`). | +| `Discord/` | Discord webhook config (`webhooks.json` git-ignored, `webhooks.example.json` tracked). | +| `RecNet/`, `Radicade/` | Shared response/enum models and RecNet-shaped DTOs. | +| `RadicadeServer-Config/` | Catalogs served to clients (storefronts, avatars, equipment, keepsakes, level-up rewards, Steam whitelist). | +| `Middleware/`, `Extensions/`, `Utils/` | Cross-cutting plumbing and helpers. | +| `Tooling/` | Debug and test-case management endpoints. | + +## Routing and multi-version support + +Routes are declared with controller attributes and many endpoints deliberately expose +several paths for the same handler, because different 2023 builds call slightly +different URLs (for example `~/clubs/club/home/me`, `~/club/home/me` and +`~/api/clubs/v1/home/me`). Supported client versions are listed in +`DBConfig.SupportedGameVersions`, with **`20230831`** (September 2023) as the primary +target and `20230301`–`20231207` plus a couple of 2025 builds also accepted. + +CORS is wide open (`AllowAllOrigins`), which is appropriate for a self-hosted server +serving the game client and local tooling. + +## Data + +`DBConfig.PublicData` defaults to `./RadicadeServer-Data/` and is split into: + +- `CDN/Images`, `CDN/Videos`, `CDN/Storage`, `CDN/Custom` — served content and uploads; +- `Persistent/Logs` — rotating logs and generated `debugkey.txt`; +- `Persistent/DataProtection-Keys` — Data Protection key ring; +- `Persistent/localhost-rsa-aes-devcert.pfx` — the Development HTTPS certificate. + +## Configuration + +See the configuration table in the [root README](../README.md#configuration). The +checked-in `appsettings.*.json` files contain `USEYOURS` placeholders; override real +values with user secrets or environment variables (`Radicade__JwtKey`, +`ConnectionStrings__DefaultConnection`, …). Production startup validates the three +required secrets. + +## Tests + +Covered by [`ServerTests/`](../ServerTests) — see that README for how to run them and +which helpers they use. diff --git a/ServerTests/README.md b/ServerTests/README.md new file mode 100644 index 0000000..ae4a48b --- /dev/null +++ b/ServerTests/README.md @@ -0,0 +1,56 @@ +# ServerTests + +xUnit suite for the server and for the parts of the client patch that must agree with +it. The folder is `ServerTests/` but the project file (and assembly) is +`Server.Tests.csproj` / `Server.Tests.dll`, root namespace `RadicadeServer.Tests`. + +## Running + +```bash +# whole suite +dotnet test ServerTests/Server.Tests.csproj + +# a single feature area +dotnet test ServerTests/Server.Tests.csproj --filter FullyQualifiedName~GiftTests + +# one test +dotnet test ServerTests/Server.Tests.csproj --filter DisplayName~WeeklyChallenge +``` + +Around 1,600 tests run in well under a minute. On the current checkout roughly +1,605 pass, 1 is skipped and a handful fail (storefront capture count, some +catalog/locker and weekly-challenge rollover tests) — treat the run output as the +source of truth rather than the README. + +## How the tests are wired + +- **Database providers.** Tests never touch SQL Server. They build a context through + `TestDbContextFactory`, which uses `Microsoft.EntityFrameworkCore.InMemory` where a + fast stand-in is enough and `Microsoft.EntityFrameworkCore.Sqlite` where real + relational behaviour matters. +- **Static configuration.** A lot of server behaviour is configured through static + fields on `DBConfig` (for example `DBConfig.SteamApiKey`, `DBConfig.AdminApiKey`, + `DBConfig.JwtKey`). Tests assign throwaway values such as `"unit-test-steam-api-key"` + and restore the previous value in a `finally` block. +- **Fixtures.** `Fixtures/*.json` are linked from `Server/RadicadeServer-Config/` + (`avatar.json`, `merged-avatar.json`, `equipment*.json`, `consumables.json`, + `avatar-item-lookup.json`, `2022-avatar.json`) and copied to the output directory; + `Fixtures/split-tests-20230301.json` lives in the repository itself. +- **Shared client sources.** `NameserverRedirect.cs` and `ImageSignatureRequest.cs` + from `2023Patch/RadicadeEacBypass/` are compiled into this test assembly, so the + host-redirect map and image-signature contract are asserted server-side. +- **Test doubles.** Helpers such as `TestDbContextFactory`, `SetBody`, `CreateEventAsync` + and the various request builders keep test bodies focused on the assertion. + +## Adding tests + +Follow the existing shape: one class per feature area, `[Fact]`/`[Theory]` per case, +names that read as a sentence about the behaviour (`AvatarUnlockCatalog_Preserves…`). +Prefer asserting the exact JSON shape the 2023 client expects over asserting internal +state, since response-shape conformance is what most of this suite protects. + +## Known warnings + +The build emits `xUnit1031` warnings in `RecNetShapeConformanceTests.cs` where tests +block on tasks (`.Result`/`.GetAwaiter().GetResult()`). They are pre-existing; the +inline fix is to make the test `async` and `await`. diff --git a/Tools/README.md b/Tools/README.md new file mode 100644 index 0000000..2a5d94d --- /dev/null +++ b/Tools/README.md @@ -0,0 +1,51 @@ +# Tools + +Developer and diagnostic scripts. Most are read-only: the triage and inspection tools +never modify a game install, and the patchers write a separate output or require an +explicit `--apply`. + +## C# / Node projects + +| Tool | What it does | +| --- | --- | +| `AugustInterop/` (C#, net9.0 + `normalize.cjs`) | Repairs il2cpp metadata/interop generation for the August 2023 build (`20230825`): normalises the shuffled 256-byte `global-metadata.dat` header and patches the BepInEx loader to point at the normalised copy. See its [README](AugustInterop/README.md). | +| `ColliderGuard.Tests/` (C#, net6.0, exe) | Regression harness for `ColliderCastGuardPatch` / `ToolCollisionLayerRestore`, compiled straight from `2023Patch/RadicadePatch/` against the game's Harmony/MonoMod assemblies. | +| `CommentStripper/` (C#, net9.0, exe) | Strips comments from C# sources using the real Roslyn parser (not a text scan), re-parsing its own output to prove only trivia moved. `CommentStripper [--dry-run] ...`. | +| `InteropInspect/` (C#, net9.0, exe) | Reads a managed assembly's metadata and searches it: `InteropInspect ...` or `--type ` for a full listing of one type. | +| `recroom-mirror/` (Node.js ESM) | Local mirror/adapter for RecNet web content: `download.mjs` caches pages from `recroom.network`, `public-cache.mjs` serves the cached public endpoints, `radicade-adapter.mjs` proxies to a Radicade server (`RADICADE_WEB_SERVER`), and `serve.mjs` puts both behind one local HTTP server. | + +## Client metadata patching (Python) + +| Tool | What it does | +| --- | --- | +| `patch-0831-client-metadata.py` | Points a Rec Room `20230831` install at a Radicade server by rewriting the `rec.net` bootstrap URLs in the IL2CPP string table. | +| `patch-quest-client-metadata.py` | Same, for the unpacked Quest `20230907` client (accepts the Apktool-decoded APK tree). | +| `patch-quest-standalone-login.py` | Experimental Quest standalone login flow; needs the matching server platform guard. | +| `sep-literals.py` | Reads/inspects the il2cpp string-literal table of a `20230831` client (the metadata header is not the standard magic). | +| `sep-scan.py` | Locates metadata-usage tokens/pointers inside an il2cpp binary. | +| `sept-map.py` | Maps obfuscated Il2CppInterop names between two game builds. | +| `package-quest-obb.py` | Rebuilds an Android expansion OBB from an Apktool-decoded tree. | +| `diagnose-quest-tls.py` | Builds an unsigned, logging-only diagnostic APK that preserves certificate decisions. | +| `quest-disassemble.py` | Read-only ARM64 inspection with optional on-device method-map annotations. | + +## Triage and analysis (PowerShell / Python) + +| Tool | What it does | +| --- | --- | +| `august-client-triage.ps1` | Read-only triage of a patched August 2023 install; writes a report naming the missing piece of the stack. | +| `september-client-triage.ps1` | Read-only triage of a patched September 2023 install (same idea, `20230831`). | +| `client-exit-triage.ps1` | Separates a crash from an orderly exit using the Windows Application event log and the last Unity log. | +| `player-log-window.py` | Slices `Player.log` by wall-clock window and summarises it. | +| `make-august-client-pack.ps1` | Builds a shareable client bundle from a known-good August install, verifying every required file against a reference. | +| `check-obfuscation-safety.py` | Fails when Obfuscar renaming would break a by-name (Harmony/reflection/interop) lookup in the client plugin. | + +## Environment repair + +| Tool | What it does | +| --- | --- | +| `patch-rrs-nameserver.ps1` | Points the Rec Room Studio Unity editor at a Radicade server instead of Rec Room's nameserver. | +| `repair-unity-script-compiler.ps1` | Restores the Roslyn script compiler that Windows Defender quarantines out of the Unity package cache. | +| `reorganize-server-features.sh` | Moves server sources into feature folders without touching namespaces; `--plan` prints, `--apply` moves. | + +Tooling that needs a game install takes the path as an argument or a documented +default; change the default rather than editing the script in place. diff --git a/VPS/README.md b/VPS/README.md new file mode 100644 index 0000000..41fdd5a --- /dev/null +++ b/VPS/README.md @@ -0,0 +1,30 @@ +# VPS + +Scripts for the host running the public server (a Windows VPS behind Caddy; see the +repo-level [`Caddyfile`](../Caddyfile), which terminates TLS for `ns.radicade.app` and +proxies to `127.0.0.1:3030`). + +## `fix-dorm-blob.sh` + +Repairs the "joining room" / dorm-load crash caused by a room save that references a +DataBlob missing from CDN storage. It has two modes: + +```bash +# MODE 1 — data fix: upload a valid empty room blob through the admin API. +# Requires the server's BotSecret (the Radicade:AdminApiKey / bot secret). +BOT_SECRET='' bash VPS/fix-dorm-blob.sh +BOT_SECRET='' HOST=https://ns.radicade.app bash VPS/fix-dorm-blob.sh + +# MODE 2 — durable code fix: deploy the RoomUtils self-heal on the host. +bash VPS/fix-dorm-blob.sh --deploy +``` + +Mode 1 POSTs the blob to `/admin/rooms/upload/datablob` and then verifies it via +`GET /room/`; HTTP 401 means a wrong secret, `000`/timeout means the backend is +down. Mode 2 pulls the branch, publishes the server, restarts the service and checks +`GET /ns`. + +Notes: the secret is taken from `$BOT_SECRET` or `$1` and is never written to disk by +the script, so never commit it — pass it through the environment. The service restart +assumes a systemd-style unit; if the host restarts the server another way, change +`mode2` accordingly.