This commit is contained in:
@@ -1,211 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
dataDir = config.services.minecraft-servers.dataDir;
|
|
||||||
createMods = pkgs.linkFarmFromDrvs "create-aeronautics-mods" (
|
|
||||||
builtins.attrValues {
|
|
||||||
Sable = pkgs.fetchurl {
|
|
||||||
url = "https://cdn.modrinth.com/data/T9PomCSv/versions/g8CObHcP/sable-neoforge-1.21.1-1.1.3.jar";
|
|
||||||
sha512 = "8180e214681c171c9e3b7fa307f7a92bd7de0b8125d671291425f04a4ba26b408758d8ea80a6386d8e73bb1e6b02caf3f20afb9b91ecedd48c37ed44363ac961";
|
|
||||||
};
|
|
||||||
Create = pkgs.fetchurl {
|
|
||||||
url = "https://cdn.modrinth.com/data/LNytGWDc/versions/UjX6dr61/create-1.21.1-6.0.10.jar";
|
|
||||||
sha512 = "11cc8fc049d2f67f6548c7abfada6b82a3adb5c7ca410a742de04bbca76e03862c518721b88d806f6e6d768a4d68531fdb903a85859b25d1484d550cc7bafd4b";
|
|
||||||
};
|
|
||||||
CreateAeronautics = pkgs.fetchurl {
|
|
||||||
url = "https://cdn.modrinth.com/data/oWaK0Q19/versions/1sv6OtSz/create-aeronautics-bundled-1.21.1-1.1.3.jar";
|
|
||||||
sha512 = "94831bc4702b3864524258fa0a73a50ab3cd37e9c157b5c6688a6845b866ec5838452804050b55e490549d91dad909fc37f0d619f354c5676e2e2651b9c15ec6";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
in {
|
|
||||||
sops.secrets."minecraft/storage-box-key" = {
|
|
||||||
sopsFile = ../../../sus/neuro.yaml;
|
|
||||||
owner = "minecraft-map-import";
|
|
||||||
group = "minecraft-map-import";
|
|
||||||
mode = "0400";
|
|
||||||
};
|
|
||||||
|
|
||||||
users.groups.minecraft-map-import = { };
|
|
||||||
users.users.minecraft-map-import = {
|
|
||||||
description = "Minecraft map importer";
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "minecraft-map-import";
|
|
||||||
extraGroups = [ "minecraft" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.ssh.knownHosts."u664722.your-storagebox.de".publicKey =
|
|
||||||
"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA5EB5p/5Hp3hGW1oHok+PIOH9Pbn7cnUiGmUEBrCVjnAw+HrKyN8bYVV0dIGllswYXwkG/+bgiBlE6IVIBAq+JwVWu1Sss3KarHY3OvFJUXZoZyRRg/Gc/+LRCE7lyKpwWQ70dbelGRyyJFH36eNv6ySXoUYtGkwlU5IVaHPApOxe4LHPZa/qhSRbPo2hwoh0orCtgejRebNtW5nlx00DNFgsvn8Svz2cIYLxsPVzKgUxs8Zxsxgn+Q/UvR7uq4AbAhyBMLxv7DjJ1pc7PJocuTno2Rw9uMZi1gkjbnmiOh6TTXIEWbnroyIhwc8555uto9melEUmWNQ+C+PwAK+MPw==";
|
|
||||||
|
|
||||||
systemd.services.minecraft-world-import-wowMineMap = {
|
|
||||||
description = "Import WoW Mine custom Minecraft map";
|
|
||||||
before = [ "minecraft-server-wowMineMap.service" ];
|
|
||||||
wants = [ "network-online.target" ];
|
|
||||||
after = [ "network-online.target" ];
|
|
||||||
path = [ pkgs.coreutils pkgs.findutils pkgs.openssh pkgs.unar ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = "minecraft-map-import";
|
|
||||||
Group = "minecraft-map-import";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
StateDirectory = "minecraft-maps";
|
|
||||||
TimeoutStartSec = "30min";
|
|
||||||
ProtectSystem = "strict";
|
|
||||||
ProtectHome = true;
|
|
||||||
PrivateDevices = true;
|
|
||||||
PrivateTmp = true;
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
CapabilityBoundingSet = [ "" ];
|
|
||||||
ReadWritePaths = [ "/var/lib/minecraft-maps" dataDir ];
|
|
||||||
UMask = "0077";
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
set -eu
|
|
||||||
umask 077
|
|
||||||
|
|
||||||
cache_dir=/var/lib/minecraft-maps
|
|
||||||
server_dir=${dataDir}/wowMineMap
|
|
||||||
archive="$cache_dir/wow mine map.rar"
|
|
||||||
temporary_archive="$cache_dir/.wow-mine-map.rar.$$"
|
|
||||||
extraction_dir="$cache_dir/.wow-mine-map.$$"
|
|
||||||
key=${config.sops.secrets."minecraft/storage-box-key".path}
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
rm -f "$temporary_archive"
|
|
||||||
rm -rf "$extraction_dir"
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
mkdir -p "$cache_dir" "$server_dir"
|
|
||||||
chmod 0700 "$cache_dir"
|
|
||||||
chgrp minecraft "$server_dir"
|
|
||||||
chmod 0770 "$server_dir"
|
|
||||||
|
|
||||||
if [ -d "$server_dir/world" ]; then
|
|
||||||
if [ -f "$server_dir/world/level.dat" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Minecraft world directory exists but has no level.dat" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$archive" ]; then
|
|
||||||
downloaded=false
|
|
||||||
attempt=1
|
|
||||||
while [ "$attempt" -le 3 ]; do
|
|
||||||
if sftp \
|
|
||||||
-o BatchMode=yes \
|
|
||||||
-o StrictHostKeyChecking=yes \
|
|
||||||
-o UserKnownHostsFile=/etc/ssh/ssh_known_hosts \
|
|
||||||
-i "$key" \
|
|
||||||
-b - \
|
|
||||||
u664722@u664722.your-storagebox.de <<EOF
|
|
||||||
get "wow mine map.rar" "$temporary_archive"
|
|
||||||
EOF
|
|
||||||
then
|
|
||||||
downloaded=true
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
rm -f "$temporary_archive"
|
|
||||||
sleep 10
|
|
||||||
attempt=$((attempt + 1))
|
|
||||||
done
|
|
||||||
if [ "$downloaded" != true ]; then
|
|
||||||
echo "Unable to download WoW Mine map from Storage Box" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
mv "$temporary_archive" "$archive"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! printf '%s %s\n' \
|
|
||||||
bc80084de10a06b0fc2cb1651c61936b9e2fd2288f3f0fe44c964d83a393aa30 \
|
|
||||||
"$archive" | sha256sum -c -; then
|
|
||||||
rm -f "$archive"
|
|
||||||
echo "Cached WoW Mine map checksum mismatch" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$extraction_dir"
|
|
||||||
unar -quiet -output-directory "$extraction_dir" "$archive"
|
|
||||||
|
|
||||||
find "$extraction_dir" \
|
|
||||||
\( -type l -o -type b -o -type c -o -type p -o -type s \) \
|
|
||||||
-delete
|
|
||||||
|
|
||||||
world_level_dat=$(find "$extraction_dir" -type f -name level.dat -print -quit)
|
|
||||||
if [ -z "$world_level_dat" ]; then
|
|
||||||
echo "RAR archive contains no Minecraft level.dat" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mv "$(dirname "$world_level_dat")" "$server_dir/world"
|
|
||||||
chgrp -R minecraft "$server_dir/world"
|
|
||||||
chmod -R u+rwX,g+rwX,o-rwx "$server_dir/world"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.minecraft-server-wowMineMap = {
|
|
||||||
requires = [ "minecraft-world-import-wowMineMap.service" ];
|
|
||||||
after = [ "minecraft-world-import-wowMineMap.service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
services.minecraft-servers = {
|
|
||||||
enable = true;
|
|
||||||
eula = true;
|
|
||||||
openFirewall = true;
|
|
||||||
|
|
||||||
servers = {
|
|
||||||
vanilla = {
|
|
||||||
enable = true;
|
|
||||||
jvmOpts = "-Xmx6G -Xms2G";
|
|
||||||
package = pkgs.minecraftServers.vanilla-1_21_11;
|
|
||||||
|
|
||||||
serverProperties = {
|
|
||||||
server-port = 25565;
|
|
||||||
difficulty = "hard";
|
|
||||||
online-mode = true;
|
|
||||||
view-distance = 32;
|
|
||||||
level-seed = "8306359138650378643";
|
|
||||||
pause-when-empty-seconds = 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
createAeronautics = {
|
|
||||||
enable = true;
|
|
||||||
jvmOpts = "-Xmx8G -Xms2G";
|
|
||||||
package = pkgs.minecraftServers.neoforge-1_21_1;
|
|
||||||
|
|
||||||
symlinks = {
|
|
||||||
mods = createMods;
|
|
||||||
};
|
|
||||||
|
|
||||||
serverProperties = {
|
|
||||||
server-port = 25566;
|
|
||||||
difficulty = "hard";
|
|
||||||
online-mode = true;
|
|
||||||
view-distance = 20;
|
|
||||||
pause-when-empty-seconds = 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
wowMineMap = {
|
|
||||||
enable = true;
|
|
||||||
jvmOpts = "-Xmx8G -Xms2G";
|
|
||||||
package = pkgs.minecraftServers.neoforge-1_21_1;
|
|
||||||
|
|
||||||
symlinks.mods = createMods;
|
|
||||||
|
|
||||||
serverProperties = {
|
|
||||||
server-port = 25567;
|
|
||||||
difficulty = "hard";
|
|
||||||
online-mode = true;
|
|
||||||
view-distance = 20;
|
|
||||||
level-name = "world";
|
|
||||||
pause-when-empty-seconds = 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.minecraft-servers.servers.createAeronautics = {
|
||||||
|
enable = true;
|
||||||
|
jvmOpts = "-Xmx8G -Xms2G";
|
||||||
|
package = pkgs.minecraftServers.neoforge-1_21_1;
|
||||||
|
|
||||||
|
symlinks = {
|
||||||
|
mods = import ./mods.nix { inherit pkgs; };
|
||||||
|
};
|
||||||
|
|
||||||
|
serverProperties = {
|
||||||
|
server-port = 25566;
|
||||||
|
difficulty = "hard";
|
||||||
|
online-mode = true;
|
||||||
|
view-distance = 20;
|
||||||
|
pause-when-empty-seconds = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./vanilla.nix
|
||||||
|
./create-aeronautics.nix
|
||||||
|
./wow-mine-map.nix
|
||||||
|
./world-import.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.minecraft-servers = {
|
||||||
|
enable = true;
|
||||||
|
eula = true;
|
||||||
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{ pkgs }:
|
||||||
|
|
||||||
|
pkgs.linkFarmFromDrvs "create-aeronautics-mods" (
|
||||||
|
builtins.attrValues {
|
||||||
|
Sable = pkgs.fetchurl {
|
||||||
|
url = "https://cdn.modrinth.com/data/T9PomCSv/versions/g8CObHcP/sable-neoforge-1.21.1-1.1.3.jar";
|
||||||
|
sha512 = "8180e214681c171c9e3b7fa307f7a92bd7de0b8125d671291425f04a4ba26b408758d8ea80a6386d8e73bb1e6b02caf3f20afb9b91ecedd48c37ed44363ac961";
|
||||||
|
};
|
||||||
|
Create = pkgs.fetchurl {
|
||||||
|
url = "https://cdn.modrinth.com/data/LNytGWDc/versions/UjX6dr61/create-1.21.1-6.0.10.jar";
|
||||||
|
sha512 = "11cc8fc049d2f67f6548c7abfada6b82a3adb5c7ca410a742de04bbca76e03862c518721b88d806f6e6d768a4d68531fdb903a85859b25d1484d550cc7bafd4b";
|
||||||
|
};
|
||||||
|
CreateAeronautics = pkgs.fetchurl {
|
||||||
|
url = "https://cdn.modrinth.com/data/oWaK0Q19/versions/1sv6OtSz/create-aeronautics-bundled-1.21.1-1.1.3.jar";
|
||||||
|
sha512 = "94831bc4702b3864524258fa0a73a50ab3cd37e9c157b5c6688a6845b866ec5838452804050b55e490549d91dad909fc37f0d619f354c5676e2e2651b9c15ec6";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.minecraft-servers.servers.vanilla = {
|
||||||
|
enable = true;
|
||||||
|
jvmOpts = "-Xmx6G -Xms2G";
|
||||||
|
package = pkgs.minecraftServers.vanilla-1_21_11;
|
||||||
|
|
||||||
|
serverProperties = {
|
||||||
|
server-port = 25565;
|
||||||
|
difficulty = "hard";
|
||||||
|
online-mode = true;
|
||||||
|
view-distance = 32;
|
||||||
|
level-seed = "8306359138650378643";
|
||||||
|
pause-when-empty-seconds = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (lib) mkOption types;
|
||||||
|
|
||||||
|
cfg = config.services.minecraft-world-imports;
|
||||||
|
enabledImports = lib.filterAttrs (_: import: import.enable) cfg;
|
||||||
|
dataDir = config.services.minecraft-servers.dataDir;
|
||||||
|
minecraftServers = config.services.minecraft-servers.servers;
|
||||||
|
targetPairs = lib.mapAttrsToList (_: import:
|
||||||
|
"${import.serverName}:${import.worldName}") enabledImports;
|
||||||
|
|
||||||
|
importerUser = name: let
|
||||||
|
descriptiveName = "minecraft-map-import-${name}";
|
||||||
|
in
|
||||||
|
if builtins.stringLength descriptiveName <= 31
|
||||||
|
then descriptiveName
|
||||||
|
else "mc-import-${builtins.substring 0 12 (builtins.hashString "sha256" name)}";
|
||||||
|
|
||||||
|
stateDirectory = cacheDir:
|
||||||
|
if lib.hasPrefix "/var/lib/" cacheDir
|
||||||
|
then lib.removePrefix "/var/lib/" cacheDir
|
||||||
|
else null;
|
||||||
|
|
||||||
|
stateDirectoryCompatible = cacheDir: let
|
||||||
|
relative = stateDirectory cacheDir;
|
||||||
|
components = lib.splitString "/" (if relative == null then "" else relative);
|
||||||
|
in
|
||||||
|
relative != null
|
||||||
|
&& relative != ""
|
||||||
|
&& lib.all (component: component != "" && component != "." && component != "..") components;
|
||||||
|
|
||||||
|
escapeSftp = value:
|
||||||
|
"\"${lib.replaceStrings ["\\" "\""] ["\\\\" "\\\""] value}\"";
|
||||||
|
|
||||||
|
escapeTmpfiles = value:
|
||||||
|
lib.replaceStrings ["%" " " "\t"] ["%%" "\\x20" "\\x09"] value;
|
||||||
|
|
||||||
|
importerServices = lib.mkMerge (lib.mapAttrsToList (name: import: let
|
||||||
|
user = importerUser name;
|
||||||
|
unitName = "minecraft-world-import-${name}";
|
||||||
|
serverUnit = "minecraft-server-${import.serverName}.service";
|
||||||
|
cacheStateDirectory = stateDirectory import.cacheDir;
|
||||||
|
serverDir = "${dataDir}/${import.serverName}";
|
||||||
|
worldDir = "${serverDir}/${import.worldName}";
|
||||||
|
in {
|
||||||
|
${unitName} = {
|
||||||
|
description = "Import Minecraft world ${name}";
|
||||||
|
before = [ serverUnit ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
path = [ pkgs.coreutils pkgs.findutils pkgs.openssh pkgs.unar ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = user;
|
||||||
|
Group = user;
|
||||||
|
RemainAfterExit = true;
|
||||||
|
TimeoutStartSec = import.timeoutStartSec;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
CapabilityBoundingSet = [ "" ];
|
||||||
|
ReadWritePaths = [ import.cacheDir dataDir ];
|
||||||
|
UMask = "0077";
|
||||||
|
} // lib.optionalAttrs (stateDirectoryCompatible import.cacheDir) {
|
||||||
|
StateDirectory = cacheStateDirectory;
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
set -eu
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
cache_dir=${lib.escapeShellArg import.cacheDir}
|
||||||
|
server_dir=${lib.escapeShellArg serverDir}
|
||||||
|
world_dir=${lib.escapeShellArg worldDir}
|
||||||
|
archive_name=${lib.escapeShellArg import.archiveName}
|
||||||
|
world_name=${lib.escapeShellArg import.worldName}
|
||||||
|
archive="$cache_dir/$archive_name"
|
||||||
|
temporary_archive="$cache_dir/.$archive_name.$$"
|
||||||
|
extraction_dir="$cache_dir/.minecraft-world-import-${name}.$$"
|
||||||
|
staged_world="$server_dir/.$world_name.import.$$"
|
||||||
|
key=${lib.escapeShellArg import.sshKeyFile}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$temporary_archive"
|
||||||
|
rm -rf "$extraction_dir" "$staged_world"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
mkdir -p "$cache_dir" "$server_dir"
|
||||||
|
chmod 0700 "$cache_dir"
|
||||||
|
chgrp minecraft "$server_dir"
|
||||||
|
chmod 0770 "$server_dir"
|
||||||
|
|
||||||
|
if [ -d "$world_dir" ]; then
|
||||||
|
if [ -f "$world_dir/level.dat" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Minecraft world directory exists but has no level.dat" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$archive" ]; then
|
||||||
|
downloaded=false
|
||||||
|
attempt=1
|
||||||
|
while [ "$attempt" -le ${toString import.downloadRetries} ]; do
|
||||||
|
if sftp \
|
||||||
|
-o BatchMode=yes \
|
||||||
|
-o StrictHostKeyChecking=yes \
|
||||||
|
-o UserKnownHostsFile=/etc/ssh/ssh_known_hosts \
|
||||||
|
-i "$key" \
|
||||||
|
-b - \
|
||||||
|
${lib.escapeShellArg "${import.remoteUser}@${import.remoteHost}"} <<EOF
|
||||||
|
get ${escapeSftp import.remotePath} "$temporary_archive"
|
||||||
|
EOF
|
||||||
|
then
|
||||||
|
downloaded=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
rm -f "$temporary_archive"
|
||||||
|
sleep ${toString import.retryDelaySeconds}
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
done
|
||||||
|
if [ "$downloaded" != true ]; then
|
||||||
|
echo "Unable to download Minecraft world ${name}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mv "$temporary_archive" "$archive"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s %s\n' \
|
||||||
|
${lib.escapeShellArg import.archiveSha256} \
|
||||||
|
"$archive" | sha256sum -c -; then
|
||||||
|
rm -f "$archive"
|
||||||
|
echo "Cached Minecraft world ${name} checksum mismatch" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$extraction_dir"
|
||||||
|
unar -quiet -output-directory "$extraction_dir" "$archive"
|
||||||
|
|
||||||
|
find "$extraction_dir" \
|
||||||
|
\( -type l -o -type b -o -type c -o -type p -o -type s \) \
|
||||||
|
-delete
|
||||||
|
|
||||||
|
world_level_dat=$(find "$extraction_dir" -type f -name level.dat -print -quit)
|
||||||
|
if [ -z "$world_level_dat" ]; then
|
||||||
|
echo "Minecraft world archive contains no level.dat" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "$(dirname "$world_level_dat")" "$staged_world"
|
||||||
|
chgrp -R minecraft "$staged_world"
|
||||||
|
chmod -R u+rwX,g+rwX,o-rwx "$staged_world"
|
||||||
|
mv "$staged_world" "$world_dir"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
"minecraft-server-${import.serverName}" = {
|
||||||
|
requires = [ "${unitName}.service" ];
|
||||||
|
after = [ "${unitName}.service" ];
|
||||||
|
};
|
||||||
|
}) enabledImports);
|
||||||
|
in {
|
||||||
|
options.services.minecraft-world-imports = mkOption {
|
||||||
|
default = { };
|
||||||
|
type = types.attrsOf (types.submodule ({ name, ... }: {
|
||||||
|
options = {
|
||||||
|
enable = lib.mkEnableOption "Minecraft world import ${name}";
|
||||||
|
|
||||||
|
serverName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "minecraft-servers server receiving imported world";
|
||||||
|
};
|
||||||
|
|
||||||
|
remoteHost = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "SSH host serving world archive";
|
||||||
|
};
|
||||||
|
|
||||||
|
remoteUser = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "SSH user used to download world archive";
|
||||||
|
};
|
||||||
|
|
||||||
|
remotePath = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Remote path to world archive";
|
||||||
|
};
|
||||||
|
|
||||||
|
archiveName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Archive file name inside cache directory";
|
||||||
|
};
|
||||||
|
|
||||||
|
cacheDir = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/var/lib/minecraft-world-imports/${name}";
|
||||||
|
description = "Persistent archive cache directory";
|
||||||
|
};
|
||||||
|
|
||||||
|
archiveSha256 = mkOption {
|
||||||
|
type = types.strMatching "[0-9a-fA-F]{64}";
|
||||||
|
description = "Expected SHA-256 digest of world archive";
|
||||||
|
};
|
||||||
|
|
||||||
|
sshKeyFile = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Runtime path to private SSH key";
|
||||||
|
};
|
||||||
|
|
||||||
|
worldName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "world";
|
||||||
|
description = "World directory name beneath server directory";
|
||||||
|
};
|
||||||
|
|
||||||
|
hostPublicKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Pinned SSH host public key";
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadRetries = mkOption {
|
||||||
|
type = types.ints.positive;
|
||||||
|
default = 3;
|
||||||
|
description = "Maximum SFTP download attempts";
|
||||||
|
};
|
||||||
|
|
||||||
|
retryDelaySeconds = mkOption {
|
||||||
|
type = types.ints.unsigned;
|
||||||
|
default = 10;
|
||||||
|
description = "Delay between SFTP download attempts";
|
||||||
|
};
|
||||||
|
|
||||||
|
timeoutStartSec = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "30min";
|
||||||
|
description = "Importer service start timeout";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
description = "Minecraft worlds imported before selected servers start";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf (enabledImports != { }) {
|
||||||
|
assertions = lib.flatten (lib.mapAttrsToList (name: import: [
|
||||||
|
{
|
||||||
|
assertion = builtins.match "[A-Za-z0-9_-]+" name != null;
|
||||||
|
message = "services.minecraft-world-imports.${name}: name must contain only letters, digits, underscores, or hyphens";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = builtins.stringLength name <= 24;
|
||||||
|
message = "services.minecraft-world-imports.${name}: name must be at most 24 characters";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = builtins.match "/.*" import.cacheDir != null;
|
||||||
|
message = "services.minecraft-world-imports.${name}.cacheDir must be absolute";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = builtins.match "[A-Za-z0-9_-]+" import.serverName != null;
|
||||||
|
message = "services.minecraft-world-imports.${name}.serverName must contain only letters, digits, underscores, or hyphens";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = !lib.hasInfix "\n" import.remotePath && !lib.hasInfix "\r" import.remotePath;
|
||||||
|
message = "services.minecraft-world-imports.${name}.remotePath must not contain newlines";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = builtins.hasAttr import.serverName minecraftServers
|
||||||
|
&& (builtins.getAttr import.serverName minecraftServers).enable;
|
||||||
|
message = "services.minecraft-world-imports.${name}.serverName must name an enabled Minecraft server";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = lib.length (lib.unique targetPairs) == lib.length targetPairs;
|
||||||
|
message = "services.minecraft-world-imports: each server/world target must be unique";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = builtins.match "[^/]+" import.archiveName != null;
|
||||||
|
message = "services.minecraft-world-imports.${name}.archiveName must be a file name";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = builtins.match "[^/]+" import.worldName != null;
|
||||||
|
message = "services.minecraft-world-imports.${name}.worldName must be a directory name";
|
||||||
|
}
|
||||||
|
]) enabledImports);
|
||||||
|
|
||||||
|
users.groups = lib.mapAttrs' (name: _: lib.nameValuePair (importerUser name) { }) enabledImports;
|
||||||
|
users.users = lib.mapAttrs' (name: _: let
|
||||||
|
user = importerUser name;
|
||||||
|
in lib.nameValuePair user {
|
||||||
|
description = "Minecraft world importer ${name}";
|
||||||
|
isSystemUser = true;
|
||||||
|
group = user;
|
||||||
|
extraGroups = [ "minecraft" ];
|
||||||
|
}) enabledImports;
|
||||||
|
|
||||||
|
programs.ssh.knownHosts = lib.mapAttrs' (name: import:
|
||||||
|
lib.nameValuePair "minecraft-world-import-${name}" {
|
||||||
|
hostNames = [ import.remoteHost ];
|
||||||
|
publicKey = import.hostPublicKey;
|
||||||
|
}) enabledImports;
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = lib.flatten (lib.mapAttrsToList (name: import:
|
||||||
|
lib.optional (!stateDirectoryCompatible import.cacheDir)
|
||||||
|
"d ${escapeTmpfiles import.cacheDir} 0700 ${importerUser name} ${importerUser name} -") enabledImports);
|
||||||
|
|
||||||
|
systemd.services = importerServices;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
sops.secrets."minecraft/storage-box-key" = {
|
||||||
|
sopsFile = ../../../../sus/neuro.yaml;
|
||||||
|
owner = "minecraft-map-import-wowMineMap";
|
||||||
|
group = "minecraft-map-import-wowMineMap";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.minecraft-world-imports.wowMineMap = {
|
||||||
|
enable = true;
|
||||||
|
serverName = "wowMineMap";
|
||||||
|
remoteHost = "u664722.your-storagebox.de";
|
||||||
|
remoteUser = "u664722";
|
||||||
|
remotePath = "minecraft/map/wow mine map.rar";
|
||||||
|
archiveName = "wow mine map.rar";
|
||||||
|
cacheDir = "/var/lib/minecraft-maps";
|
||||||
|
archiveSha256 = "bc80084de10a06b0fc2cb1651c61936b9e2fd2288f3f0fe44c964d83a393aa30";
|
||||||
|
sshKeyFile = config.sops.secrets."minecraft/storage-box-key".path;
|
||||||
|
worldName = "world";
|
||||||
|
hostPublicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA5EB5p/5Hp3hGW1oHok+PIOH9Pbn7cnUiGmUEBrCVjnAw+HrKyN8bYVV0dIGllswYXwkG/+bgiBlE6IVIBAq+JwVWu1Sss3KarHY3OvFJUXZoZyRRg/Gc/+LRCE7lyKpwWQ70dbelGRyyJFH36eNv6ySXoUYtGkwlU5IVaHPApOxe4LHPZa/qhSRbPo2hwoh0orCtgejRebNtW5nlx00DNFgsvn8Svz2cIYLxsPVzKgUxs8Zxsxgn+Q/UvR7uq4AbAhyBMLxv7DjJ1pc7PJocuTno2Rw9uMZi1gkjbnmiOh6TTXIEWbnroyIhwc8555uto9melEUmWNQ+C+PwAK+MPw==";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.minecraft-servers.servers.wowMineMap = {
|
||||||
|
enable = true;
|
||||||
|
jvmOpts = "-Xmx8G -Xms2G";
|
||||||
|
package = pkgs.minecraftServers.neoforge-1_21_1;
|
||||||
|
|
||||||
|
symlinks.mods = import ./mods.nix { inherit pkgs; };
|
||||||
|
|
||||||
|
serverProperties = {
|
||||||
|
server-port = 25567;
|
||||||
|
difficulty = "hard";
|
||||||
|
online-mode = true;
|
||||||
|
view-distance = 20;
|
||||||
|
level-name = "world";
|
||||||
|
pause-when-empty-seconds = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -70,7 +70,7 @@ in {
|
|||||||
imports = [
|
imports = [
|
||||||
self.nixosModules.hectic
|
self.nixosModules.hectic
|
||||||
inputs.sops-nix.nixosModules.sops
|
inputs.sops-nix.nixosModules.sops
|
||||||
./minecraft.nix
|
./minecraft
|
||||||
./hardware.nix
|
./hardware.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user