commit d6c713cec5dffeadaa04b0114b33ceba87238a09
parent a88c19104988fdcec10f019fdac11b9879c547a3
Author: Luke Willis <lukejw@loquat.dev>
Date:   Thu, 14 May 2026 00:27:47 -0400

bta: Add times mod and mod config support

Diffstat:
Msystem/andrew.scm | 98+++++++++++++++++++++++++------------------------------------------------------
Asystem/andrew/minecraft.scm | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 257 insertions(+), 67 deletions(-)

diff --git a/system/andrew.scm b/system/andrew.scm @@ -1,6 +1,6 @@ (define-module (system andrew) #:use-module (system andrew ci) - #:use-module (system andrew bta) + #:use-module (system andrew minecraft) #:use-module (guix gexp) #:use-module (guix packages) #:use-module (guix modules) @@ -68,8 +68,11 @@ table inet filter { # allow ngninx tcp dport { 80, 443 } accept - # allow minecraft / voice chat - th dport { 25565, 24454 } accept + # allow minecraft java (TCP) + tcp dport 25565 accept + + # allow minecraft bedrock & voice chat (UDP) + udp dport 19132 accept # reject everything else reject with icmpx type port-unreachable @@ -85,60 +88,21 @@ table inet filter { (define %mod-list (list - ;; Required mods - (bta-mod - (name "halplibe") - (display-name "HalpLibe") - (url "https://github.com/Turnip-Labs/bta-halplibe/releases/download/v5.4.1/halplibe-5.4.1.jar") - (sha256 (base32 "0vn64nh94zx6bv060bxgjkwjmn53759glvkjy5c1h8g4qmpghsd1"))) - (bta-mod - (name "modmenu") - (display-name "Mod Menu") - (url "https://github.com/Turnip-Labs/ModMenu/releases/download/4.0.1/modmenu-bta-4.0.1.jar") - (sha256 (base32 "0yhj3xsb4gljgjvc7c4xhf9g9dgkhgia6xbsv9gybnm8dkha0cm2"))) - (bta-mod - (name "orthodoxicons") - (display-name "Orthodox Icons") - (url "https://files.monastech.xyz/orthodoxicons-1.1.0.jar") - (sha256 (base32 "0y580h3bczghbp75zja4530pkm1kj4dj4r6858dl5f41dmxvgnf2"))) - (bta-mod - (name "baskettipping") - (display-name "Basket Tipping") - (url "https://github.com/BlueMoonJune/BasketTipping/releases/download/Release/baskettipping-1.0.2.jar") - (sha256 (base32 "1ayscx03lw26pi4ra0bdfyb8mpkh0706r77a3jk0wxqqnfwxaws4"))) - (bta-mod - (name "breeding") - (display-name "Breeding") - (url "https://github.com/UselessSolutions/bta-breeding-backport/releases/download/v1.1.0-7.3/btabreeding-1.1.0-7.3.jar") - (sha256 (base32 "1l3jwc4q1ygv31v19c50y1j1gvsfq286i0k9ndhpmbbdjhlab1cw"))) - (bta-mod-times - (bta-times-configuration - (realtime #t))) - ;; Optional mods - (bta-mod - (name "btwaila") - (display-name "Better Than WAILA") - (description "Adds additional tooltips when looking at things. If you do a \ -lot of inventory management or technical stuff, this is probably for you.") - (optional #t) - (url "https://github.com/ToufouMaster/BTWaila/releases/download/1.2.5-7.3_04/btwaila-1.2.5-7.3_04.jar") - (sha256 (base32 "12yxaisg7rixmpwyx3li0nc1jz1k55d9fybjbih1nrjbvlahmrvv"))) - (bta-mod - (name "legacyui") - (display-name "Legacy UI") - (description "Modifies the UI to be similar to the legacy console edition, \ -alongside improving controller support. If you use a controller, I recommend this.") - (optional #t) - (url "https://github.com/UselessSolutions/BTA_Babric_LegacyUI/releases/download/v1.3.1-7.3.3/legacyui-1.3.1-7.3.3.jar") - (sha256 (base32 "1xqdcs10y5xdjmlpqkwicsqzniin3wzzb4khfgi67qkhb70p6z0k"))) - (bta-mod - (name "piessuildingtweaks") - (display-name "Pie's Building Tweaks") - (description "Adds some options to assist with building: offhand, randomize, \ -refill, etc.") - (optional #t) - (url "https://cdn.modrinth.com/data/bODCFQpm/versions/pIctNkZy/BuildingTweaks-1.1.0-7.3_04.jar") - (sha256 (base32 "0nf3yi3bz7z6hm5jpf0bljbpp3ggmlry50aq0bhlhcl4n0496p4i"))))) + (minecraft-mod + (url "https://cdn.modrinth.com/data/P7dR8mSH/versions/q87Wssd1/fabric-api-0.148.2+26.1.2.jar") + (sha256 (base32 "1ar6f4f94yxk06flpknh10y6q8k5yibm6nzjzzrd738jnvyviicr"))) + (minecraft-mod + (url "https://cdn.modrinth.com/data/aZj58GfX/versions/h9nSM2ZF/easyauth-mc26.1-3.4.3-SNAPSHOT.48.jar") + (sha256 (base32 "01v14b7r0b95idjqimqa0q3wdyzqmyi3j03bw3n8jpbsqzni613n"))) + (minecraft-mod + (url "https://cdn.modrinth.com/data/wKkoqHrH/versions/q4Pg0AcC/Geyser-Fabric-2.10.0-b1138.jar") + (sha256 (base32 "1j1gs3kpnp4wfa8sj9s7j3c4aqx4cn33pqcfwz3iska88k54h88i"))) + (minecraft-mod + (url "https://cdn.modrinth.com/data/bWrNNfkb/versions/fD4J9lnX/Floodgate-Fabric-2.2.6-b63.jar") + (sha256 (base32 "0bazalawy2md8x1q51vgns4p9847k0bgcs4pnnhvbnp53wljpapl"))) + (minecraft-mod + (url "https://cdn.modrinth.com/data/gvQqBUqZ/versions/R7MxYvuW/lithium-fabric-0.24.2+mc26.1.2.jar") + (sha256 (base32 "002fvncv6g6122ark6748lj4b3za0ffa8raz6sdp9qphlbqqjli2"))))) (define andrew-os (operating-system @@ -183,17 +147,16 @@ refill, etc.") (list (service nftables-service-type (nftables-configuration (ruleset %nftables-ruleset))) - (service bta-service-type - (bta-configuration - (properties `(("motd" . "MonasTech Private Server") - ("difficulty" . "3") + (service minecraft-service-type + (minecraft-configuration + (properties `(("motd" . "HolyCross SMP - CHADS ONLY") + ("difficulty" . "hard") ("allow-flight" . "true") ("white-list" . "true") - ("online-mode" . "false"))) - (ops '("1a68c56c-0bbc-413d-8fe1-10a2e4e04ad2")) - (mods %mod-list) - (packwiz-home "/var/www/files.monastech.xyz/bta") - (packwiz-url "https://files.monastech.xyz/bta"))) + ("spawn-protection" . "0") + ("online-mode" . "true") + ("enforce-secure-profile" . "false"))) + (mods %mod-list))) (service update-channels-locked-service-type '()) (service nginx-service-type (nginx-configuration @@ -215,7 +178,8 @@ refill, etc.") (listen '("443 ssl")) (root "/var/www/files.monastech.xyz") (ssl-certificate "/etc/dehydrated/certs/monastech.xyz/fullchain.pem") - (ssl-certificate-key "/etc/dehydrated/certs/monastech.xyz/privkey.pem")) (nginx-server-configuration + (ssl-certificate-key "/etc/dehydrated/certs/monastech.xyz/privkey.pem")) + (nginx-server-configuration (server-name '("loquat.dev" "www.loquat.dev")) (listen '("443 ssl")) (root "/var/www/loquat.dev") diff --git a/system/andrew/minecraft.scm b/system/andrew/minecraft.scm @@ -0,0 +1,226 @@ +(define-module (system andrew minecraft) + #:use-module (gnu services) + #:use-module (gnu services configuration) + #:use-module (gnu services shepherd) + #:use-module (gnu packages admin) + #:use-module (gnu packages compression) + #:use-module (gnu packages gnupg) + #:use-module (gnu packages java) + #:use-module (gnu system shadow) + #:use-module (guix gexp) + #:use-module (guix store) + #:use-module (guix base16) + #:use-module (guix modules) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (guix download) + #:use-module (guix build-system copy) + #:use-module (mt utils) + #:use-module (mt packages games) + #:use-module ((srfi srfi-1) #:select (filter-map)) + #:use-module (ice-9 match) + #:export (minecraft-mod + minecraft-mod? + minecraft-mod->origin + minecraft-configuration + minecraft-configuration? + minecraft-service-type)) + +;; Mod helper records +(define-record-type* <minecraft-mod> + minecraft-mod + make-minecraft-mod + minecraft-mod? + (url minecraft-mod-url) + (sha256 minecraft-mod-sha256) + (config minecraft-mod-config + (default #f))) + +(define (minecraft-mod->origin mod) + (origin + (method url-fetch) + (uri (minecraft-mod-url mod)) + (sha256 (minecraft-mod-sha256 mod)))) + +;; Main code +(define %fabric-server + (origin + (method url-fetch) + (uri "https://meta.fabricmc.net/v2/versions/loader/26.1.2/0.19.2/1.1.1/server/jar") + (sha256 (base32 "1gax8i1risr0irgcmbwc0jdf78yhahplsqiyhiblrq7hkydx26z9")))) + +(define %minecraft-account + (list (user-account + (name "minecraft") + (group "minecraft") + (system? #t) + (comment "Minecraft server user") + (home-directory "/var/lib/minecraft") + (shell (file-append shadow "/sbin/nologin"))) + (user-group + (name "minecraft") + (system? #t)))) + +;; TODO: Add more options +(define-record-type* <minecraft-configuration> + minecraft-configuration + make-minecraft-configuration + minecraft-configuration? + (jdk minecraft-configuration-jdk + (default openjdk25)) + (memory minecraft-configuration-memory + (default 8)) + (properties minecraft-configuration-properties + (default '())) + (mods minecraft-configuration-mods + (default '())) + (home minecraft-configuration-home + (default "/var/lib/minecraft")) + (log-file minecraft-configuration-log-file + (default "/var/log/minecraft.log"))) + +(define %rcon-password + (random-string 72)) + +(define minecraft-activation + (match-lambda + (($ <minecraft-configuration> jdk memory properties mods home log-file) + (let* ((mod-files (map minecraft-mod->origin mods)) + (mod-config-files (filter-map minecraft-mod-config mods))) + (with-imported-modules '((guix build utils)) + #~(begin + (use-modules (guix build utils) + (ice-9 match)) + + (define (remkdir dir) + (when (file-exists? dir) + (delete-file-recursively dir)) + (mkdir dir)) + + (let ((user (getpwnam "minecraft")) + (eula-txt + #$(string-append home "/eula.txt")) + (mod-dir + #$(string-append home "/mods")) + (config-dir + #$(string-append home "/config")) + (server-properties + #$(string-append home "/server.properties"))) + ;; TODO: Finish + (match (primitive-fork) + (0 + (dynamic-wind + (const #t) + (lambda () + ;; Switch to the minecraft user + (setgid (passwd:gid user)) + (setuid (passwd:uid user)) + + ;; Initialize eula.txt + (unless (file-exists? eula-txt) + (call-with-output-file + eula-txt + (lambda (port) + (display "eula=true\n" port)))) + + ;; Initialize server.properties + (call-with-output-file + server-properties + (lambda (port) + (display "enable-rcon=true\n" port) + (format port "rcon.password=~a\n" #$%rcon-password) + (for-each + (lambda (pair) + (format port "~a=~a\n" (car pair) (cdr pair))) + '#$properties))) + + ;; Link mods + (remkdir mod-dir) + (for-each + (lambda (mod) + (symlink mod + (string-append mod-dir "/" + (string-drop (basename mod) 33)))) + '#$mod-files) + + ;; Link configs + (remkdir config-dir) + (for-each + (lambda (config) + (symlink config + (string-append config-dir "/" + (string-drop (basename config) 33)))) + '#$mod-config-files) + + ;; Return to main thread + (primitive-exit 0)) + (lambda () + (primitive-exit 1)))) + (pid (waitpid pid)))))))))) + + +(define minecraft-shepherd-service + (match-lambda + (($ <minecraft-configuration> jdk memory properties mods home log-file) + (shepherd-service + (documentation "Minecraft server") + (provision '(minecraft)) + (requirement '(user-processes networking)) + (start #~(make-forkexec-constructor + (list #$(file-append jdk "/bin/java") + #$(format #f "-Xmx~aG" memory) + "-jar" #$%fabric-server + "nogui") + #:user "minecraft" + #:group "minecraft" + #:directory #$home + #:log-file #$log-file)) + (actions (list (shepherd-action + (name 'rcon) + (documentation "Run server commands via rcon.") + (procedure + #~(begin + (use-modules (ice-9 rdelim)) + + (lambda (running . args) + (define (collect-lines port) + "Collect all lines from a port in a list" + (let loop ((line (read-line port)) + (acc '())) + (if (eof-object? line) + (begin + (close-port port) + acc) + (loop (read-line port) + (cons line acc))))) + + (let ((output-pipe (pipe))) + (spawn #$(file-append mcrcon "/bin/mcrcon") + (cons "mcrcon" args) + #:output (cdr output-pipe) + #:error (cdr output-pipe) + #:environment + (list #$(string-append "MCRCON_PASS=" + %rcon-password)) + #:search-path? #f) + (close-port (cdr output-pipe)) + (while #t + (let ((line (read-line (car output-pipe)))) + (if (eof-object? line) + (break)) + (display line) + (newline)))))))))) + (stop #~(make-kill-destructor SIGINT)))))) + +(define minecraft-service-type + (service-type + (name 'minecraft) + (extensions + (list (service-extension account-service-type + (const %minecraft-account)) + (service-extension activation-service-type + minecraft-activation) + (service-extension shepherd-root-service-type + (compose list minecraft-shepherd-service)))) + (default-value (minecraft-configuration)) + (description "Run a Minecraft server.")))