Dark Tactics

Run a relay

Version 1.0.3 · 11 KB · Node 18+

On the same Wi-Fi you do not need any of this — the two phones find each other. A relay is for two people on different networks, or on a router that refuses to let its own devices see each other.

Why anything is in the middle at all

React Native ships a WebSocket client and nothing that listens, so for two phones to talk, one of them would have to be a server — and neither can be. Put something in the middle and both are clients, which is why playing over the internet turned out to be the cheaper path in this game rather than the dearer one.

It is stateless, so one is enough for everybody

A room is two live sockets and a display name, and it is gone when they hang up. Nothing is written to disk, nothing is read from a database, and no game message is ever parsed — the relay is a pipe. So this is not a server per game: one instance serves every player at once, and the smallest free tier anywhere is already more machine than it needs.

Download relay.js11 KB · one file · no dependencies

Three ways to run one

Numbered by how many people each one reaches, not by difficulty.

  1. Deploy it once, and nobody types an address again

    Reaches: everybody, forever

    Any host that runs a container will do. The image is the Node base plus 11 KB, there is nothing to install, and Dockerfile, render.yaml and fly.toml come with the packaged version.

    docker build -t dtbattle-relay . docker run -p 8787:8787 dtbattle-relay

    A host that terminates TLS gives you a wss:// address, which the game takes as-is — paste it into the Relay field and leave the port off. Give that one address to everyone who plays and multiplayer is done.

  2. Run it on a laptop the two players can reach

    Reaches: the people in the room with it

    Download the file above and run it. It prints every address it can be reached on, and on your own Wi-Fi it also shouts a beacon the game picks up — so on a LAN there is nothing to type at all: the relay appears in the lobby by name, for both players.

    node relay.js node relay.js 9000 # a different port

    Or install it as a command, from the tarball the game repo’s packaging script writes:

    npm install -g ./dtbattle-relay-1.0.3.tgz dtbattle-relay
  3. The machine has no Node on it

    Reaches: one stubborn machine

    The game repo’s tools/build_relay_dist.py --sea bundles the script into a self-contained executable you can copy anywhere. It is about 110 MB for an 11 KB script, because the whole Node runtime rides along — which is why it is the last option rather than the first. Installing Node is the better trade nearly every time.

Using it in the game

Both players open Play together, pick Relay, and use the same address. Then either share a room code or leave it blank: a host with a blank code is given one, and a guest with a blank code gets the list of open rooms on that relay to tap — the same way the same-Wi-Fi lobby lists games.

What it does with what passes through it

It forwards bytes between two sockets and keeps nothing. There is no record of moves, no database, no account and no analytics — see the privacy policy for the rest. Run your own and the messages pass through your machine and nobody else’s.

Checking one is alive

Two plain HTTP endpoints, so answering “is it up” never needs a WebSocket client:

GET / -> dtbattle relay rooms: 2 GET /rooms -> [{"code":"KEEP","name":"Rowan","mode":"versus"}]

/rooms lists only rooms with a host and no guest — the ones a second player can actually join. The name and mode beside each are a label the hosting phone supplies in its own connect URL; the relay still never reads a game message.