mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
added some stuff
This commit is contained in:
@@ -74,6 +74,7 @@ Tests/**/TestResults/
|
|||||||
/2023Patch
|
/2023Patch
|
||||||
!/2023Patch/
|
!/2023Patch/
|
||||||
/2023Patch/*
|
/2023Patch/*
|
||||||
|
!/2023Patch/README.md
|
||||||
!/2023Patch/RadicadeAugustClient/
|
!/2023Patch/RadicadeAugustClient/
|
||||||
/2023Patch/RadicadeAugustClient/bin/
|
/2023Patch/RadicadeAugustClient/bin/
|
||||||
/2023Patch/RadicadeAugustClient/obj/
|
/2023Patch/RadicadeAugustClient/obj/
|
||||||
|
|||||||
@@ -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 `<Compile Include>` 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.
|
||||||
@@ -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='<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. |
|
||||||
+1
-1
@@ -5,7 +5,7 @@ VisualStudioVersion = 17.14.36705.20
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{9DDA5EBF-9DCD-45C3-83ED-2AC383109529}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{9DDA5EBF-9DCD-45C3-83ED-2AC383109529}"
|
||||||
EndProject
|
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
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023Patch", "2023Patch", "{05A810AF-0524-D221-1DA2-A901B183F7A6}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023Patch", "2023Patch", "{05A810AF-0524-D221-1DA2-A901B183F7A6}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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`.
|
||||||
@@ -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] <path>...`. |
|
||||||
|
| `InteropInspect/` (C#, net9.0, exe) | Reads a managed assembly's metadata and searches it: `InteropInspect <assembly.dll> <keyword>...` or `--type <FullName>` 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.
|
||||||
@@ -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='<secret>' bash VPS/fix-dorm-blob.sh
|
||||||
|
BOT_SECRET='<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/<blob>`; 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.
|
||||||
Reference in New Issue
Block a user