Files
Radicade/README.md
T
2026-10-16 23:43:29 -07:00

168 lines
9.4 KiB
Markdown

# 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='<random 32+ byte base64>'
export Radicade__AdminApiKey='<random>'
export Radicade__SteamApiKey='<steam web api key>'
```
## 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. |