143 lines
4.7 KiB
Nix
143 lines
4.7 KiB
Nix
# NixOS module: runs the dedicated game server as a systemd service and, by
|
|
# default, serves the static web client through nginx on the same host.
|
|
#
|
|
# services.battle-for-tismo.enable = true;
|
|
# services.battle-for-tismo.web.hostName = "tismo.example.com";
|
|
#
|
|
# The web client connects to the page's own origin, so nginx serves the static
|
|
# files at "/" and forwards "/ws" to the game server.
|
|
#
|
|
# By default the server runs the immutable copy packaged in the Nix store, so
|
|
# updating the game means rebuilding the system configuration. To update the
|
|
# game on its own instead, point the service at a writable checkout:
|
|
#
|
|
# services.battle-for-tismo.appDirectory = "/opt/battle-for-tismo";
|
|
#
|
|
# then `git -C /opt/battle-for-tismo pull` and
|
|
# `systemctl restart battle-for-tismo`.
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
cfg = config.services.battle-for-tismo;
|
|
pkg = pkgs.callPackage ./package.nix { };
|
|
in
|
|
{
|
|
options.services.battle-for-tismo = {
|
|
enable = lib.mkEnableOption "the Battle for 'Tismo game server";
|
|
|
|
appDirectory = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
example = "/opt/battle-for-tismo";
|
|
description = ''
|
|
Directory containing a writable checkout of the game (`client/`,
|
|
`server/` and `shared/`). When set, the server runs straight from this
|
|
directory with the Node.js from Nixpkgs, so the game can be updated
|
|
with a Git pull and a service restart instead of a system rebuild.
|
|
|
|
When null (the default) the immutable copy packaged in the Nix store
|
|
is used, and updates require a rebuild.
|
|
'';
|
|
};
|
|
|
|
package = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = pkg.server;
|
|
description = ''
|
|
Package providing the `battle-for-tismo-server` command. Ignored when
|
|
`appDirectory` is set.
|
|
'';
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 27015;
|
|
description = "Local WebSocket port the game server listens on.";
|
|
};
|
|
|
|
bindAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "127.0.0.1";
|
|
description = ''
|
|
Address the game server binds to. Keep the default to expose it only
|
|
through the reverse proxy; use "*" to accept direct connections.
|
|
'';
|
|
};
|
|
|
|
web = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Serve the static web client through nginx.";
|
|
};
|
|
|
|
package = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = pkg.web;
|
|
description = ''
|
|
Directory containing the static web client (client/index.html). Point
|
|
this at the `appDirectory` to serve the mutable checkout as well.
|
|
'';
|
|
};
|
|
|
|
hostName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "localhost";
|
|
description = "nginx virtual host name for the web client.";
|
|
};
|
|
|
|
enableSSL = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Enable ACME/HTTPS for the virtual host (browsers then use wss://).";
|
|
};
|
|
|
|
openFirewall = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Open the HTTP (and HTTPS when enabled) ports in the firewall.";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
systemd.services.battle-for-tismo = {
|
|
description = "Battle for 'Tismo game server";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network.target" ];
|
|
environment.HOME = "%S/battle-for-tismo";
|
|
serviceConfig = {
|
|
ExecStart =
|
|
if cfg.appDirectory != null then
|
|
"${pkgs.nodejs}/bin/node ${toString cfg.appDirectory}/server/server.js"
|
|
+ " --port ${toString cfg.port} --bind ${cfg.bindAddress}"
|
|
else
|
|
"${cfg.package}/bin/battle-for-tismo-server --port ${toString cfg.port} --bind ${cfg.bindAddress}";
|
|
DynamicUser = true;
|
|
StateDirectory = "battle-for-tismo";
|
|
Restart = "on-failure";
|
|
RestartSec = 3;
|
|
};
|
|
};
|
|
|
|
services.nginx = lib.mkIf cfg.web.enable {
|
|
enable = true;
|
|
virtualHosts.${cfg.web.hostName} = {
|
|
root = cfg.web.package;
|
|
# The client lives under client/; send the bare root to its index.
|
|
locations."= /".return = "302 /client/index.html";
|
|
locations."/ws" = {
|
|
proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
|
proxyWebsockets = true;
|
|
};
|
|
addSSL = cfg.web.enableSSL;
|
|
enableACME = cfg.web.enableSSL;
|
|
};
|
|
};
|
|
|
|
networking.firewall.allowedTCPPorts = lib.mkIf (cfg.web.enable && cfg.web.openFirewall) (
|
|
[ 80 ] ++ lib.optionals cfg.web.enableSSL [ 443 ]
|
|
);
|
|
};
|
|
}
|