diff --git a/flake.lock b/flake.lock index e17eb7a..c01c547 100644 --- a/flake.lock +++ b/flake.lock @@ -184,6 +184,22 @@ "type": "github" } }, + "nixpkgs-vintagestory": { + "locked": { + "lastModified": 1781229721, + "narHash": "sha256-ORvqDbb/LYxiJljGIejapjkc/kJbVote2N1WSb9W45I=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "173d0ad7a974f8543a9ab01d2271b2e290341b33", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "173d0ad7a974f8543a9ab01d2271b2e290341b33", + "type": "github" + } + }, "nixpkgs_2": { "locked": { "lastModified": 1780952837, @@ -206,6 +222,7 @@ "dicebot": "dicebot", "disko": "disko", "nixpkgs": "nixpkgs_2", + "nixpkgs-vintagestory": "nixpkgs-vintagestory", "sops-nix": "sops-nix" } }, diff --git a/flake.nix b/flake.nix index 05116dd..0f1e00c 100644 --- a/flake.nix +++ b/flake.nix @@ -24,6 +24,12 @@ url = "git+https://forgejo.ellie.town/wizzy/dicebot.git"; inputs.nixpkgs.follows = "nixpkgs"; }; + + # Pinned by rev so automated flake updates don't bump the game server: + # Vintage Story clients must match the server version, so upgrades + # should be deliberate. This rev carries vintagestory 1.22.3, which + # needs unstable's package recipe anyway (.NET 10, new asset layout). + nixpkgs-vintagestory.url = "github:nixos/nixpkgs/173d0ad7a974f8543a9ab01d2271b2e290341b33"; }; outputs = @@ -33,6 +39,7 @@ sops-nix, continuwuity, dicebot, + nixpkgs-vintagestory, ... }: let @@ -45,7 +52,7 @@ { nixosConfigurations.home-server = nixpkgs.lib.nixosSystem { inherit system; - specialArgs = { inherit continuwuity dicebot; }; + specialArgs = { inherit continuwuity dicebot nixpkgs-vintagestory; }; modules = [ disko.nixosModules.disko @@ -72,6 +79,7 @@ ./services/forgejo-runner.nix ./services/borgbackup.nix ./services/gotosocial.nix + ./services/vintagestory.nix # ./services/akkoma.nix ]; }; diff --git a/services/vintagestory-mods.nix b/services/vintagestory-mods.nix new file mode 100644 index 0000000..2301a25 --- /dev/null +++ b/services/vintagestory-mods.nix @@ -0,0 +1,300 @@ +# Server mods pinned by ModDB download URL. Hashes were computed from +# the originally-downloaded zips, so nix verifies ModDB serves identical +# bytes. Client-only mods are omitted: the server has no use for them. +[ + { + name = "ACulinaryArtillery_2.0.0-dev.20.zip"; + url = "https://moddbcdn.vintagestory.at/ACulinaryArtillery%2B2_3de2ebc82f4afc7e151d6dbd3471abfc.zip?dl=ACulinaryArtillery%202.0.0-dev.20.zip"; + hash = "sha256-I0iFB/If8CxneNQtOJFev3lbf4XAQRSw1NT7m+EaTUo="; + } + { + name = "BarkBeetle_1.0.6.zip"; + url = "https://moddbcdn.vintagestory.at/BarkBeetle_1.0.6_a122749665a99cdbfc748c4b03e1f49e.zip?dl=BarkBeetle_1.0.6.zip"; + hash = "sha256-JP/f+QCYQa4zpOVlC63cmUcciH8FzqiYMeZvrOfeLPg="; + } + { + name = "BetterForest_0.1.2.zip"; + url = "https://moddbcdn.vintagestory.at/BetterForest_0.1.2_b2c2dce76416ead1e077c4ea2bc40090.zip?dl=BetterForest_0.1.2.zip"; + hash = "sha256-nJ2lozOdYElquqsZKLP4iymJUC4shRlFSJrq0K/NlSs="; + } + { + name = "CarryOn-1.22.0_v1.14.2.zip"; + url = "https://moddbcdn.vintagestory.at/CarryOn-1.22.0_v1.14_48dccd248b8c48ce3be7a57b97001f1d.zip?dl=CarryOn-1.22.0_v1.14.2.zip"; + hash = "sha256-ZHMP3uYtCiZiGpGX5tzRJvPg756yiOvOthtT9sxgf1k="; + } + { + name = "EternalStew_v1.4.1.zip"; + url = "https://moddbcdn.vintagestory.at/EternalStew_v1.4.1_a936c9e81a85dbc2248e94436d47ac17.zip?dl=EternalStew_v1.4.1.zip"; + hash = "sha256-ShIbhKkFzJovFCOtVP/EsfwGNEHXXAJFqhVHuesUru0="; + } + { + name = "ExpandedFoods_2.0.0-dev.9.zip"; + url = "https://moddbcdn.vintagestory.at/ExpandedFoods%2B2.0.0-_aeda57604a3c556f8c2c3b5259512df2.zip?dl=ExpandedFoods%202.0.0-dev.9.zip"; + hash = "sha256-PAWA6vbhz85oBShhgBfriOrKNCs7loVkNJTvSusnJDQ="; + } + { + name = "ExpandedFoods_MealsModule_1.0.0-dev.4.zip"; + url = "https://moddbcdn.vintagestory.at/ExpandedFoods_MealsM_0461ec7acdd5a5d8172c3befbf3b5c58.zip?dl=ExpandedFoods_MealsModule%201.0.0-dev.4.zip"; + hash = "sha256-n7ZHf6uwSw9TxjkN0UGQim1WeFZstdLwtwS01IZSIdo="; + } + { + name = "FloralZonesCosmopolitanRegion-v1.0.11.zip"; + url = "https://moddbcdn.vintagestory.at/FloralZonesCosmopoli_b447b353b156b6ff9679d675d42b6d1e.zip?dl=FloralZonesCosmopolitanRegion-v1.0.11.zip"; + hash = "sha256-pLnToC+ebwWyPPIJzyWAzgWbkyte0KGL9is+Ln7J1GU="; + } + { + name = "Footprints-v1.2.3.zip"; + url = "https://moddbcdn.vintagestory.at/Footprints-v1.2.3_e019324bdc6462f5c24da96c75e4e7c9.zip?dl=Footprints-v1.2.3.zip"; + hash = "sha256-odjUDO28tM3GJay9Vbjk7cNf+fvI9a9JDkwbnprOYIM="; + } + { + name = "ForeverEmpty_0.1.1.zip"; + url = "https://moddbcdn.vintagestory.at/ForeverEmpty_0.1.1_cb71f4ddcf2065a5db9b242c483d885a.zip?dl=ForeverEmpty_0.1.1.zip"; + hash = "sha256-Ja5yaVHN3PKqPFgT3j+qoBbUCb+asQq37ONXu0EZdQ4="; + } + { + name = "FotSA-Bovinae-v0.3.5.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Bovinae-v0.3.5_03d4ca207124ffc4a305f6077fd5d327.zip?dl=FotSA-Bovinae-v0.3.5.zip"; + hash = "sha256-9C1VwPCZj/E9xN+rasTWtqnxUChX7Xok6Qt0eJ5bixo="; + } + { + name = "FotSA-Caninae-v1.1.5.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Caninae-v1.1.5_eb6160b0c2e2f9154852ee42dc26d77d.zip?dl=FotSA-Caninae-v1.1.5.zip"; + hash = "sha256-PZYyL9XfXwh9VOA/L2O/8C4jstBJMXL7Djs8oxtqrwM="; + } + { + name = "FotSA-Capreolinae-v2.0.12.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Capreolinae-v2_81f29034957f1bef751f5eb3075ba9b3.zip?dl=FotSA-Capreolinae-v2.0.12.zip"; + hash = "sha256-F8b9Cuo6RMcKLg3XUVfPr/gfozMg+Dve7on2x/US3BY="; + } + { + name = "FotSA-Casuariidae-v1.1.9.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Casuariidae-v1_bb79c5345f80a6ac304761fcb3494585.zip?dl=FotSA-Casuariidae-v1.1.9.zip"; + hash = "sha256-Wg4nqW8eIAOYjkIc43Rvqjx7Py0mpLu1bRr4NpEzrqk="; + } + { + name = "FotSA-Cervinae-v0.1.13.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Cervinae-v0.1._4cccde632143e6df95ab5e3b55459e72.zip?dl=FotSA-Cervinae-v0.1.13.zip"; + hash = "sha256-et0Yy8z/+Qt+keto3/XCsM0aG8Iv/U8Nifavwlibxu8="; + } + { + name = "FotSA-Chelonioidea-v1.0.7.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Chelonioidea-v_66bd897c21d608e4e86b15764ac8e548.zip?dl=FotSA-Chelonioidea-v1.0.7.zip"; + hash = "sha256-k8vkMXH/8z4dJnP+kem9DHBclmXU/dv1hN6Cru1av1s="; + } + { + name = "FotSA-Dinornithidae-v1.0.22.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Dinornithidae-_6ec466ef3ade0f35c202c70ebf8e5be5.zip?dl=FotSA-Dinornithidae-v1.0.22.zip"; + hash = "sha256-pNETRIzniqfx8DGxrPc7ic4OTCtL76c4ugEQiDcBWzc="; + } + { + name = "FotSA-Elephantidae-v1.0.17.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Elephantidae-v_77eae91bd7f5caba76799d69c36919d2.zip?dl=FotSA-Elephantidae-v1.0.17.zip"; + hash = "sha256-cT+ld7IhaE61yrrrceSUbm3IE+cW78QqPtBWrs+xP94="; + } + { + name = "FotSA-Felinae-v0.2.13.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Felinae-v0.2.1_b0b826af6b26d4e53cd198f9f99f190f.zip?dl=FotSA-Felinae-v0.2.13.zip"; + hash = "sha256-fSBpG1MgGYjXaU8EtGOZ7ftK7fTMrKoyy7APPJulh5M="; + } + { + name = "FotSA-Iniidae-v0.1.6.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Iniidae-v0.1.6_ccc3808449493b8869c5c5d94d7b31d7.zip?dl=FotSA-Iniidae-v0.1.6.zip"; + hash = "sha256-tvjFuCgAndWNPBw/6i1rPE87DSpqs3IVZjyqbNmzst8="; + } + { + name = "FotSA-Machairodontinae-v1.1.9.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Machairodontin_3a115455a1087d286cec88bc4e2c2f1f.zip?dl=FotSA-Machairodontinae-v1.1.9.zip"; + hash = "sha256-Sm7psOx+/3NXqO+OHMhiJNC3SecWGv9WNAj0rOTypHA="; + } + { + name = "FotSA-Manidae-v1.0.19.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Manidae-v1.0.1_079f36d26134f703c391a8d2878ea3ad.zip?dl=FotSA-Manidae-v1.0.19.zip"; + hash = "sha256-V45OSIbSNXRlU3QtEk1xyuFTh8+Ok7Pqq1UIoRDZHG0="; + } + { + name = "FotSA-Meiolaniidae-v0.1.10.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Meiolaniidae-v_a0bb02d79ceebec5f016b29b8dfcd18b.zip?dl=FotSA-Meiolaniidae-v0.1.10.zip"; + hash = "sha256-WbA2S45RPSOEVeVCPcEoDKCURdcl0Gtz3lPoqwwi9ik="; + } + { + name = "FotSA-Pantherinae-v1.2.13.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Pantherinae-v1_009563fc574db5035ef01a4f472420e9.zip?dl=FotSA-Pantherinae-v1.2.13.zip"; + hash = "sha256-4Dx7qQUuLYPiwLZ2nBujzBnDtUOsnWpLyNXdwynchBg="; + } + { + name = "FotSA-Rhinocerotidae-v1.0.23.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Rhinocerotidae_46f7d8642bfeed08b8112db3412a267a.zip?dl=FotSA-Rhinocerotidae-v1.0.23.zip"; + hash = "sha256-1xpkkLAToT8Nlhv/LBtQla4gRfp+D3Ab00QApTUdXhI="; + } + { + name = "FotSA-Sirenia-v1.0.28.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Sirenia-v1.0.2_d727680473f7cb68eef774a722a97f8a.zip?dl=FotSA-Sirenia-v1.0.28.zip"; + hash = "sha256-jVvXRSj2H1SvPR4DXXbOEitGtk5kCZk84/luR92Ka6g="; + } + { + name = "FotSA-Spheniscidae-v1.0.16.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Spheniscidae-v_740bfdf53499bdd1e8b6833fe4613bfa.zip?dl=FotSA-Spheniscidae-v1.0.16.zip"; + hash = "sha256-GpCeGrGkBxGEvLlHEGeXLA4gVBjT7nFjwTc0PcHg6ZY="; + } + { + name = "FotSA-Thylacinidae-v0.1.6.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Thylacinidae-v_214e792773e4e5f70c0fc322f6f7799e.zip?dl=FotSA-Thylacinidae-v0.1.6.zip"; + hash = "sha256-sg97HSWJJz2KrD7mro4SF4cuviSdM6UVws8HcSLVdeo="; + } + { + name = "FotSA-Viverridae-v1.0.8.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Viverridae-v1._78f54920028890d40d25cad4568edf86.zip?dl=FotSA-Viverridae-v1.0.8.zip"; + hash = "sha256-UZh7OLzfoetUyStfUFsNchCPKt7FAE9rPkxhZ8KsCBE="; + } + { + name = "FotSA-Vombatidae-v0.4.5.zip"; + url = "https://moddbcdn.vintagestory.at/FotSA-Vombatidae-v0._a56aeddd4b0b393409a9304e66c116ff.zip?dl=FotSA-Vombatidae-v0.4.5.zip"; + hash = "sha256-ax5XutRRtOCtk4pp6vEaWqjI5LD0CPhVJp6mvjh60To="; + } + { + name = "GeologyAdditions_1.4.7.zip"; + url = "https://moddbcdn.vintagestory.at/GeologyAdditions%2B1.4_9747b7280441789492853161902812cb.zip?dl=GeologyAdditions%201.4.7.zip"; + hash = "sha256-OS2nVTBd2jmz7a4RhzvTl2PAoEbJGk4jZ+HKy2CC/64="; + } + { + name = "MaterialNeedsFlowers_1.0.0.zip"; + url = "https://moddbcdn.vintagestory.at/MaterialNeedsFlowers_0ceeca18b872d6d7adb4155c1520107c.zip?dl=MaterialNeedsFlowers_1.0.0.zip"; + hash = "sha256-E91h9BzdbYM/IuMTkrac1LLDuDnpN8IlYEfibZmH2Nk="; + } + { + name = "MaterialNeedsGeology_2.0.8.zip"; + url = "https://moddbcdn.vintagestory.at/MaterialNeedsGeology_0b78d0e14b669c4524922a2d1aef68ea.zip?dl=MaterialNeedsGeology_2.0.8.zip"; + hash = "sha256-yX5wmNRW0Z0s51j7mFEmoBDulyhlPqqEjk75LhsRGLA="; + } + { + name = "MaterialNeeds_1.1.16.zip"; + url = "https://moddbcdn.vintagestory.at/MaterialNeeds_1.1.16_2d89f9239c75e921ce432021d6c9d9b9.zip?dl=MaterialNeeds_1.1.16.zip"; + hash = "sha256-7xHetbVN1taJLSxVi/YNRMEcoPYF9KArokqj2G7jiA8="; + } + { + name = "NoSurfaceMonsters.zip"; + url = "https://moddbcdn.vintagestory.at/NoSurfaceMonsters_76c6ccf0a7de12e3f18b28b1e72dfbb5.zip?dl=NoSurfaceMonsters.zip"; + hash = "sha256-3GIxhTrf8Q7O3sB3SKnYacPFoAx8lbSlQqz6gXQIpvA="; + } + { + name = "PS_BagTweaks_1.0.0.zip"; + url = "https://moddbcdn.vintagestory.at/PS_BagTweaks_1.0.0_2bac41ef4d392e1066321395ff207646.zip?dl=PS_BagTweaks_1.0.0.zip"; + hash = "sha256-Nzqt0QRt7OXQhUsA/gFs5U3w+m99tgQnWWZ5YFMtmpU="; + } + { + name = "PS_LumberSlings_1.0.3.zip"; + url = "https://moddbcdn.vintagestory.at/PS_LumberSlings_1.0._88b18a7f0bf5e0b50472e9f445352734.zip?dl=PS_LumberSlings_1.0.3.zip"; + hash = "sha256-+j89zZLC1iGFrrto1LfRBlBGsgON7kI7t2OTigClUBk="; + } + { + name = "SeraphLeveling_1.17.2.zip"; + url = "https://moddbcdn.vintagestory.at/SeraphLeveling_1.17._23732e29a89a79bb092aa3922fd8f72d.zip?dl=SeraphLeveling_1.17.2.zip"; + hash = "sha256-LGKtNfIG90fMSF0oFLuy/LKftZgQfghhHMmm36u6vos="; + } + { + name = "TerraPrety_7.10.2.zip"; + url = "https://moddbcdn.vintagestory.at/TerraPrety_7.10.2_2b52cbb110fa0f0ff4bd4299a2633a3a.zip?dl=TerraPrety_7.10.2.zip"; + hash = "sha256-1KZh2NgQRhK0EbqFoTZXoYpd6dWqNOqzLYhSuGWa0Mw="; + } + { + name = "WaymarkerAndMatNeedsGeology-1.0.0.zip"; + url = "https://moddbcdn.vintagestory.at/WaymarkerAndMatNeeds_416916f714e9f55c2d91d965342e9ad1.zip?dl=WaymarkerAndMatNeedsGeology-1.0.0.zip"; + hash = "sha256-hUPAOQn3qwFW0jjxRBnQFgnuTd2+SPnXalYyYCxrXH4="; + } + { + name = "Wilderlands_Waymarkers_1.0.4.zip"; + url = "https://moddbcdn.vintagestory.at/Wilderlands%2BWaymarke_a411cf5ca2f1b95b2a240347efd6f46a.zip?dl=Wilderlands%20Waymarkers%201.0.4.zip"; + hash = "sha256-2xGSH9qHQH76vpA+D11sc6S0PFDHcNFE9prXMNpPcCM="; + } + { + name = "betterloot2.0.2.zip"; + url = "https://moddbcdn.vintagestory.at/betterloot2.0.2_1e6e57047509ceb8f52d5c310b4feeb9.zip?dl=betterloot2.0.2.zip"; + hash = "sha256-itNjpH/FohJBDiCrgyzuooJ3nyfVjmz0/KgFW8Im7wk="; + } + { + name = "butchering_1.13.4.zip"; + url = "https://moddbcdn.vintagestory.at/butchering_1.13.4_f7fdbb40b481dccac50566ae9ead1b9e.zip?dl=butchering_1.13.4.zip"; + hash = "sha256-bf5KSa3GSzXxtztG8qydiVLgMRVFF6VKnPJ3cWJTI2s="; + } + { + name = "crateful_2.1.0.zip"; + url = "https://moddbcdn.vintagestory.at/crateful_2.1.0_8c23246416fca899d7e02ba17150681f.zip?dl=crateful_2.1.0.zip"; + hash = "sha256-F3tPA5E4Ph+m3VetwTUotdXSUhiJIqBMoVj2CHpUnPs="; + } + { + name = "expandedbeekeepingfork_0.3.1.zip"; + url = "https://moddbcdn.vintagestory.at/expandedbeekeepingfo_134512e413b3b275881eba6435c22f06.zip?dl=expandedbeekeepingfork_0.3.1.zip"; + hash = "sha256-+qLJqhg1jIvQWPMwTs6LCRwEkW5kOhLT7LyZqdEKIS4="; + } + { + name = "firearmsfork_1.9.16.zip"; + url = "https://moddbcdn.vintagestory.at/firearmsfork_1.9.16_cf92cbd64b88404c05401ae06d058b96.zip?dl=firearmsfork_1.9.16.zip"; + hash = "sha256-2bNyaOQBGzh/20prE9ojU/XE2u2s5nAhYiukwetdPxA="; + } + { + name = "overhaulliblegacycompat_1.1.18.zip"; + url = "https://moddbcdn.vintagestory.at/overhaulliblegacycom_c3ef40dd4cf376bbfb3ed7e87343b99a.zip?dl=overhaulliblegacycompat_1.1.18.zip"; + hash = "sha256-bXLGg269VmBka+Ejdy/KEjcuRrdYhFfAgnyfygHsjl0="; + } + { + name = "placeonslabs_1.1.3.zip"; + url = "https://moddbcdn.vintagestory.at/placeonslabs_1.1.3_132240c82eef616f7be3e2dec1e61b87.zip?dl=placeonslabs_1.1.3.zip"; + hash = "sha256-mCDo8l/1gmQu6V6f/D7Lg4RBIcBjwDwUwZ6uAKmYu7g="; + } + { + name = "rockstratavariety_0.0.4.zip"; + url = "https://moddbcdn.vintagestory.at/rockstratavariety_0._1b4de983682f2c3eec000c044acfd20a.zip?dl=rockstratavariety_0.0.4.zip"; + hash = "sha256-1UYA+04qfrB1UtshPLD/wT57cPUkIO+FreaYl3XBrfk="; + } + { + name = "seamlessrapids.zip"; + url = "https://moddbcdn.vintagestory.at/seamlessrapids_bcd613b522fd8ff67f994f1f81cd5764.zip?dl=seamlessrapids.zip"; + hash = "sha256-wHIG4iSDB0iY00Fty7PqXps7/WtRZvThr6pHu5/6/hw="; + } + { + name = "shipwright_1.3.3.zip"; + url = "https://moddbcdn.vintagestory.at/shipwright_1.3.3_da324c578f5c7c20f33f2f3aa2d63b88.zip?dl=shipwright_1.3.3.zip"; + hash = "sha256-34piz9CqpGX/TfNccLzSHpD+dVStnvE7HyTTeWUxmwQ="; + } + { + name = "smithingplus_1.9.0-rc.1.zip"; + url = "https://moddbcdn.vintagestory.at/smithingplus_1.9.0-r_387b188934e826e4ba9cd7b3661499ed.zip?dl=smithingplus_1.9.0-rc.1.zip"; + hash = "sha256-63/2q7SZlD0qvxwAcxDuu7RFRk+cMpme/XrMb2Zoqy4="; + } + { + name = "terrainslabs_1.0.16.zip"; + url = "https://moddbcdn.vintagestory.at/terrainslabs_1.0.16_947f988ba279dbb0d9be7aad4de6dba4.zip?dl=terrainslabs_1.0.16.zip"; + hash = "sha256-MtB8KorBfeIYQtY/7pqO80/DFZNn07H7OirRkHFZEws="; + } + { + name = "toolsanimationsfork_1.2.6.zip"; + url = "https://moddbcdn.vintagestory.at/toolsanimationsfork__e9cf461ea870e5296d4edd7f85c899b0.zip?dl=toolsanimationsfork_1.2.6.zip"; + hash = "sha256-eilRzUd5T00MqsiOK8GJpynoxEl+RoySyiyz12p1D+8="; + } + { + name = "toolsmith_1.2.17.zip"; + url = "https://moddbcdn.vintagestory.at/toolsmith_1.2.17_7ac73dc4a0c3463a4935fe497b55d87d.zip?dl=toolsmith_1.2.17.zip"; + hash = "sha256-OD1WbuzvT7pux68VSgYdED2IV0ZYQX7kI4Wr9C1tRdQ="; + } + { + name = "vsimgui_1.2.7.zip"; + url = "https://moddbcdn.vintagestory.at/vsimgui_1.2.7_930a5532b150476e25ceef6f716182dd.zip?dl=vsimgui_1.2.7.zip"; + hash = "sha256-J8uH3gIDSpfRVOogUmh6osAVhwvR7Qu3oBuBP6V1WOM="; + } + { + name = "vsroofing_1.5.7.zip"; + url = "https://moddbcdn.vintagestory.at/vsroofing_1.5.7_7220e272a1ee1d1853e27e0a71dfd52d.zip?dl=vsroofing_1.5.7.zip"; + hash = "sha256-znvTScMo1Xi2Uwjl87jJjEfgtZiHuDvDsBqcdj+7Vnc="; + } + { + name = "watersheds_6.4.2.zip"; + url = "https://moddbcdn.vintagestory.at/watersheds_6.4.2_c6aa1cfb5d52b1b28de307ce84717c2c.zip?dl=watersheds_6.4.2.zip"; + hash = "sha256-MAUBVheTEzwm5z1ZKLFunqQrRJdsY4Cs7KHrprwlFuE="; + } + { + name = "windchimes-1.22.0-v1.5.2.zip"; + url = "https://moddbcdn.vintagestory.at/windchimes-1.22.0-v1_ec4d82e30b435f2704c7de371d1c46cb.zip?dl=windchimes-1.22.0-v1.5.2.zip"; + hash = "sha256-X13E0f7LXiwBcbb7L9MOnucBsHgJfMdo8AjU/eX2xpU="; + } +] diff --git a/services/vintagestory-serverconfig.json b/services/vintagestory-serverconfig.json new file mode 100644 index 0000000..d6898de --- /dev/null +++ b/services/vintagestory-serverconfig.json @@ -0,0 +1,384 @@ +{ + "FileEditWarning": "", + "ConfigVersion": "1.10", + "ServerName": "Vintage Story Server", + "ServerUrl": null, + "ServerDescription": null, + "WelcomeMessage": "Welcome {0}, may you survive well and prosper", + "Ip": null, + "Port": 42420, + "Upnp": false, + "UpnpInfiniteLifetime": false, + "CompressPackets": true, + "AdvertiseServer": false, + "LastLaunchMods": null, + "LastLaunchPlaystyle": null, + "MaxClients": 16, + "MaxClientsInQueue": 0, + "WarnClientsAfterAfkSeconds": 0, + "KickClientsAfterAfkSeconds": 0, + "PassTimeWhenEmpty": false, + "MasterserverUrl": "http://masterserver.vintagestory.at/api/v1/servers/", + "ModDbUrl": "https://mods.vintagestory.at/", + "ClientConnectionTimeout": 150, + "EntityDebugMode": false, + "Password": null, + "MapSizeX": 1024000, + "MapSizeY": 256, + "MapSizeZ": 1024000, + "ServerLanguage": "en", + "MaxChunkRadius": 12, + "TickTime": 33.333332, + "SpawnCapPlayerScaling": 0.5, + "BlockTickChunkRange": 5, + "MaxMainThreadBlockTicks": 10000, + "RandomBlockTicksPerChunk": 16, + "BlockTickInterval": 300, + "SkipEveryChunkRow": 0, + "SkipEveryChunkRowWidth": 0, + "Roles": [ + { + "Code": "suvisitor", + "PrivilegeLevel": -1, + "Name": "Survival Visitor", + "Description": "Can only visit this world and chat but not use/place/break anything", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "chat" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 1, + "Color": "Green", + "LandClaimAllowance": 0, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 3, + "AutoGrant": false + }, + { + "Code": "crvisitor", + "PrivilegeLevel": -1, + "Name": "Creative Visitor", + "Description": "Can only visit this world, chat and fly but not use/place/break anything", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "chat" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 2, + "Color": "DarkGray", + "LandClaimAllowance": 0, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 3, + "AutoGrant": false + }, + { + "Code": "limitedsuplayer", + "PrivilegeLevel": -1, + "Name": "Limited Survival Player", + "Description": "Can use/place/break blocks only in permitted areas (priv level -1), create/manage player groups and chat", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "controlplayergroups", + "manageplayergroups", + "chat", + "build", + "useblock", + "attackcreatures", + "attackplayers", + "selfkill" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 1, + "Color": "White", + "LandClaimAllowance": 0, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 3, + "AutoGrant": false + }, + { + "Code": "limitedcrplayer", + "PrivilegeLevel": -1, + "Name": "Limited Creative Player", + "Description": "Can use/place/break blocks in only in permitted areas (priv level -1), create/manage player groups, chat, fly and set his own game mode (= allows fly and change of move speed)", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "controlplayergroups", + "manageplayergroups", + "chat", + "build", + "useblock", + "gamemode", + "freemove", + "attackcreatures", + "attackplayers", + "selfkill" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 2, + "Color": "LightGreen", + "LandClaimAllowance": 0, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 3, + "AutoGrant": false + }, + { + "Code": "suplayer", + "PrivilegeLevel": 0, + "Name": "Survival Player", + "Description": "Can use/place/break blocks in unprotected areas (priv level 0), create/manage player groups and chat. Can claim an area of up to 8 chunks.", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "controlplayergroups", + "manageplayergroups", + "chat", + "areamodify", + "build", + "useblock", + "attackcreatures", + "attackplayers", + "selfkill" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 1, + "Color": "White", + "LandClaimAllowance": 262144, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 3, + "AutoGrant": false + }, + { + "Code": "crplayer", + "PrivilegeLevel": 100, + "Name": "Creative Player", + "Description": "Can use/place/break blocks in all areas (priv level 100), create/manage player groups, chat, fly and set his own game mode (= allows fly and change of move speed). Can claim an area of up to 40 chunks.", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "controlplayergroups", + "manageplayergroups", + "chat", + "areamodify", + "build", + "useblock", + "gamemode", + "freemove", + "attackcreatures", + "attackplayers", + "selfkill" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 2, + "Color": "LightGreen", + "LandClaimAllowance": 1310720, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 6, + "AutoGrant": false + }, + { + "Code": "sumod", + "PrivilegeLevel": 200, + "Name": "Survival Moderator", + "Description": "Can use/place/break blocks everywhere (priv level 200), create/manage own/other player groups, chat, kick/ban players and do serverwide announcements. Can claim an area of up to 4 chunks.", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "controlplayergroups", + "manageplayergroups", + "manageotherplayergroups", + "chat", + "areamodify", + "build", + "useblock", + "buildblockseverywhere", + "useblockseverywhere", + "kick", + "ban", + "announce", + "readlists", + "attackcreatures", + "attackplayers", + "selfkill" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 1, + "Color": "Cyan", + "LandClaimAllowance": 1310720, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 60, + "AutoGrant": false + }, + { + "Code": "crmod", + "PrivilegeLevel": 500, + "Name": "Creative Moderator", + "Description": "Can use/place/break blocks everywhere (priv level 500), create/manage own/other player groups, chat, kick/ban players, fly and set his own or other players game modes (= allows fly and change of move speed). Can claim an area of up to 40 chunks.", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "controlplayergroups", + "manageplayergroups", + "manageotherplayergroups", + "chat", + "areamodify", + "build", + "useblock", + "buildblockseverywhere", + "useblockseverywhere", + "kick", + "ban", + "gamemode", + "freemove", + "commandplayer", + "announce", + "readlists", + "attackcreatures", + "attackplayers", + "selfkill" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 2, + "Color": "Cyan", + "LandClaimAllowance": 1310720, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 60, + "AutoGrant": false + }, + { + "Code": "admin", + "PrivilegeLevel": 99999, + "Name": "Admin", + "Description": "Has all privileges, including giving other players admin status.", + "DefaultSpawn": null, + "ForcedSpawn": null, + "Privileges": [ + "build", + "useblock", + "buildblockseverywhere", + "useblockseverywhere", + "attackplayers", + "attackcreatures", + "freemove", + "gamemode", + "pickingrange", + "chat", + "kick", + "ban", + "whitelist", + "setwelcome", + "announce", + "readlists", + "give", + "areamodify", + "setspawn", + "controlserver", + "tp", + "time", + "grantrevoke", + "root", + "commandplayer", + "controlplayergroups", + "manageplayergroups", + "selfkill", + "manageotherplayergroups" + ], + "RuntimePrivileges": [], + "DefaultGameMode": 1, + "Color": "LightBlue", + "LandClaimAllowance": 2147483647, + "LandClaimMinSize": { + "X": 5, + "Y": 5, + "Z": 5 + }, + "LandClaimMaxAreas": 99999, + "AutoGrant": true + } + ], + "DefaultRoleCode": "suplayer", + "ModPaths": [ + "Mods" + ], + "AntiAbuse": 0, + "WorldConfig": { + "Seed": null, + "SaveFileLocation": null, + "WorldName": "A new world", + "AllowCreativeMode": true, + "PlayStyle": "surviveandbuild", + "PlayStyleLangCode": "surviveandbuild-bands", + "WorldType": "standard", + "WorldConfiguration": null, + "MapSizeY": null, + "CreatedByPlayerName": null, + "DisabledMods": null, + "RepairMode": false + }, + "NextPlayerGroupUid": 10, + "GroupChatHistorySize": 20, + "MaxOwnedGroupChannelsPerUser": 10, + "OnlyWhitelisted": false, + "WhitelistMode": 0, + "VerifyPlayerAuth": true, + "DefaultSpawn": null, + "AllowPvP": true, + "AllowFireSpread": true, + "AllowFallingBlocks": true, + "HostedMode": false, + "HostedModeAllowMods": false, + "VhIdentifier": null, + "StartupCommands": null, + "RepairMode": false, + "AnalyzeMode": false, + "CorruptionProtection": true, + "RegenerateCorruptChunks": false, + "ChatRateLimitMs": 1000, + "DieBelowDiskSpaceMb": 400, + "ModIdBlackList": null, + "ModIdWhiteList": null, + "ServerIdentifier": "fc7176da-aa9a-4a73-aec9-5ddf8f3eabaa", + "LogBlockBreakPlace": false, + "LogFileSplitAfterLine": 500000, + "DieAboveErrorCount": 100000, + "LoginFloodProtection": false, + "TemporaryIpBlockList": false, + "DisableModSafetyCheck": false, + "DisableAutoRemap": false, + "DieAboveMemoryUsageMb": 50000 +} diff --git a/services/vintagestory-worldconfig.json b/services/vintagestory-worldconfig.json new file mode 100644 index 0000000..89d7a94 --- /dev/null +++ b/services/vintagestory-worldconfig.json @@ -0,0 +1,68 @@ +{ + "gameMode": "survival", + "playerlives": "-1", + "startingClimate": "hot", + "spawnRadius": "0", + "graceTimer": "0", + "deathPunishment": "keep", + "droppedItemsTimer": "600", + "seasons": "enabled", + "daysPerMonth": "6", + "harshWinters": "true", + "blockGravity": "sandgravel", + "caveIns": "off", + "allowFallingBlocks": true, + "allowFireSpread": false, + "lightningFires": false, + "allowUndergroundFarming": false, + "noLiquidSourceTransport": false, + "playerHealthPoints": "15", + "playerHealthRegenSpeed": "1", + "playerHungerSpeed": "1", + "lungCapacity": "40000", + "bodyTemperatureResistance": "0", + "playerMoveSpeed": "1.75", + "creatureHostility": "aggressive", + "creatureStrength": "1", + "creatureSwimSpeed": "2", + "foodSpoilSpeed": "1", + "saplingGrowthRate": "0.25", + "toolDurability": "1", + "toolMiningSpeed": "1", + "propickNodeSearchRadius": "6", + "microblockChiseling": "stonewood", + "allowCoordinateHud": true, + "allowMap": true, + "colorAccurateWorldmap": true, + "loreContent": true, + "clutterObtainable": "ifrepaired", + "temporalStability": false, + "temporalStorms": "rare", + "tempstormDurationMul": "1", + "temporalRifts": "off", + "temporalGearRespawnUses": "-1", + "temporalStormSleeping": "0", + "worldClimate": "realistic", + "landcover": "0.7", + "oceanscale": "3", + "upheavelCommonness": "0", + "geologicActivity": "0.4", + "landformScale": "3", + "worldWidth": "1024000", + "worldLength": "1024000", + "worldEdge": "traversable", + "polarEquatorDistance": "50000", + "storyStructuresDistScaling": "1", + "globalTemperature": "1", + "globalPrecipitation": "2", + "globalForestation": "0", + "globalDepositSpawnRate": "1.6", + "surfaceCopperDeposits": "0.5", + "surfaceTinDeposits": "0.25", + "snowAccum": "true", + "allowLandClaiming": false, + "classExclusiveRecipes": false, + "auctionHouse": false, + "playstyle": "surviveandbuild", + "worldHeight": 384 +} diff --git a/services/vintagestory.nix b/services/vintagestory.nix new file mode 100644 index 0000000..595f4be --- /dev/null +++ b/services/vintagestory.nix @@ -0,0 +1,128 @@ +{ + lib, + pkgs, + nixpkgs-vintagestory, + ... +}: + +let + # Take vintagestory (1.22.3) from our rev-pinned nixpkgs input so the + # server version only changes when we deliberately bump the rev in + # flake.nix — clients must match the server version to connect. + vsPkgs = import nixpkgs-vintagestory { + system = "x86_64-linux"; + config.allowUnfree = true; + }; + vintagestory = vsPkgs.vintagestory; + + mods = map (m: pkgs.fetchurl { inherit (m) name url hash; }) (import ./vintagestory-mods.nix); + modDir = pkgs.linkFarm "vintagestory-mods" ( + map (drv: { + inherit (drv) name; + path = drv; + }) mods + ); + + # World settings only apply when the world is first created — the server + # bakes them into the save file, so editing these later does nothing for + # an existing world (delete the save to regenerate). + # + # The seeded serverconfig.json must be COMPLETE, not just a WorldConfig + # fragment: the server only generates its default Roles/etc. when no file + # exists at all, so a partial file leaves DefaultRoleCode pointing at a + # role that isn't defined and the server kills itself on start. So we + # start from the canonical default (captured from this exact server + # version into vintagestory-serverconfig.json) and merge our settings in. + worldConfiguration = builtins.fromJSON (builtins.readFile ./vintagestory-worldconfig.json); + baseServerConfig = builtins.fromJSON (builtins.readFile ./vintagestory-serverconfig.json); + serverConfig = lib.recursiveUpdate baseServerConfig { + MapSizeY = worldConfiguration.worldHeight; + WorldConfig = { + Seed = "534793158"; + WorldName = "ellie.town"; + PlayStyle = worldConfiguration.playstyle; + MapSizeY = worldConfiguration.worldHeight; + SaveFileLocation = "/var/lib/vintagestory/Saves/ellie.town.vcdbs"; + WorldConfiguration = worldConfiguration; + }; + }; + initialServerConfig = pkgs.writeText "vintagestory-serverconfig.json" ( + builtins.toJSON serverConfig + ); + + # Access policy, re-asserted on every start (see preStart). Unlike world + # settings, these must apply to the already-created world, and the server + # owns serverconfig.json at runtime — so we merge just these keys into the + # live file each start rather than seeding them once. + # WhitelistMode: 0 = default (on for dedicated servers), 1 = off, 2 = on + serverPolicy = pkgs.writeText "vintagestory-policy.json" ( + builtins.toJSON { + WhitelistMode = 1; + Password = "noxalia"; + } + ); +in +{ + # Friends connect directly, so accept game traffic on all interfaces + networking.firewall.allowedTCPPorts = [ 42420 ]; + networking.firewall.allowedUDPPorts = [ 42420 ]; + + users.users.vintagestory = { + isSystemUser = true; + group = "vintagestory"; + home = "/var/lib/vintagestory"; + }; + users.groups.vintagestory = { }; + + systemd.services.vintagestory = { + description = "Vintage Story dedicated server"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment = { + # dotnet wants a writable HOME for its temp/config files + HOME = "/var/lib/vintagestory"; + }; + + # The server rewrites serverconfig.json at runtime (it's mutable state), + # so seed the full config once on first start, then re-assert just our + # access-policy keys on every start so repo changes reach an existing + # world. World-creation settings in the seed are deliberately NOT + # re-asserted — they only matter at creation and the save owns them now. + preStart = '' + cfg=/var/lib/vintagestory/serverconfig.json + if [ ! -e "$cfg" ]; then + install -m 0600 ${initialServerConfig} "$cfg" + fi + tmp=$(mktemp -p /var/lib/vintagestory) + # Only overwrite the live config if the merge actually succeeds, so a + # jq error can never truncate serverconfig.json to an empty file. + if ${pkgs.jq}/bin/jq -s '.[0] * .[1]' "$cfg" ${serverPolicy} > "$tmp"; then + install -m 0600 "$tmp" "$cfg" + fi + rm -f "$tmp" + ''; + + serviceConfig = { + ExecStart = "${vintagestory}/bin/vintagestory-server --dataPath /var/lib/vintagestory --addModPath ${modDir}"; + User = "vintagestory"; + Group = "vintagestory"; + StateDirectory = "vintagestory"; + StateDirectoryMode = "0700"; + WorkingDirectory = "/var/lib/vintagestory"; + Restart = "on-failure"; + RestartSec = "10s"; + + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + NoNewPrivileges = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + LockPersonality = true; + }; + }; +}