## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# Introduction

Here’s the documentation for Spaceships v1.5.0. Right now this documentation is pretty incomplete.

If you have any questions, ask them on Lemmy or Matrix, whichever you prefer. More information about Lemmy and Matrix is available here:

- Lemmy: https://join-lemmy.org/
- Matrix: https://matrix.org/

## Screenshots

Here are some screenshots of the game:

## Links

- Website: https://spaceships.me
- Download: https://spaceships.me/download
- Documentation: https://spaceships.me/docs
- Source code: https://codeberg.org/rustydev/spaceships
- Lemmy: https://lemmy.spaceships.me
  - Development blog: https://lemmy.spaceships.me/c/blog
  - Help and support: https://lemmy.spaceships.me/c/help
- Matrix: #spaceships:matrix.spaceships.me
  - Chat: #chat:matrix.spaceships.me
  - Help and support: #help:matrix.spaceships.me
- PeerTube: https://peertube.spaceships.me

# How to play

There are two ways of playing Spaceships: singleplayer and multiplayer.

Multiplayer has existed since the game’s creation while a functional singleplayer has only very recently been implemented, being new to v1.5.0. This page will discuss gameplay that is common to both singleplayer and multiplayer.

## Playing the game

Gameplay in Spaceships involves the player controlling a spaceship that they move around and kill other spaceships by firing bullets at them to diminish their health and eventually destroy them.

### Controls

To move around, use WASD or the arrow keys. To change the direction you’re looking in and where bullets will be fired from, move the mouse. Bullets are shot by pressing the left mouse button or spacebar. You can hold down these buttons to easily fire multiple bullets at a time. These controls can be changed by pressing the “Settings” button while in the title screen.

You can zoom the camera in and out by scrolling (or by pressing ‘J’ and ‘K’), and you can reset to the default zoom by pressing the middle mouse button.

If you’re playing a game that has flags and you have captured one, you can drop it by pressing ‘Q’.

### Health

Your health is displayed in the health bar at the bottom of your screen, with the number of bullets also displayed above that. The health bars of other players are displayed right above them. After getting shot, players slowly regenerate their health.

### Ammo

You have a finite amount of bullets (unless the server is configured to provide you infinite), which are reset when you die. Periodically during the game, ammo crates are spawned which supply the player collecting them with a random amount of bullets.

### Powerups

Additionally, powerups are periodically spawned which give the player collecting them a temporary effect. These effects either last for 30 seconds or happen immediately (teleportation) and are listed as follows:

1. Speed. This is the green powerup which increases your movement speed.
2. Reload. This is the yellow powerup which increases your reload speed, making you shoot faster.
3. Health. This is the pink powerup which increases your health and makes you regenerate health faster. The increase of health that this powerup provides can increase your health above the usual limit and make your health bar blue.
4. Damage. This is the red powerup which increases the damage of your bullets.
5. Forcefield. This is the blue powerup which provides a forcefield that deflects bullets fired by other players.
6. Teleportation. This is the purple powerup which teleports you to a random place on the map. Unlike the others, this can be disadvantageous depending where you teleport…

Here are these six powerups:

## Exiting a game

To exit the game, press escape.

## Player customisation

The name and colour of the player can be changed by going into the settings.

# Singleplayer

Right now singleplayer exists in the form of a story mode, consisting of five levels of increasing difficulty.

The story in this game is that you are an insurgent fighter in a military operation to recover the private key and liberate all consumer computing devices by unlocking their bootloaders and allowing installation of a custom freedom-respecting operating system. At the beginning of each level you will be presented a message from your commander about the current status of the operation.

## Playing the levels

To play a level, first click the “Play” button on the title screen and then click “Story Mode” under “Singleplayer”. Then click any one of the five level buttons to play a level.

By default, you need to complete the levels in order and so levels that you cannot play because you haven’t completed the previous level are greyed out. If you want to disable this feature and play any level you want to, enable the “Unlock All Levels” checkbox.

There also is a button available to reset your progress.

## Completing a level

To complete a level you need to navigate the map and kill all bot players. While playing the level you have a finite number of lives, which you lose when you get killed. If you lose all your lives you lose the level and need to restart.

## Checkpoints

A feature that exists in levels but doesn’t exist in multiplayer servers is checkpoints. When you reach a checkpoint, you will respawn at that location whenever you get killed.

# Multiplayer

## Joining a game

To play a multiplayer game, first click the “Play” button on the title screen. The next step depends on if the server is hosted on the internet or on the local network.

If the game server is available online, click “Online” and then “Search” to view a list of public online servers. Then click “Play” on the particular server you want to join.

If the game server is hosted on the local network, click “Local” and then click “Search LAN” and if everything works well, the game server will pop up and you can then click “Play”.

Sometimes you might need to manually enter the address. This can be if the server is hosted on the local network and the above instructions don’t work for some reason (maybe a host or network firewall), or if the server’s online but not publicly visible. In that case, click “Enter Address” and enter the IP address or domain name in the “Host” section, and then click “Play”. The default settings for “Port” and “Game ID” don’t need to be changed unless the server administrator mentions setting them to a particular value.

## Teams

Some Spaceships servers can have teams enabled. When you join the game, you will be assigned to a specific team. To change teams, first list the available teams by running the command (keep reading for information about commands) `teams`. This should return a list of teams on the server. Then run `team NAME` where `NAME` is the name of the team you want to change to.

When in a team, your bullets cannot hit other members of your team (unless the server has friendly fire enabled).

## Winning the game

The way the game is actually won, if there is a way, is highly customisable. On the top-left corner of the screen is a list of scores that the server keeps track of. These scores can increase or decrease when certain events happen, such as when a player gets a kill or kills someone else.

Here is a list of a few examples of win conditions that are possible:

- First to N kills.
- Most kills within a certain amount of time.
- Last player remaining where players are eliminated if they lose too many lives.

## Chat

Spaceships has two types of in-game chat that you can use to talk to other players. The first type is team chat which allows you to talk to your team members. To open team chat, press ‘T’ (you can bind this to something different), start typing and press enter to send your message. Only other players on your team will receive your message, unless teams are disabled in which all players will receive the message.

The second type of chat is global chat which lets you talk to all players. Opening global chat is done by pressing ‘G’.

## Commands

Spaceships also supports sending commands to the server using the chat window. To send a command, first press ‘/’ (again, can be rebinded), type your command and then press enter to send it. Run the `help` command to get an exact list of commands the server supports.

Note that opening chat and typing a message beginning with ‘/’ won’t execute a command and instead send a regular message.

### Underpowering

If you’re an experienced player playing with a friend who is new to the game, you can choose to make yourself weaker to make the game fairer. This is called underpowering and is done by executing commands.

For an example, to decrease your speed to 75% of what it usually is, run `underpower speed 75%`. Note that you can use 0.75 instead of 75%, and the alias `up` can be used. Another example is to completely disable regeneration, you can run `up regen 0`.

The general syntax for this specific subcommand is `underpower/up EFFECT VALUE`, where `EFFECT` is `speed`, `reload`, `regen` (or `regeneration`) and `damage`, and value is a number or a percentage.

## Security

If you’re playing this game online, you might be concerned about whether encryption is used, especially when it comes to chat messages which might be sensitive. Spaceships uses QUIC, a transport-layer protocol over UDP that always uses encryption. However, that doesn’t mean that all communications are secure.

First note that the security I’m referring to here is about whether a malicious device on the path between the client and the server can eavesdrop communications. All chat messages are logged for the server owner to read, so if you don’t trust the server owner, don’t send any sensitive chat messages.

While playing the game, the connection’s security is displayed in the bottom-left corner when the chat window is open. This information is one of the three security levels, “Secure”, “Local” and “Insecure”.

### Secure

If a connection is secure, as indicated by the green text saying “Secure connection”, it is very unlikely that communication is being eavesdropped.

This level of security is reached when the server is hosted on the same device, or a trusted TLS certificate is used. This level of security is the same as HTTPS on the web.

### Local

If a connection is local, as indicated by the blue text saying “Local connection”, it means that the connection isn’t secure, but can only be eavesdropped by an attacker on the local network. If you’re connecting to a server on the local network and you trust that network, communications are secure.

### Insecure

If a connection is insecure, as indicated by the red text saying “INSECURE connection!”, it means that the connection isn’t secure and can be eavesdropped by an attacker on the internet that controls a node on the path. This attacker can be an ISP or a government agency.

For most users, it is unlikely that there actually is an attacker listening into communications, but it is still possible. If you live in an authoritarian regime such as Russia, Saudi Arabia, Myanmar, the United States, Israel, China, Iran or the United Kingdom, you shouldn’t send any chat messages that the government would disapprove of if using an insecure connection. It’s better to be safe than sorry.

### Note on eavesdropping

I mentioned earlier that encryption is always enabled in Spaceships. That is true but it is still possible to eavesdrop by performing a man-in-the-middle attack in which an attacker impersonates the server and can decrypt communications.

This encryption is still better than nothing as it fully prevents against passive eavesdropping attacks, where the attacker can only listen in but can’t modify the messages.

## Spectator mode

If you want to watch a game play out but not be an active player, you can join as a spectator. To do this, click the checkbox before starting the game or execute the `spectate` command while playing the game.

While in spectator mode, you can move the camera around using WASD or the arrow keys and you can also drag the camera using the mouse. You can also press ‘F’ to toggle between freely moving around and following another player in the world.

Some game modes can eliminate the player before the game ends, in which those players will be forcefully put into spectator mode.

# Settings

In the title screen, you can click “Settings” to change some settings related to input and graphics.

## Player

Here you can change your name and colour.

## Input

The second part of the settings screen provides a way to change the keyboard and mouse button bindings to certain actions. These actions are organised into many categories available as headers, such as “UI” and “Player”. Click on these to open up a list of buttons for each action in that category.

You can then hover over a button to get a list of inputs bound to that action. For example, hovering over the “Open Chat” button shows “Bound to: Key T”.

To change these bindings, first click on one of these buttons after which a window will pop up showing the bound inputs and three buttons. To add a binding, first click “Select” and then press any key or click any mouse button. Now you can click “Bind” to bind the recently performed input to that action. You can do the same for unbinding.

## Graphics

The graphics part of the settings screen allows you to change the decorative sky background, which particle effects are enabled and more.

## Sound

Here you can change the volume of the game’s sound effects.

## Online Servers

When you browse the list of online servers, a request is made to some server that maintains this list. By default this server is `game.spaceships.me`, but you can change this.

## Accessibility

Here you can make the text larger by increasing the UI scaling factor, and choose to have labels above powerups which can help for colourblindness.

# Hosting a game server

There are two ways of hosting a server, using the GUI and with the command line.

The GUI for creating a server provides only the basic functionality for creating game servers playable on the local network. For more advanced functionality like having the server accessible from the internet, you need to use the command line.

## Configurations

To get started you first need to know the basics of game server configurations. A server configuration, or config specifies many different aspects to how a game is played, including but not limited to the block map, the rate at which items (powerups and ammo crates) spawn, the scoring system and how you win the game. Spaceships by default includes many different configurations in the `configs/` directory, each of which focus on a particular feature.

# GUI

To get started, from the title screen click the “Play” button and then under the “Create” subsection, click the “Local” button.

Now select your desired configuration from the drop-down menu and click the “Create” button that pops up. These configs are listed in the `configs/` directory for the standalone distribution, or for Flatpak in both `/app/config/` and `$XDG_DATA_HOME/spaceships/configs` (`~/.var/app/me.spaceships.spaceships/data/spaceships/configs/` outside of the sandbox), with the latter directory being writable. If you add or change a config, click “Refresh Servers” to reload this list of servers.

Now if everything goes well, the server has been created. You can join the server by clicking “Play”1 and stop the server by clicking “Stop Server”.

Instructions are also included for how players can join the game by manually entering the address, if searching the local network doesn’t work for some reason.

1. You can also access it by searching the local network. ↩

# Command line

To get started, simply run `./spaceships server configs/survival.ron`.1 This starts a game of survival, where each player has five lives and the winner is the last player alive. In this command, `configs/survival.ron` is the path to the game’s configuration. If you’re using Flatpak, the default configuration files are instead located in `/app/configs/` inside the sandbox.

The server program outputs logs to the terminal (in particular, stderr), which include information about players joining and leaving the game. If you want to save these logs and if your server is publicly accessible, I strongly recommend not directly writing to a file (`spaceships server ... 2> out.log`). Doing so can expose you to a certain type of denial-of-service attack where an attacker might be able to completely fill up your storage with logs. I created a tool that allows you to save logs in a way that mitigates this attack.

The server also provides a simple REPL interface which allows more advanced configuration.

## Online server

By default the server can only be joined from the local network. If you want your server to be available online, you need to perform a few more steps.

First you need to configure the Spaceships server to accept connections from the internet, which by default are prohibited. To do this, add `-P global` to the command line arguments which changes the privacy settings.

The next steps after this are to allow incoming connections from the internet to actually reach the server. What you will need to do isn’t unique to Spaceships, these steps are necessary if you want to have any server accessible on the internet, whether that is another game server or a website. Knowing this, other guides for hosting servers such as this guide for Luanti will also work for Spaceships.

## Allowing internet connections

In some cases, you won’t need to do anything!2

One option is to have the server hosted on your machine or another machine in your home. To do this, you need to set up port forwarding on your router (and possibly update any firewall configuration), which is mapping a port on the public IP address seen by the internet to a port on a private IP address of the machine you’re hosting the server. Spaceships uses UDP port 4080 by default, so make sure the port on the private IP address also is 4080. The port associated with the public IP address can be anything, but 4080 is recommended (as this port will already be filled in when connecting by manually entering the address).

Another option is to use a VPS, which you might need to use if you’re unlucky enough to be behind a CGNAT.

### Connecting to the server

To test if the server is available online, you need to try connecting to it with the public IP address. If you’re using port forwarding, the public IP address can be found by searching “what is my ip” into your preferred search engine and clicking one of the results. If you’re using a VPS, you should already know this.

Joining the game over the internet is done by clicking the “Enter Address” button in the playing screen and manually entering this public IP address (as well as the port if the default of 4080 isn’t used). If you don’t want players to manually enter an address, keep reading.

If you’re using port forwarding, the public IP address might be dynamic and so would change over time. This would require users to periodically change the entered IP address, or dynamic DNS can be used with users instead entering a hostname.

### Using a TLS certificate

If you’re running an online server with an associated domain name, if users want to connect to the server by entering the domain name you will need a valid TLS (also called SSL) certificate. To run the server to use this certificate, run `./spaceships ... --cert-path PATH --key-path PATH`, replacing `PATH` with the paths of the certificate and private key in PEM format.

Updating the certificate without restarting the server can be done with the `cert` command in the server console. See `spaceships-cert-status` for a tool that can be used by a script to create reminders before the certificate expires.

Automatic updating of certificates isn’t yet supported, though if you really wanted to, you could automatically write the `cert` command into the server’s standard input to execute the command.

### Private servers

Once you’ve created an online server, anyone on the internet can connect to it. If you want to limit this to only those with a password, see these instructions.

## Privacy

Spaceships allows restricting which clients can discover and join each game server. These privacy settings are called discoverability and joinability.

Each privacy setting can be set to a privacy level, which can be `none`, `loopback`, `local` or `global` in order from most to least restrictive. By default both discoverability and joinability are set to `local`, which blocks connections from outside the local network.

### Privacy levels

Here’s an explanation on each of the four privacy levels for whether clients are allowed to discover/join the server:

- `none`: No clients are allowed.
- `loopback`: Only clients on the same host are allowed.
- `local`: Only clients on the local network are allowed.
- `global`: All clients are allowed. Note that this doesn’t automatically allow players on the internet to connect to the game, see the above instructions to do this.

### Setting the config

To set the default privacy config for game servers, add `--privacy` or `-P` with the given privacy config. This is either a single privacy level (to configure both to the same level) or multiple in the form of discoverability/joinability. Here are two examples:

- `-P global` disables all restrictions for players joining and discovering the server.
- `-P none/local` prevents all clients from discovering servers and limits clients on the local network to joining a server.

Note that the joinability cannot be more restrictive than the discoverability. This prevents players from seeing a game when browsing online or local servers but not being able to join it.

# Publishing a game server

If you want your game server to be available online and included on the public list of online servers, you need to publish it to a publication server. This is done by adding `--publish ADDRESS` to the command line arguments, replacing “ADDRESS” with `game.spaceships.me` (the default public server list) or the address of another publication server. To change the port of this publication server, add `--publish-port PORT` to the command line arguments.

If you’re using a TLS certificate, it is highly recommended to also add `--server-name NAME` to the command line arguments so that players connect with the given hostname and so can obtain a secure connection.

When publishing, all game servers that are discoverable by the publication server are published, meaning having global discovery (or less if the discovery server is on the local network). If servers get added or removed using the server console, this information gets automatically published to the publication server.

If the connection with the publication server is ever lost, the publishing server periodically attempts to reconnect to it.

# Hosting a publication server

If you want to host a server that accepts these publications from other servers, you can do this by simply adding `--accept-publications` to the command line arguments.

1. On Windows, you would run `./spaceships.exe server ...`. ↩

2. This can happen if you’re on a network that doesn’t have any NAT or firewall in the way. ↩

# Requesting server info

This game is licensed under the GNU AGPLv3, which requires that any user interacting with the game over the network can get a copy of the exact version of the game running on the server. If you want to know the exact version of a server and if it’s a fork, run `./spaceships info HOST`, where `HOST` is the domain name or IP address of the server.

## Client information

The information about your version of Spaceships can be obtained by running `./spaceships info` or clicking the “About” button in the title screen.

# Server configuration

The long-term goal of Spaceships is to support configuration of everything in the game, including the physics, weapons, and other objects, similar to how games like Luanti currently are.

Right now Spaceships isn’t there yet, but game servers are pretty configurable.

If you find any errors in this documentation, which are likely if I forget to update this in future versions, make an issue here.

## Get started

The documentation here is incomplete. I recommend first taking a look at files in the `configs/` directory attached with your distribution of the game, or at https://codeberg.org/rustydev/spaceships/src/branch/main/configs for examples of what a config file looks like. These can be used as a template for creating your own server config.

These configuration files are in the RON (Rusty Object Notation) format. See the link for more information.

The complete schema of the config files is available inside `src/net/server/config.rs`, with some (but not enough) code comments explaining things.

## Example

Here’s an example of a very simple configuration file that’s a large world with no blocks in it. This is available at `configs/empty.ron`.

    (
    	world: (
    		size: (inf, inf),
    	),
    	discovery: (
    		name: "Empty",
    		desc: "An empty map.",
    	),
    	blocks: (
    		block_size: 1,
    		grid_size: (0, 0),
    		ops: [ Constant(0) ],
    	),
    )

Now here’s an explanation of each of these fields:

- `world.size`: This specifies the width and height of the world. Players cannot go outside this space. In this example, both the width and height are set to `inf` which configures the largest possible world (not actually infinite).
- `discovery`: This specifies the name and description of the game server shown in search results for online and local servers.
- `blocks`: This consists of three fields, each for configuring the physical blocks in the world that constrain the players’ movement.
  - `blocks.block_size`: This is an integer that sets how large each block is in the game.
  - `blocks.grid_size`: This sets the width and height of the grid which contains these blocks. The number of blocks in this grid cannot exceed 220.
  - `blocks.ops`: This specifies a list of operations that are performed on block maps stored on a stack to produce a final block map.

## Spawning, ammo crates and powerups

To configure these, see the fields with the `events` prefix. See the full schema for more information.

## Special areas

The server has the ability to send an arbitrary list of rectangles of arbitrary position, size and colour to the clients, which the clients should render these in the world. These are designed for highlighting regions of the world where custom server-side configuration can perform certain actions. Right now the two special areas implemented in this game are portals and effect zones, though more special areas can be added in the future and those special areas can still be compatible with older clients.

Here’s an example of how to add special areas that are just decorative:

    (
    	...

    	special_areas: (
    		areas: [
    			( pos: (15, 15), size: (4, 4), colour: (127, 255, 0, 63) )
    		],
    	),
    )

### Portals

Portals allow players (but not bullets) to teleport from one location to another. Portals can either be sinks (you enter these), sources (you leave these), or bidirectional (you can go both ways). Portals are expressed in the config as a directed graph, with nodes being the portals and edges being the possible transitions between portals. If a node has more than one outgoing edge, players entering that portal will teleport to a random outgoing neighbour of that portal.

It is a desirable feature to allow the velocity of players to change after they leave the portal, like if they entered a horizontal portal and leave a vertical portal. To do this, `special_areas.portals.out.rotation` can be set to either `D0` (default for no rotation), `D90`, `D180` or `D270`.

When specifying portals, make sure that the player isn’t inside a block or outside the world border if they are at the portal’s centre. If you don’t ensure this, it might be possible for players to teleport inside a block.

#### Example

    (
    	...

    	special_areas: (
    		portals: [
    			(
    				rect: ((-4.5, -3.0), (1.5, 0.5)),
    				out: [
    					( index: 1, rotation: D90),
    					( index: 2 ),
    				],
    			),
    			( rect: ((12.5, -9.5), (14.5, -1.5)) ),
    			(
    				rect: ((22, 22), (24, 23)),
    				out: [( index: 0 )],
    			),
    		],
    	),
    )

### Effect zones

An effect zone is a region where players get effects applied to them (the same effects that powerups give) while there. These effects can persist for an arbitrary amount of time when the player leaves the effect zone.

#### Example

    (
    	...

    	special_areas: (
    		effect_zones: [
    			(
    				rect: ((15, 15), (19, 19)),
    				effect: Speed,
    				time: 5,
    				power: 4, // Very fast!
    			),
    		]
    	),
    )

## Teams

By default teams are disabled. To enable them, you need to specifically add them to the config.

### Example

Here is an example of how to create a red and blue team.

    (
    	...

    	teams: [
    		(
    			name: "Red",
    			colour: "red",
    			aliases: ["r", "red"],
    		),
    		(
    			name: "Blue",
    			colour: "blue",
    			aliases: ["b", "blue"],
    		),
    	],
    )

### Custom spawn points

It is possible to have all players in a team spawn at a fixed location in the world by adding `spawn_point: Some((X, Y))` for some `X` and `Y`. This doesn’t affect player spawning when in the lobby. See `configs/occupation.ron` for an example of this.

## Scoring

The scoring part of the config schema consists of two parts: the controller and the conditions for winning and elimination. The score controller specifies how the player and team scores should change in response to events, like a player getting a kill, dying or occupying a part of the world (see the occupation game). The win and elimination conditions accept as input the scores of either the player or the teams, as well as the timer. These decide respectively when the game should end and who (either in terms of players or teams) should win the game, or who should be removed from the game until the game finishes.

By default there is no win or elimination condition and the score of each player is their kill count.

### Examples

Here are a few examples of how the scoring system can be configured.

#### First to 10 kills

To win the game in the following config, you need to be the first player to get at least 10 kills.

It should be noted that multiple players can win the game if they both get to at least 10 kills at the exact same time, though this is very unlikely to occur.

    (
    	...

    	scoring: (
    		win: Player(FirstTo((GreaterEqual, 10))),
    	),
    )

#### Survival

Here’s another config where each player starts off with five lives, and the winning player is the last player remaining. Players get eliminated when they lose all their lives, as shown in the elimination condition.

The full config is in `configs/survival.ron`.

    (
    	...

    	scoring: (
    		controller: (
    			events: [
    				(
    					listener: Death,
    					score: -1,
    				),
    			],
    			player: (
    				init: 5,
    			),
    		),
    		win: Player(LastWith((Greater, 0))),
    		elimination: Player(FirstTo((LessEqual, 0))),
    		can_join_midgame: false,
    	),
    )

#### Occupation

Here is a server config that provides a unique game mode that isn’t around killing the other players or staying alive. Instead, the game consists of two teams, red and blue, that spawn at fixed locations in their base. When you occupy the base of the other team, your team’s score increases. The winner is the team with the highest score after five minutes. If after five minutes the scores are tied, the first team to then increase their score wins. (Also when occupying the other team’s base, you can teleport back to your own base with the team-only portal.)

This config also involves the timer which is displayed on the top-right corner of the screen and is used in the win condition.

The full config is in `configs/occupation.ron`.

    (
    	...

    	scoring: (
    		controller: (
    			events: [
    				(
    					listener: Occupation((
    						owner: 0,
    						rect: ((-10, -30), (10, -25.5)),
    						capture_time: 1,
    						period: 0.2,
    					)),
    					score: 1,
    					output: Team,
    				),
    				(
    					listener: Occupation((
    						owner: 1,
    						rect: ((-10, 25.5), (10, 30)),
    						capture_time: 1,
    						period: 0.2,
    					)),
    					score: 1,
    					output: Team,
    				),
    			],
    		),
    		win: Team(And([
    			Extremum( type: Maximum, exclusive: true ),
    			Timer((LessEqual, 0)),
    		])),
    	),

    	...

    	timer: (
    		now: 300,
    		limit: 0,
    		counting: Down,
    	),
    )

### Notes

When making your own scoring system and conditions for winning and elimination, here are a few important notes to consider:

1. Avoid `Equal` as a comparison operator. If having a goal of the winning player being the first one to 20 kills, you probably don’t want to use `goal: FirstTo((Equal, 20))`. This is because it’s possible that a player has 19 kills, and then fires two bullets which kill two players at the exact same time and boost their score to 21. In that case, that player lost their chance of winning the game.
2. Prevent players from joining mid-game. If your scoring system involves the elimination of players, you should add `can_join_midgame: false` to the config to prevent players from joining after being eliminated. You should also do this if players joining the game later on have an advantage (like having much fewer deaths).

## Lobby

By default as soon as players join, the game starts. However, you can put players in a “lobby” where players can shoot and kill each other but scoring is disabled before the game starts. This can be done to give players a break after playing the game.

To enable the lobby and have it last for, say 60 seconds, add this to your config:

    (
    	...

    	lobby: (
    		time: 60,
    	),
    )

Players can start the game prematurely if they all are ready by running the `ready` command (or its alias of `r`). If you want to decrease the proportion of players that need to be ready before the game starts, change `ready_threshold` to a value in [0, 1].

## Timer

A timer can be added while playing the game which can provide useful information to players, such as when the game will end. This timer can be used in the scoring system for the win and elimination conditions.

Here is an example of a timer that starts at zero and counts up forever. This timer is configured to display the raw seconds and use millisecond precision (which is probably overkill).

By default there is no timer.

    (
    	...

    	timer: (
    		now: 0,
    		limit: inf,
    		counting: Up,
    		precision: Milli,
    		format: S,
    	),
    )

## Capture the flag

Here is an example of a capture the flag configuration. This is a teamed game where players can capture flags from other teams and bring those flags to their own team’s base.

The full config is in `configs/ctf.ron`.

    (
    	...

    	flags: [
    		( default_pos: (-6, -28), team: 0 ),
    		( default_pos: (0, -28), team: 0 ),
    		( default_pos: (6, -28), team: 0 ),

    		( default_pos: (-6, 28), team: 1 ),
    		( default_pos: (0, 28), team: 1 ),
    		( default_pos: (6, 28), team: 1 ),
    	],
    	teams: [
    		(
    			name: "Red",
    			colour: "red",
    			aliases: ["r", "red"],
    			spawn_point: (0, -27.5),
    			base: ( rect: ((-12, -30), (12, -25.5)) ),
    		),
    		(
    			name: "Blue",
    			colour: "blue",
    			aliases: ["b", "blue"],
    			spawn_point: (0, 27.5),
    			base: ( rect: ((-12, 25.5), (12, 30)) ),
    		),
    	],
    )

Here, `default_pos` is the starting position of the flag and `team` is the id (indexing the `teams` array below) of the flag’s team. Also in the teams defined below is the team’s base. Flags are returned to their default position if they collide with their team’s base (and aren’t being picked up by a player on the other team), and they emit score events (for the base’s team) when a flag is returned to another team’s base.

### Increasing the score

Here is an example of how to increase the team’s score by one whenever a flag is returned to that team’s base.

Here `Flag(None)` increases the score for all flags returned. If you want different flags to be worth different scores, instead use `Flag(Some(i))` to only increase the score for the flag of index i being returned.

    (
    	...

    	scoring: (
    		controller: (
    			events: [
    				(
    					listener: Flag(None),
    					score: 1,
    				),
    			],
    		),
    	),
    )

## Stability

I make no guarantees about the stability of this schema between versions, even to changes in the patch version (third number in the version).

## Advanced configuration

Everything that can be configured right now through the server config is just a small sample of what you can actually configure. If you want to add something like an effect zone but for giving players ammo, it’s possible to modify the server source code without needing all players to update their game to your specific fork.

Here’s a list of ideas that can be implemented for custom server-side configuration that unmodified clients can play with:

- Recoil: When players fire bullets, the server can decrease the player’s velocity by a small amount in the opposite direction of the fired bullet.
- Stealing ammo: When a player kills another player, an ammo crate might spawn containing all ammo that player had.
- Player-player collision: Right now the game’s physics don’t support collision between players. However, this functionality can be hacked on by the server arbitrarily controlling the players’ positions and velocities.

Even more advanced configuration would require forking the game, such as if you want to add a new type of weapon.

## Tips

Here are some tips to be more efficient when customising a game server:

1. Right-click to print position. When in the world, you can right-click (or whatever you choose to bind it to) and the position your cursor’s pointing at will be printed to stderr. This can be very helpful when placing special effects.
2. `reload` command. In the server console, you can run `reload default` to quickly restart the server, which you might do a lot.

# Server console

When starting the server, you have access to a console that allows addition and removal of game servers, without restarting the entire process. Complete documentation for these particular commands isn’t provided here but instead available by running `help` in this console. Instead what is documented here is game ids and basic usage of running multiple game servers on the same process.

When using this console, I recommend redirecting stderr to somewhere else to avoid the logs from distracting you as you write your commands.

## Game ids

A Spaceships server can run multiple different game servers on the same UDP port. When a player joins the game, the client includes a game id in the request it makes to the server to play the game. The server then takes this id and checks its “routing table” to find a game server the player can join. Game ids are either entered manually when manually entering the address, or automatically with online or local discovery.

The exact mapping of game ids to game servers is up to the server. Game servers are limited to having only one game id, and game ids are limited to being at most 64 bytes.

One game server can be configured to be the default, if it has game id “default”. Here, if the user entered a game id that doesn’t match any explicit rule in the routing table, the player will join this default server. When you start a server without specifying a game id, it will be configured to be the default server.

## Private servers

Game ids can be used to have the server accessible over the internet but restrict who can join the game by requiring users enter a password. Here, the game id is the password. This works because clients cannot access to this routing table.

To ensure that a game server is truly private, it must not be globally discoverable! This is the case in the default settings but not if you run the server with `-P global`. One way to prevent this and still have the game publicly joinable is to add `-P none/global` instead. To verify this privacy setting, run the `status` command which prints out information of all running game servers, including the privacy config.

## Examples

Here are some examples. Lines beginning with `$` indicate a shell, while lines beginning with `>` indicate the server console.

### Multiple servers

Here is an example of how to host multiple game servers on the same process.

    $ ./spaceships server maze:configs/maze.ron
    > add cave:configs/cave.ron
    > add white_noise:configs/white_noise.ron
    > rename white_noise noise
    > add configs/empty.ron

After running all of this, the game ids `maze`, `cave` and `noise` are mapped to the servers with configs `configs/maze.ron`, `configs/cave.ron` and `configs/white_noise.ron` respectively, and all other game ids are mapped to `configs/empty.ron` by default.

### Hosting a private server

Here is how to run just a single server only accessible through a hard to guess game id.

    $ ./spaceships server -P none/global I-L0v3_sP4c35h1p5:configs/private.ron

Make sure that no colons are present in the password. If not, the game id would end there and the remainder of the string would be the path of a config file that most likely doesn’t exist.