Dedicated server¶
With a dedicated server nobody has to host: the game runs on a machine on the internet, with no window and no player, and everyone joins it, phones included. A small rented Linux server (a VPS) is enough. It needs no display and no graphics card.
The server is a program of its own, redgem-server. One server holds several rooms. Each room
is a match of its own, with the whole game running in it. Players join by the server's address and
get the first room with a free place, or a new room when every room is full. A friend can follow
them into the same room with its code (Network.room). A room closes when its match ends: the game
calls Game.quit, or everyone left. The next players get a new room, with a new game.
Nobody runs a server for you: each game runs its own, on a machine its players can reach, as below. The same goes for a relay.
Run it¶
./build/src/redgem-server games/platformer # listens on port 7777
./build/src/redgem-server games/platformer --port 9000 --max-players 4 --rooms 16 --data /srv/platformer
| Option | Default | What it is |
|---|---|---|
--port |
7777 | The TCP port players connect to (0 picks a free one) |
--max-players |
8 | Players in one room, up to 15 |
--rooms |
8 | Rooms open at once, up to 64 |
--threads |
The machine's cores | Threads the rooms run on, up to 64: each room has one of its own while there are no more rooms than threads |
--per-address |
24 | Machines one address may connect, up to 1024: raise it when many players share one address (a campus, some mobile networks) |
--data |
<game>-data beside the game's folder |
Where the game's Save is kept (save.sqlite3), for every room and every run |
It writes what happens to its output (rooms opened and closed, how many players) and stops
cleanly on Ctrl+C or SIGTERM: every room is closed and its players are told.
A room is light (a platformer room takes 1 to 2 MB of memory and a few percent of one core), but
every room runs the whole game, so --rooms is what sizes the machine. Each room runs on a thread
of its own, so the rooms use all the machine's cores, and a room loading a level (when its first
player arrives, or when its game loads another) holds up no other room. Players connect and are
placed in rooms on the server's main thread meanwhile. With more rooms open than --threads, the
rooms share the threads there are: a level loading in one then holds up the others on its thread
for that moment.
Run it with Docker¶
Dockerfile.server builds an image holding the server and one game, nothing else:
docker build -f Dockerfile.server --build-arg GAME=games/platformer -t my-game-server .
docker run -d --restart unless-stopped -p 7777:7777 -v my-game-data:/data my-game-server --rooms 4
GAME is the game's folder, relative to the repository. docker compose up server does the same
for the platformer (GAME=path/to/game docker compose up server for another). The container runs
as an unprivileged user and writes only to /data, where the game's saves are kept (a volume, so
that they outlive the container).
Put it on the internet¶
- Rent any small Linux server with a public address, and install Docker on it (or build RedGem there, see Install).
- Copy the repository and your game there, then build and run the image as above.
- Open the port: allow TCP 7777 in the server's firewall and in the provider's control panel.
- Point the game at it. In the platformer demo, set
SERVERinscripts/main_scene.rb; in a game made from the platformer template, setSERVERinscripts/online.rb:
SERVER = "play.example.com".freeze # or "203.0.113.7", or "play.example.com:9000"
The game's menus then offer Play online. In your own game, it is one line:
Network.join("play.example.com") # any room with a free place
Network.join("play.example.com", room: "K7QM3P") # the room a friend is in
Make the game work on a server¶
The server runs the same scripts as the players, with Network.server? true. It has no player of
its own, so whatever a script does for "my player" is skipped there:
Network.on_connect do
spawn_my_hero unless Network.server? # the server plays nobody
Network.players.each { |peer| show_hero_of(peer) } # the others who play, never the server
end
Network.host? is true on the server: it hosts the game and owns the level's own objects, so
what the host decides (scores, a door opening, the end of a match) is decided there. Menus never
open on a server. Call Game.quit on the server when a match is over to give its room to the next
players.
Save on the server is the server's own: a SQLite database in its data folder (--data),
shared by all its rooms and kept across restarts. Each key is a row of its own, written as soon as
it changes, in a transaction (a crash keeps every change made before it, never half of one), and
only what a script asks for is read into memory. Rooms write one at a time, and change a value
that several rooms may change with Save.update, which reads and writes it with no other room's
save in between. Arrays and hashes
saved there may hold any number of things (only their nesting is limited, to 16 levels). Keep
each player's data under their identity, as
Online services shows; with many players,
a key per player (Save["player:#{identity}"]) reads and writes one player instead of all of
them.
A copy of the database for a backup is taken with sqlite3 save.sqlite3 ".backup copy.sqlite3",
or by copying the folder while the server is stopped.
The server faces the internet. It keeps every limit a hosted game and the relay have (message sizes, how much each player may send, connections per address, spawned objects), lets go of machines that stay silent and of players who say nothing to the game for five minutes (a game left paused counts), and keeps out addresses that guess at room codes. Messages are not encrypted. Players are let in without asking unless the game decides who plays: see Online services.