Add initial documentation and structure for DorkNet project

- Create README.md for project overview and setup instructions
- Add admin-ui/README.md for admin UI details and development setup
- Introduce client-mod/README.md explaining the MelonLoader mod functionality
- Establish docker/README.md for Docker setup and configuration
- Create docs/README.md to index project documentation
- Implement advanced-setup.md for Docker stack configuration
- Document architecture in architecture.md
- Provide easy-setup.md for hosting instructions
- Add FAQ.md for common questions and legal considerations
- Create joining-a-server.md for player connection instructions
- Establish troubleshooting.md for common issues and resolutions
- Introduce easy-app/README.md for the unified desktop application
- Add server/README.md for ASP.NET Core backend details
- Document tools/README.md for CLI helper scripts
This commit is contained in:
Alexa
2026-05-21 13:26:46 +03:00
commit bcc5fe1bcb
18 changed files with 1752 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
# DorkNet docs
| Doc | For who |
| -------------------------------------- | ---------------------------------------------------- |
| [easy-setup.md](easy-setup.md) | Hosts who want the desktop-app one-click experience |
| [advanced-setup.md](advanced-setup.md) | Hosts running a community server on a VPS |
| [joining-a-server.md](joining-a-server.md) | Players whose friend gave them a join code |
| [architecture.md](architecture.md) | Anyone hacking on the code |
| [faq.md](faq.md) | Common questions before you ask in issues |
| [troubleshooting.md](troubleshooting.md) | Known failure modes + fixes |
+129
View File
@@ -0,0 +1,129 @@
# Advanced mode — Docker stack
For hosts running a community server (more than ~10 concurrent players),
doing custom modding work, or contributing to DorkNet itself.
If you just want to play with friends, the [easy-setup](easy-setup.md)
desktop app is what you want.
## What you get
- **server**: the ASP.NET Core backend
- **postgres**: the canonical store (the desktop app uses SQLite; for >10
concurrent players, Postgres is what you want)
- **redis**: cross-replica presence cache (skip with `REDIS_URL=` empty
to fall back to in-memory; loses presence on restart but otherwise fine)
- **nginx**: TLS termination + multi-subdomain routing
(`api.*`, `auth.*`, `accounts.*`, `match.*`, `chat.*`, `cdn.*`, `commerce.*`,
`img.*`, `leaderboard.*`, `notify.*`)
Behind a proper TLS certificate (Let's Encrypt via Caddy or Cloudflare
Tunnel) this should comfortably run 50+ concurrent players on a $20/mo VPS.
## Prerequisites
- A Linux VPS (Debian 12 / Ubuntu 22.04 / etc.)
- A domain you control with wildcard DNS pointing at the VPS
(`*.your-server.example.com → 1.2.3.4`)
- Docker + Docker Compose
## Setup
```bash
git clone https://github.com/YOUR_GH_USERNAME/dorknet
cd dorknet/docker
cp .env.example .env
$EDITOR .env # fill in the values noted below
```
### Required `.env` values
```env
# Your domain. Server expects to find itself at *.DORKNET_DOMAIN.
DORKNET_DOMAIN=your-server.example.com
# A long random string. Used to sign JWTs.
JWT_SECRET=$(openssl rand -base64 64)
# Postgres password. Random, written once, kept in this file.
POSTGRES_PASSWORD=$(openssl rand -base64 32)
# Photon Cloud AppIds. Free tier at https://dashboard.photonengine.com
PHOTON_APP_ID=your-photon-realtime-appid
PHOTON_VOICE_APP_ID=your-photon-voice-appid # optional
PHOTON_CLOUD_REGION=eu # eu, us, asia, jp, sa, kr, in, au, etc.
# Steam Web API key — only needed if you want Steam logins to work.
# Free at https://steamcommunity.com/dev/apikey
STEAM_API_KEY=
```
### TLS
The bundled nginx config terminates TLS but expects certificates to exist
on the host. You have two reasonable paths:
1. **Caddy in front** (recommended for new hosts) — replace the nginx
service with a Caddyfile; Caddy auto-fetches Let's Encrypt certs.
2. **Cloudflare Tunnel** — DNS your wildcard at Cloudflare, run
`cloudflared` as another container, never expose port 443 to the
internet. The repo includes a sample `docker-compose.cloudflared.yml`.
### Run it
```bash
docker compose up -d
docker compose logs -f server
```
First boot does:
1. Schema initialization (`EnsureCreated` from the entity classes)
2. Legacy data upgrades (no-op on a fresh install)
3. Seeds RR-Original rooms + store catalog
4. Applies canonical room overrides (Crescendo, Paintball merge, etc.)
5. Downloads the bundled room thumbnail images
You should see `[boot] migrations complete` within ~3 seconds. Then the
server is ready at `https://api.your-server.example.com/healthz`.
## Patching the client to point here
Same patcher as Easy mode, but you'll run it from the command line:
```powershell
.\tools\patch-client.ps1 `
-RecRoomDir "C:\path\to\Recroom_Release" `
-ServerHost "your-server.example.com" `
-PhotonAppId "your-photon-realtime-appid" `
-PhotonRegion "eu"
```
## Admin UI
`https://admin.your-server.example.com` — log in with the first account
you create (it auto-promotes to admin) or with the bootstrap credentials
in your `.env` if you set `BOOTSTRAP_ADMIN_*`.
## Backups
Postgres is the only stateful service.
```bash
docker compose exec postgres pg_dump -U dorknet dorknet > backup.sql
```
`data/images/` on the host is the image blob store. Back that up too.
## Updating
```bash
cd dorknet
git pull
docker compose pull
docker compose up -d
```
The server runs all idempotent migrations on boot, so version upgrades
are zero-downtime as long as the schema changes are additive.
+71
View File
@@ -0,0 +1,71 @@
# Architecture
```
┌──────────────────────────────────────────────────────────────────┐
│ Rec Room 2020.03.10 client (patched) │
│ ┌────────────────────┐ ┌──────────────────────────────────┐ │
│ │ IL2CPP game code │ │ MelonLoader + client-mod │ │
│ │ • set_BaseUri │←───│ • rewrites *.rec.net hosts │ │
│ │ • Photon client │ │ • injects Photon AppId │ │
│ │ • BouncyCastle TLS│ │ • bypasses TLS trust check │ │
│ └─────────┬──────────┘ └──────────────────────────────────┘ │
└────────────┼─────────────────────────────────────────────────────┘
│ HTTPS to *.your-server.example.com
▼
┌──────────────────────────────────────────────────────────────────┐
│ DorkNet server (ASP.NET Core) │
│ │
│ Subdomain-multiplexed routing: │
│ api.* → REST endpoints (rooms, players, store, etc.) │
│ auth.* → OAuth-style /connect/token, /photon/customauth │
│ accounts.* → /account/bulk, /account/me, /account/{id}/bio │
│ match.* → /goto/room/*, /player/heartbeat, /roominstance/*│
│ chat.* → /thread, /thread/{id}/message │
│ cdn.* → static asset serving (room blobs, images) │
│ notify.* → SignalR hub for push notifications │
│ img.* → signed image fetch + on-the-fly resize │
│ leaderboard.* → /leaderboard/SetStat, GetPlayerRank, etc. │
│ admin.* → React SPA + /api/admin/v1/* │
│ │
│ Storage: │
│ • Postgres (advanced mode) / SQLite (easy mode) │
│ • Redis presence cache (advanced) / in-memory (easy) │
│ • data/images/ — room thumbnails + player polaroids │
└──────────────┬───────────────────────────────────────────────────┘
│
│ The server does NOT host Photon. Players' clients
│ connect to Photon Cloud directly via the AppId
│ the server hands them.
▼
┌──────────────────────────────────────────────────────────────────┐
│ Photon Cloud (external — Exit Games) │
│ Each Rec Room instance = one Photon room. Photon handles │
│ realtime sync between clients in the same instance. │
│ Server only touches Photon to authenticate connections via │
│ /photon/customauth. │
└──────────────────────────────────────────────────────────────────┘
```
## Why subdomains?
The 2020 Rec Room client routes every request through a hardcoded
service map (`RecNet.Core.SendRequest(method, Service.Match, …)`) that
picks a host per service. We mirror the official `*.rec.net` layout so
the watch's URI synthesis works unmodified after a single root-host
rewrite by the client mod.
## Why SignalR?
The 2020 client expects WebSocket push notifications from a long-poll-or-
WebSocket "Notifications" service. SignalR's transport negotiation
matches what the client expects after a small DTO-shape wrap; see
`Services/NotificationService.cs`.
## Why a separate Photon Cloud?
The 2020 build is tightly coupled to Photon Realtime SDK 4 — replacing it
would mean shipping a custom game client. Using Photon Cloud as-is means
clients talk peer-to-peer through Photon's relay; our server only
witnesses the auth handshake. That's also why voice chat needs a Photon
Voice AppId — voice runs through Photon Voice (a separate service from
Photon Realtime).
+93
View File
@@ -0,0 +1,93 @@
# Easy mode — host your own server
> Built for non-technical hosts. If you can install Discord, you can do this.
## What you'll need
- A Windows PC (Mac and Linux support coming later)
- A copy of **Rec Room 2020.03.10** — the specific old build DorkNet targets.
You acquire this yourself; see [Getting the 2020 client](#getting-the-2020-client) below.
- ~5 GB free disk space
- About 10 minutes
## Step 1 — Download DorkNet
Grab `dorknet.exe` from the [latest release](https://github.com/YOUR_GH_USERNAME/dorknet/releases/latest).
> Windows SmartScreen may warn you. The exe is code-signed; click "More info"
> → "Run anyway".
## Step 2 — First launch
When you open DorkNet for the first time, you'll see a welcome screen
asking what you want to do.
![first-run welcome screen](images/first-run-welcome.png)
Click **🏠 Host a server**.
## Step 3 — Patch your Rec Room install
DorkNet needs to redirect Rec Room to talk to your server instead of the
official one. This is a one-time patch.
1. Click the **Patch game** tab.
2. Click **Browse for Rec Room install...** — point it at the folder where
`Recroom_Release.exe` lives.
3. Click **Patch**. Takes 30 seconds.
![patch tab](images/patch-tab.png)
✅ You're done with patching. You won't need to do this again unless you
reinstall Rec Room.
## Step 4 — Start the server + tunnel
1. Click **Start server** on the Status tab.
2. After ~5 seconds you'll see a public URL like
`https://abc-def-ghi.trycloudflare.com`. Anyone with that URL + a
matching join code can connect to you.
![status tab running](images/status-running.png)
## Step 5 — Share the join code with your friends
1. Click the **Share** tab.
2. Click **Copy join code**.
3. Paste it to your friend in Discord / iMessage / wherever.
That's it for hosting. Your friend opens DorkNet, picks "Join a server",
pastes the code, and they're in.
---
## Getting the 2020 client
DorkNet doesn't ship the Rec Room game itself — that's Rec Room Inc.'s
intellectual property. You acquire your own copy through legal means.
Common paths:
- If you bought Rec Room on Steam before 2020, you can use SteamDB depot
tools to download the old build (manifest `4651244957411961725` for
`Recroom_Release_Data`).
- Friends who already have a 2020 install can share files with you under
whatever license terms apply on their end.
DorkNet's authors can't help you obtain the game. Search the web for
"Steam depot 2020.03.10 Rec Room" if you need pointers.
---
## Troubleshooting
See [troubleshooting.md](troubleshooting.md) for common issues.
The most common ones:
- **"This site can't be reached" when joining** — your tunnel URL changed.
Get the new one from the Status tab and share it again. (Cloudflare
Quick Tunnels give a new URL each session. See
[advanced-setup.md](advanced-setup.md) if you want a stable one.)
- **Friend can't connect** — they need to use the same Photon region as
you. Settings → Photon region. Default is `eu`.
- **Game launches but stays on the loading screen** — your friend isn't
patched. Make sure they ran the Patch tab in their copy of DorkNet.
+90
View File
@@ -0,0 +1,90 @@
# FAQ
### Is this legal?
The DorkNet code itself is original and AGPL-licensed. The Rec Room game
client is not ours — you supply your own copy that you've legally
acquired. The patcher runs locally on your machine and never uploads
modified game files anywhere.
DorkNet's authors don't ship Rec Room IP and don't help users obtain it.
See [DISCLAIMER.md](../DISCLAIMER.md) for the full position.
### Will Rec Room Inc. shut this down?
Possibly. Fan-server projects exist in a grey zone. AGPL doesn't shield
from DMCA. We minimize friction by never distributing Rec Room IP, never
interfering with their live servers, and being explicit that this is for
educational / private use only.
### Why the 2020 build specifically? Will newer versions work?
2020.03.10 is the last build before Rec Room shipped server-side
ObscuredInt anti-cheat changes that would require deeper client patching.
It's old enough to be effectively abandonware on Rec Room's roadmap, new
enough to have most of the features people remember.
Newer builds (any Rec Room from late 2020 onward) aren't supported and
won't be without significant additional reverse-engineering. PRs that
add support for newer builds are welcome but expect scope creep.
### Can I run a public server that anyone can join?
Technically yes (just disable signup-disabled in admin settings). Legally
risky — see the C&D answer above. Realistically, "public" private
servers attract attention and shorten the project's lifespan.
We recommend keeping servers small (friends + community), private
(invite-only signups), and not advertised.
### Will mobile / Junior accounts work?
Mobile binaries from 2020 are platform-locked to App Store / Play Store
servers and can't easily be retargeted. Quest standalone is possible in
principle but hasn't been validated.
Junior accounts work but DorkNet patches out the chat-permission gate by
default since on a private server with friends, the chat moderation
plumbing is overkill. Toggle in admin → Server settings if you need it.
### Does this work with Quest VR?
Desktop VR via SteamVR works. Standalone Quest builds aren't easily
patchable (sideloaded APK signing is its own world). Quest Link / Air
Link puts you back in the SteamVR case, which works.
### How many players can one server handle?
- **Easy mode (SQLite, single-instance)**: ~30 concurrent players in
practice. Photon does the heavy realtime lifting; the bottleneck is the
server's SignalR fanout + DB writes, both of which SQLite struggles
with past ~50.
- **Advanced mode (Postgres + Redis + multi-replica)**: tested at 200+
in dev. Real bottleneck becomes Photon AppId quotas at that point.
### Can I federate multiple DorkNet servers?
Not supported and not planned. Each server has its own account database;
players are server-local. Same as classic Minecraft realms.
### Is there a Discord?
No official one yet. If you start a community, link it from your fork.
### What about voice chat?
Routes through Photon Voice. You'll need a (free) Photon Voice AppId
separate from your Photon Realtime AppId. Set `PHOTON_VOICE_APP_ID` in
the server config; the client mod handles the rest.
### My friend can't connect
Most common causes, in order:
1. **Tunnel URL stale.** Cloudflare Quick Tunnels rotate the URL on each
restart. Re-share the join code.
2. **Photon region mismatch.** Both clients must use the same Photon
region — set in Settings.
3. **Friend didn't patch.** They need to run the Patch tab pointed at
their own Rec Room install, with your join code.
4. **Junior account / accessibility settings.** Server settings → relax
moderation gates if friends are on accounts with parental controls.
+67
View File
@@ -0,0 +1,67 @@
# Joining someone's DorkNet server
> Built for players whose friend already runs a server and gave them a
> code. If you want to *host* instead, see [easy-setup.md](easy-setup.md).
## What you'll need
- A Windows PC
- A copy of **Rec Room 2020.03.10** (your own — see
[Getting the 2020 client](easy-setup.md#getting-the-2020-client))
- The join code your friend gave you (looks like `dorknet://join?...`)
- About 2 minutes
## Step 1 — Download DorkNet
Grab `dorknet.exe` from the [latest release](https://github.com/YOUR_GH_USERNAME/dorknet/releases/latest).
## Step 2 — First launch
On the welcome screen, click **🎮 Join a server**.
![first-run welcome screen — Join selected](images/first-run-welcome-join.png)
## Step 3 — Paste the join code
Your friend's code goes in the big text box. DorkNet decodes it and shows
you what you're about to connect to so you can sanity-check:
![join code preview](images/join-preview.png)
Make sure the server name matches what your friend told you. Codes from
strangers can point anywhere — only paste codes from people you trust.
## Step 4 — Patch your Rec Room install
1. Click **Browse for Rec Room install...** — point at the folder with
`Recroom_Release.exe`.
2. Click **Patch & launch**.
DorkNet patches the game (one-time), then launches it pointed at your
friend's server. You'll see the normal Rec Room login screen, log in with
the account you made on your friend's server, and you're in.
---
## I already patched for a different server. Can I switch?
Yes — DorkNet's patcher is reversible. Click **Repair / unpatch** in the
Patch tab to undo, then re-patch with a new join code. Or just re-run
**Patch & launch** with the new code; the patcher detects an existing
patch and updates the destination.
## My account doesn't exist on the new server
Each DorkNet server runs its own account database. You'll need to create
a new account the first time you join a server. Your friend can sign you
up via their admin panel, or you can use the in-game "Create account"
flow if the server has signups enabled.
## Voice chat doesn't work
Voice routes through Photon Voice, which requires a separate Photon Voice
AppId. If your friend hasn't set one up, voice chat is text-only for now.
## More troubleshooting
See [troubleshooting.md](troubleshooting.md).
+96
View File
@@ -0,0 +1,96 @@
# Troubleshooting
If your symptom isn't here, check [the FAQ](faq.md) and then open an
issue with: your OS, DorkNet version, server log (Settings → Send logs
to clipboard), and the exact steps that triggered the problem.
## Client / patcher
### "Signature verification failed" on launch
The image-signing public key baked into the patched `global-metadata.dat`
doesn't match the private key on the server. Repair from the Patch tab —
this re-fetches the server's current pubkey before patching.
### "Check your internet connection" loop at startup
The watch can't reach the server URL. Verify:
1. The host string in `dorknet-clientmod.json` matches the server's
public URL (tunnel URL for Easy mode).
2. The server responds to `https://<host>/healthz` with `200 OK` from
the same machine the client runs on.
3. No corporate proxy / DNS filter is intercepting `*.trycloudflare.com`.
### Black screen after the loading bar fills
The watch authenticated, joined Photon, but couldn't download the room
data blob. Look in MelonLoader's `Latest.log` for
`[DownloadRoomDataBlobCoroutine]` lines — the URL there tells you which
asset the server failed to serve.
### Save doesn't persist, MakerPen objects disappear after reload
Known issue with stale `LocalRoomScene.DataBlobName`. The client mod's
`SetRoomDataBlobName_Prefix` hijack should handle this — verify it's
firing in MelonLoader's log. If not, the mod failed to load; check that
`Mods\DorkNet.ClientMod.dll` is present and not blocked by Windows.
## Server
### `[boot] migrations failed`
The schema patch SQL errored. Most likely cause: an existing column whose
type doesn't match what we're trying to add. Look at the exact failure
in the log; if it's a Postgres `cannot alter type` error, the prior
column type needs a manual migration before DorkNet starts.
### Photon CustomAuth returns 401
Either:
- The Photon AppId in `appsettings` doesn't match the AppId baked into
the patched client. They MUST match.
- The user logging in doesn't have a row in the `Players` table — the
auth flow expects `userid` to be a real account.
### Room chat doesn't send
Run the client with `SaveDebugMod` enabled (or watch MelonLoader's log)
and look for the `[chat-trace]` lines. The pipeline is:
```
SendChatEmote → RemoveInvalidCharactersFromMessage → PurifyString HTTP
→ ProcessNewChatMessageReceived → Photon RPC
```
If any trace line is missing, that's where it died. The two most common
fixes:
- `[chat-fix] CanLocalPlayerChat returned false`: the account is flagged
as a Junior with chat disabled. Force-true via the client mod (default
behavior) or fix it server-side in admin.
- `Received malformed RecNet response`: the `/api/sanitize/v1` endpoint
returned the wrong shape. Should be a bare JSON-quoted string. See
`SanitizeController.cs`.
## Easy app
### "Cloudflared exited unexpectedly"
The bundled `cloudflared.exe` couldn't start. Most likely a Windows
Defender false-positive — check Defender's quarantine. If you find a
cleaner alternative tunnel provider, open an issue; we've been looking
for one.
### Patch button is greyed out
DorkNet didn't find a Rec Room install at the path you selected. Make
sure you pointed at the *folder* containing `Recroom_Release.exe`, not
the exe itself, and that `Recroom_Release_Data/` exists alongside.
### Mode switch lost my server data
Switching from Host mode → Join mode preserves the SQLite database; it
just stops auto-starting the server. Switch back to Host mode and your
players / rooms / images are still there.
If the data really is gone, check `%APPDATA%\DorkNet\dorknet.db` — that
file is the entire host-side state, easy to copy elsewhere as a backup.