Files
DorkNet/launcher/README.md
T
Alexa df9dac053d launcher: v0.1 — PhotinoNET shell + multi-version orchestration
Single-download .NET 9 self-contained launcher for hosting / joining
DorkNet servers. Reads versions.json from main to discover supported
client builds, fetches the matching server binary + client patcher
from GitHub Releases per branch, manages the server subprocess +
Cloudflare quick-tunnel, and wraps it all in a PhotinoNET window.

Code layout:
- Program.cs           PhotinoNET window + message bridge bootstrap
- Backend/             AppPaths, AppState, VersionsManifest,
                       ReleaseDownloader, ServerProcess, ClientPatcher,
                       Tunnel, RecRoomPicker, JoinCode, MessageBridge
- ui/                  index.html + style.css + app.js (no build step)
- RELEASES.md          GitHub Releases artifact contract per-version
                       branches must publish to

Per-version branches need to ship two release assets per tag:
- dorknet-server-{version_key}-win-x64.zip
- dorknet-clientpatch-{version_key}.zip
(see launcher/RELEASES.md for the full naming + arg contract)

Photon AppIds, JWT secret, and the picked Rec Room path stay
per-machine; the launcher never bundles them.
2026-05-26 20:54:16 +03:00

3.4 KiB

DorkNet Launcher

Single-download desktop launcher for self-hosting / joining DorkNet servers. PhotinoNET-wrapped WebView2 with the orchestration logic in C#.

This README is for developers of the launcher. End-users see the main README and download a prebuilt .exe from Releases.

What it does

  • Host mode: fetches the matching server binary from GitHub Releases for the user's picked Rec Room version, runs it as a subprocess, opens a Cloudflare quick-tunnel, patches the user's Rec Room install, hands the user a shareable join code.
  • Join mode: parses the join code, fetches the matching client patcher from GitHub Releases, patches the user's Rec Room install to point at the host.

How it's wired

launcher/
├── DorkNet.Launcher.csproj    .NET 9 single-file self-contained exe (Photino.NET dep)
├── Program.cs                  PhotinoNET window + message-bridge wiring
├── app.manifest                Windows DPI-awareness manifest
├── Backend/                    Orchestration (C#)
│   ├── AppPaths.cs            %APPDATA%\DorkNet layout
│   ├── AppState.cs            Persisted user choices (mode, paths, Photon AppId)
│   ├── VersionsManifest.cs    Fetches versions.json from main
│   ├── ReleaseDownloader.cs   GitHub Releases API + artifact unpack
│   ├── ServerProcess.cs       Spawns the downloaded server binary
│   ├── ClientPatcher.cs       Invokes install-plugin.ps1 from the unpacked patcher zip
│   ├── Tunnel.cs              Wraps `cloudflared tunnel --url …`
│   ├── RecRoomPicker.cs       Windows folder dialog
│   ├── JoinCode.cs            base64url-encoded {host, version, photonAppId, …}
│   └── MessageBridge.cs       JSON envelope router between JS ↔ C#
└── ui/                         Single-file frontend (no build step)
    ├── index.html
    ├── style.css
    └── app.js

Message protocol: both directions use { "type": "command-or-event-name", "payload": { ... } }. New commands go in MessageBridge.HandleAsync's switch; new outbound events use bridge.SendEvent(name, payload).

Build + run

cd launcher
dotnet run

Watch the launcher window open. C# stderr lands in the console; the WebView2 devtools open with F12 (PhotinoNET passes them through).

Publish a self-contained .exe

dotnet publish -c Release
# Output: bin\Release\net9.0-windows\win-x64\publish\dorknet.exe (~80 MB)

The published exe is what ships in GitHub Releases on main (use the Easy launcher release workflow when it exists).

Dependencies on per-version branches

The launcher fetches release artifacts from GitHub Releases tagged on the per-version branches. See RELEASES.md for the exact naming convention each branch must follow.

Adding a new command

  1. Add a new case in MessageBridge.HandleAsync.
  2. Implement the work in a Backend/* class.
  3. In ui/app.js, call bridge.send({ type: 'your-command', payload: {...} }) from wherever the user triggers it.
  4. If the command needs to push events back, call bridge.SendEvent('your-event-name', { ... }) from the handler and add a case in app.js's handleEvent switch.

Keep the bridge handlers tight — they're routing + state mutation; long work goes in Backend/*.