nixos/mchprs: init module

Co-authored-by: Ryan Lahfa <masterancpp@gmail.com>

Changed files
+343 -1
nixos
doc
manual
release-notes
modules
services
games
+1 -1
nixos/doc/manual/release-notes/rl-2311.section.md
···
## New Services {#sec-release-23.11-new-services}
-
- Create the first release note entry in this section!
+
- [MCHPRS](https://github.com/MCHPR/MCHPRS), a multithreaded Minecraft server built for redstone. Available as [services.mchprs](#opt-services.mchprs.enable).
- [acme-dns](https://github.com/joohoi/acme-dns), a limited DNS server to handle ACME DNS challenges easily and securely. Available as [services.acme-dns](#opt-services.acme-dns.enable).
+1
nixos/modules/module-list.nix
···
./services/games/deliantra-server.nix
./services/games/factorio.nix
./services/games/freeciv.nix
+
./services/games/mchprs.nix
./services/games/minecraft-server.nix
./services/games/minetest-server.nix
./services/games/openarena.nix
+341
nixos/modules/services/games/mchprs.nix
···
+
{ config, lib, pkgs, ... }:
+
+
with lib;
+
+
let
+
cfg = config.services.mchprs;
+
settingsFormat = pkgs.formats.toml { };
+
+
whitelistFile = pkgs.writeText "whitelist.json"
+
(builtins.toJSON
+
(mapAttrsToList (n: v: { name = n; uuid = v; }) cfg.whitelist.list));
+
+
configToml =
+
(removeAttrs cfg.settings [ "address" "port" ]) //
+
{
+
bind_address = cfg.settings.address + ":" + toString cfg.settings.port;
+
whitelist = cfg.whitelist.enable;
+
};
+
+
configTomlFile = settingsFormat.generate "Config.toml" configToml;
+
in
+
{
+
options = {
+
services.mchprs = {
+
enable = mkEnableOption "MCHPRS";
+
+
declarativeSettings = mkOption {
+
type = types.bool;
+
default = false;
+
description = mdDoc ''
+
Whether to use a declarative configuration for MCHPRS.
+
'';
+
};
+
+
declarativeWhitelist = mkOption {
+
type = types.bool;
+
default = false;
+
description = mdDoc ''
+
Whether to use a declarative whitelist.
+
The options {option}`services.mchprs.whitelist.list`
+
will be applied if and only if set to `true`.
+
'';
+
};
+
+
dataDir = mkOption {
+
type = types.path;
+
default = "/var/lib/mchprs";
+
description = mdDoc ''
+
Directory to store MCHPRS database and other state/data files.
+
'';
+
};
+
+
openFirewall = mkOption {
+
type = types.bool;
+
default = false;
+
description = mdDoc ''
+
Whether to open ports in the firewall for the server.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
maxRuntime = mkOption {
+
type = types.str;
+
default = "infinity";
+
example = "7d";
+
description = mdDoc ''
+
Automatically restart the server after
+
{option}`services.mchprs.maxRuntime`.
+
The time span format is described here:
+
https://www.freedesktop.org/software/systemd/man/systemd.time.html#Parsing%20Time%20Spans.
+
If `null`, then the server is not restarted automatically.
+
'';
+
};
+
+
package = mkOption {
+
type = types.package;
+
default = pkgs.mchprs;
+
defaultText = literalExpression "pkgs.mchprs";
+
description = mdDoc "Version of MCHPRS to run.";
+
};
+
+
settings = mkOption {
+
type = types.submodule {
+
freeformType = settingsFormat.type;
+
+
options = {
+
port = mkOption {
+
type = types.port;
+
default = 25565;
+
description = mdDoc ''
+
Port for the server.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
address = mkOption {
+
type = types.str;
+
default = "0.0.0.0";
+
description = mdDoc ''
+
Address for the server.
+
Please use enclosing square brackets when using ipv6.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
motd = mkOption {
+
type = types.str;
+
default = "Minecraft High Performance Redstone Server";
+
description = mdDoc ''
+
Message of the day.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
chat_format = mkOption {
+
type = types.str;
+
default = "<{username}> {message}";
+
description = mdDoc ''
+
How to format chat message interpolating `username`
+
and `message` with curly braces.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
max_players = mkOption {
+
type = types.ints.positive;
+
default = 99999;
+
description = mdDoc ''
+
Maximum number of simultaneous players.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
view_distance = mkOption {
+
type = types.ints.positive;
+
default = 8;
+
description = mdDoc ''
+
Maximal distance (in chunks) between players and loaded chunks.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
bungeecord = mkOption {
+
type = types.bool;
+
default = false;
+
description = mdDoc ''
+
Enable compatibility with
+
[BungeeCord](https://github.com/SpigotMC/BungeeCord).
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
schemati = mkOption {
+
type = types.bool;
+
default = false;
+
description = mdDoc ''
+
Mimic the verification and directory layout used by the
+
Open Redstone Engineers
+
[Schemati plugin](https://github.com/OpenRedstoneEngineers/Schemati).
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
block_in_hitbox = mkOption {
+
type = types.bool;
+
default = true;
+
description = mdDoc ''
+
Allow placing blocks inside of players
+
(hitbox logic is simplified).
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
auto_redpiler = mkOption {
+
type = types.bool;
+
default = true;
+
description = mdDoc ''
+
Use redpiler automatically.
+
Only has effect when
+
{option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
};
+
};
+
default = { };
+
+
description = mdDoc ''
+
Configuration for MCHPRS via `Config.toml`.
+
See https://github.com/MCHPR/MCHPRS/blob/master/README.md for documentation.
+
'';
+
};
+
+
whitelist = {
+
enable = mkOption {
+
type = types.bool;
+
default = false;
+
description = mdDoc ''
+
Whether or not the whitelist (in `whitelist.json`) shoud be enabled.
+
Only has effect when {option}`services.mchprs.declarativeSettings` is `true`.
+
'';
+
};
+
+
list = mkOption {
+
type =
+
let
+
minecraftUUID = types.strMatching
+
"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" // {
+
description = "Minecraft UUID";
+
};
+
in
+
types.attrsOf minecraftUUID;
+
default = { };
+
example = literalExpression ''
+
{
+
username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
+
username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy";
+
};
+
'';
+
description = mdDoc ''
+
Whitelisted players, only has an effect when
+
{option}`services.mchprs.declarativeWhitelist` is
+
`true` and the whitelist is enabled
+
via {option}`services.mchprs.whitelist.enable`.
+
This is a mapping from Minecraft usernames to UUIDs.
+
You can use <https://mcuuid.net/> to get a
+
Minecraft UUID for a username.
+
'';
+
};
+
};
+
};
+
};
+
+
config = mkIf cfg.enable {
+
users.users.mchprs = {
+
description = "MCHPRS service user";
+
home = cfg.dataDir;
+
createHome = true;
+
isSystemUser = true;
+
group = "mchprs";
+
};
+
users.groups.mchprs = { };
+
+
systemd.services.mchprs = {
+
description = "MCHPRS Service";
+
wantedBy = [ "multi-user.target" ];
+
after = [ "network.target" ];
+
+
serviceConfig = {
+
ExecStart = "${lib.getExe cfg.package}";
+
Restart = "always";
+
RuntimeMaxSec = cfg.maxRuntime;
+
User = "mchprs";
+
WorkingDirectory = cfg.dataDir;
+
+
StandardOutput = "journal";
+
StandardError = "journal";
+
+
# Hardening
+
CapabilityBoundingSet = [ "" ];
+
DeviceAllow = [ "" ];
+
LockPersonality = true;
+
MemoryDenyWriteExecute = true;
+
PrivateDevices = true;
+
PrivateTmp = true;
+
PrivateUsers = true;
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHome = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProtectProc = "invisible";
+
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
SystemCallArchitectures = "native";
+
UMask = "0077";
+
};
+
+
preStart =
+
(if cfg.declarativeSettings then ''
+
if [ -e .declarativeSettings ]; then
+
+
# Settings were declarative before, no need to back up anything
+
cp -f ${configTomlFile} Config.toml
+
+
else
+
+
# Declarative settings for the first time, backup stateful files
+
cp -b --suffix=.stateful ${configTomlFile} Config.toml
+
+
echo "Autogenerated file that implies that this server configuration is managed declaratively by NixOS" \
+
> .declarativeSettings
+
+
fi
+
'' else ''
+
if [ -e .declarativeSettings ]; then
+
rm .declarativeSettings
+
fi
+
'') + (if cfg.declarativeWhitelist then ''
+
if [ -e .declarativeWhitelist ]; then
+
+
# Whitelist was declarative before, no need to back up anything
+
ln -sf ${whitelistFile} whitelist.json
+
+
else
+
+
# Declarative whitelist for the first time, backup stateful files
+
ln -sb --suffix=.stateful ${whitelistFile} whitelist.json
+
+
echo "Autogenerated file that implies that this server's whitelist is managed declaratively by NixOS" \
+
> .declarativeWhitelist
+
+
fi
+
'' else ''
+
if [ -e .declarativeWhitelist ]; then
+
rm .declarativeWhitelist
+
fi
+
'');
+
};
+
+
networking.firewall = mkIf (cfg.declarativeSettings && cfg.openFirewall) {
+
allowedUDPPorts = [ cfg.settings.port ];
+
allowedTCPPorts = [ cfg.settings.port ];
+
};
+
};
+
+
meta.maintainers = with maintainers; [ gdd ];
+
}