# Deploying Tachyon voice with Campus Tachyon is a standalone C++ UDP voice server that runs **alongside** the Campus C# server on the Linux VPS. Voice is proximity/room-scoped: Tachyon relays Dissonance voice and asks the C# server which room each speaker is in. **No client patch is needed** (plaintext `0x11` path). The C# server changes are already in the repo (see "What the C# side does" below) and are **config-gated OFF by default** — until you set `TachyonVoiceEndpoint`, clients keep using the existing Photon Voice P2P path, so nothing about voice changes until you flip it on. --- ## 1. Build Tachyon on the VPS Needs: `git`, CMake ≥ 3.16, a C++23 compiler (gcc 13+/clang 16+), `libcurl-devel`, `openssl-devel`. (Debian/Ubuntu: `libcurl4-openssl-dev libssl-dev`.) ```sh sudo mkdir -p /opt/tachyon && sudo chown "$USER" /opt/tachyon git clone /opt/tachyon cd /opt/tachyon git submodule update --init --recursive # LiteNetLibPP is a pinned submodule cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build -j ``` The configure step applies `patches/0001-tachyon-packet-layer-hook.patch` to the submodule, so `vendor/LiteNetLibPP` shows as "dirty" afterward — **that is expected**. Smoke-test the binary offline (no network): ```sh ./build/tachyon --selftest # exit 0 = pass ``` ## 2. Config `tachyon.conf` (repo root) is ready to use. Key line: ``` room_url = https://camprec.net/tachyon ``` Tachyon calls `GET ?id=` and expects a bare `roomInstanceId`. If Tachyon and the C# server are on the same box and you know Kestrel's local port, you can change this to `http://127.0.0.1:/tachyon` to skip the public round-trip. Copy the file to `/opt/tachyon` if you cloned elsewhere. ## 3. Firewall — open UDP 7777 Clients connect over **UDP**, not TCP. Whatever the C# server's HTTPS uses, voice needs 7777/udp open to the internet: ```sh sudo ufw allow 7777/udp # or the cloud provider's security-group equivalent ``` If the VPS is behind a NAT/provider firewall, forward/allow 7777/udp to this host too. ## 4. Run as a service ```sh sudo cp deploy/tachyon.service /etc/systemd/system/tachyon.service # edit paths in the unit if you did not use /opt/tachyon sudo systemctl daemon-reload sudo systemctl enable --now tachyon journalctl -u tachyon -f # watch it; -v in the conf for per-relay detail ``` ## 5. Flip voice over to Tachyon (C# side) In `ConfigCampus.cs` set the endpoint the CLIENT will dial (public IP or DDNS host + Tachyon's port — **not** 127.0.0.1, the client is remote): ```csharp public static string TachyonVoiceEndpoint = "camprec.net:7777"; ``` Rebuild + redeploy the C# server. From then on `/Matchmaking/player/connection-info` hands the client `voiceConnectionInfo = "camprec.net:7777"` and a non-empty `voiceServerId`, so it routes voice through Tachyon. To roll back to Photon P2P instantly, set it back to `""`. ## 6. Verify - `journalctl -u tachyon -f` shows a handshake and `account=… senderId=…` relay lines when two players are in the same room and talking. - Two clients in the **same** room hear each other; two in **different** rooms do not (room scoping via `/tachyon`). A lookup failure isolates a client (fail-safe: they hear no one). - `curl "https://camprec.net/tachyon?id="` returns that player's numeric room instance id (or `0` if they're nowhere). --- ## What the C# side does (already committed) | File | Change | |------|--------| | `Controllers/TachyonController.cs` | **New.** `GET /tachyon?id=` → bare `roomInstanceId` (text/plain), via `HeartbeatDB.PeekPresence`. `0` when unknown/offline (fail-safe). | | `Controllers/MatchmakingController.cs` | `player/connection-info` now sets `voiceConnectionInfo`/`voiceServerId` from config (empty ⇒ Photon fallback). | | `api/server/ConfigCampus.cs` | `TachyonVoiceEndpoint` (default `""`) + `TachyonVoiceServerId`. | | `Middleware/PatchGateMiddleware.cs` | `/tachyon` added to the no-patch-header open list (server-to-server). | ## Notes / caveats - Plaintext voice only (`0x11`); no `private_key` is set and none is needed. The encrypted `0x2A` path would need `KT` minting + the transport cipher confirmed first — out of scope. - The room lookup is synchronous on Tachyon's handshake path (blocks up to `room_timeout_ms`). Fine for normal joins; revisit only if join storms matter. - `voiceServerId` is just a stable client-side label — Tachyon's protocol has no room/server id on the wire; room scoping is entirely via the `/tachyon` lookup.