Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ As of currently, it packages:
- Various tools
- `nix-modrinth-prefetch`
- `fetchPackwizModpack`
- `fetchModrinthModpack`

Check out this video by vimjoyer that provides a brief overview of how to use the flake: https://youtu.be/Fph7SMldxpI

Expand Down Expand Up @@ -249,6 +250,36 @@ in

**Note**: Using `manifest`, by default, will cause [IFD](https://nixos.wiki/wiki/Import_From_Derivation). If you want to avoid IFD while still having access to `manifest`, simply pass a `manifestHash` to the `fetchPackwizModpack` function, it will then fetch the manifest through `builtins.fetchurl`.

### `fetchModrinthModpack`

[Source](./pkgs/tools/fetchModrinthModpack)

This function packages a Modrinth `.mrpack` modpack from either a URL, a local `.mrpack` file, or a local source directory containing `index.json` and optional overrides.

```nix
let
modpack = pkgs.fetchModrinthModpack {
url = "https://cdn.modrinth.com/data/PROJECT_ID/versions/VERSION_ID/modpack.mrpack";
packHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
side = "server";
};
in
{
services.minecraft-servers.servers.cool-modpack = {
enable = true;
package = pkgs.fabricServers.fabric-1_21_5.override { loaderVersion = "0.16.14"; };
symlinks = {
"mods" = "${modpack}/mods";
};
files = {
"config" = "${modpack}/config";
};
};
}
```

The resulting derivation contains downloaded pack files and applied overrides. Like `fetchPackwizModpack`, it also exposes a `manifest` attribute and an `addFiles` helper.

### Others

All of these packages are also available under `packages`, not just `legacyPackages`.
Expand Down
188 changes: 188 additions & 0 deletions pkgs/tools/fetchModrinthModpack/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
{
lib,
stdenvNoCC,
jq,
moreutils,
curl,
cacert,
unzip,
coreutils,
}:

let
fetchModrinthModpack =
{
# Provide a path to the modpack source directory (containing index.json),
# a local .mrpack archive, or a URL to a .mrpack archive.
src ? null,
url ? null,
packHash ? "",
# Either 'server', 'client' or 'both' (to get all files)
side ? "server",
...
}@args:
let
srcNull = src == null;
urlNull = url == null;
srcPath = if srcNull then "" else src;
urlPath = if urlNull then "" else url;
drv = fetchModrinthModpack args;
in

assert lib.assertMsg (
srcNull != urlNull # equivalent of (src != null) xor (url != null)
) "Either 'src' or 'url' must be provided to fetchModrinthModpack";

assert lib.assertMsg (builtins.elem side [
"server"
"client"
"both"
]) "'side' must be one of: server, client, both";

stdenvNoCC.mkDerivation (
{
pname = args.pname or "modrinth-pack";
version = args.version or "";

dontUnpack = true;

buildInputs = [
jq
moreutils
curl
cacert
unzip
coreutils
];

buildPhase = ''
set -euo pipefail
runHook preBuild

mkdir -p pack-src

if [ "${if !urlNull then "1" else "0"}" = "1" ]; then
curl -L "${urlPath}" > modpack.mrpack
unzip -q modpack.mrpack -d pack-src
elif [ -d "${srcPath}" ]; then
cp -r "${srcPath}"/. pack-src/
else
unzip -q "${srcPath}" -d pack-src
fi

test -f pack-src/index.json

while IFS= read -r file; do
if [ "${side}" != "both" ]; then
envState=$(echo "$file" | jq -r --arg side "${side}" '.env[$side] // "required"')
if [ "$envState" = "unsupported" ]; then
continue
fi
fi

path=$(echo "$file" | jq -r '.path')
url=$(echo "$file" | jq -r '.downloads[0]')
mkdir -p "$(dirname "$path")"
curl -L "$url" > "$path"

if echo "$file" | jq -e '.hashes.sha512 != null' > /dev/null; then
expected=$(echo "$file" | jq -r '.hashes.sha512')
actual=$(${coreutils}/bin/sha512sum "$path" | cut -d' ' -f1)
elif echo "$file" | jq -e '.hashes.sha1 != null' > /dev/null; then
expected=$(echo "$file" | jq -r '.hashes.sha1')
actual=$(${coreutils}/bin/sha1sum "$path" | cut -d' ' -f1)
else
echo "No supported hash for '$path' (supported: sha512, sha1)" >&2
exit 1
fi

if [ "$actual" != "$expected" ]; then
echo "Hash mismatch for '$path'" >&2
echo "expected: $expected" >&2
echo "actual: $actual" >&2
exit 1
fi
done < <(jq -c '.files[]' pack-src/index.json)

if [ -d pack-src/overrides ]; then
cp -r pack-src/overrides/. .
fi

if [ "${side}" = "server" ] && [ -d pack-src/server-overrides ]; then
cp -r pack-src/server-overrides/. .
fi

if [ "${side}" = "client" ] && [ -d pack-src/client-overrides ]; then
cp -r pack-src/client-overrides/. .
fi

if [ "${side}" = "both" ]; then
if [ -d pack-src/server-overrides ]; then
cp -r pack-src/server-overrides/. .
fi
if [ -d pack-src/client-overrides ]; then
cp -r pack-src/client-overrides/. .
fi
fi

# Keep the source manifest in output for passthru consumers.
cp pack-src/index.json ./index.json

runHook postBuild
'';

installPhase = ''
runHook preInstall

# Fix non-determinism
rm -rf env-vars
jq -Sc '.' index.json | sponge index.json

mkdir -p "$out"
cp * -r "$out/"

runHook postInstall
'';

passthru = {
# Modrinth index manifest as a nix expression.
manifest = builtins.fromJSON (builtins.readFile "${drv}/index.json");

# Adds an attribute set of files to the derivation.
# Useful to add server-specific mods not part of the pack.
addFiles =
files:
stdenvNoCC.mkDerivation {
inherit (drv) pname version;
src = null;
dontUnpack = true;
dontConfig = true;
dontBuild = true;
dontFixup = true;

installPhase = ''
cp -as "${drv}" $out
chmod u+w -R $out
''
+ lib.concatLines (
lib.mapAttrsToList (name: file: ''
mkdir -p "$out/$(dirname "${name}")"
cp -as "${file}" "$out/${name}"
'') files
);

passthru = { inherit (drv) manifest; };
meta = drv.meta or { };
};
};

dontFixup = true;

outputHashMode = "recursive";
outputHashAlgo = "sha256";
outputHash = packHash;
}
// args
);
in
fetchModrinthModpack
25 changes: 25 additions & 0 deletions tests/modrinth-modpack-from-src/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
fetchModrinthModpack,
stdenvNoCC,
}:
let
pack = fetchModrinthModpack {
src = ./sample-pack;
packHash = "sha256-OAJrZEVTZx1QZe2ubrLfK/XvRusfIN8cWMbj21TYhms=";
};
in
stdenvNoCC.mkDerivation {
name = "modrinth-modpack-from-src-check";
doCheck = true;
phases = [
"checkPhase"
"installPhase"
];
checkPhase = ''
set -euo pipefail
test -f '${pack}/index.json'
test -f '${pack}/mods/lithium-fabric-0.16.2+mc1.21.5.jar'
test -f '${pack}/config/server.properties'
'';
installPhase = "mkdir $out";
}
27 changes: 27 additions & 0 deletions tests/modrinth-modpack-from-src/sample-pack/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"formatVersion": 1,
"game": "minecraft",
"versionId": "sample-pack-1.0.0",
"name": "sample-pack",
"summary": "Sample Modrinth modpack for nix-minecraft tests",
"files": [
{
"path": "mods/lithium-fabric-0.16.2+mc1.21.5.jar",
"hashes": {
"sha512": "09a68051504bb16069dd6af8901f2bbeadfd08ad5353d8bcc0c4784e814fb293d9197b4fb0a8393be1f2db003cd987a9e4b98391bbe18c50ae181dace20c2fa4"
},
"downloads": [
"https://cdn.modrinth.com/data/gvQqBUqZ/versions/VWYoZjBF/lithium-fabric-0.16.2%2Bmc1.21.5.jar"
],
"fileSize": 769793,
"env": {
"client": "required",
"server": "required"
}
}
],
"dependencies": {
"minecraft": "1.21.5",
"fabric-loader": "0.16.14"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
motd=nix-minecraft test