{ lib, stdenv, fetchurl, busybox, coreutils, jre_headless, runtimeShell , mods ? null, modConfig ? null, defaultconfigs ? null }: let name = "forge-server"; version = "1.16.5-36.2.2"; mirror = "https://files.minecraftforge.net/maven/net/minecraftforge/forge"; src = fetchurl { url = "${mirror}/${version}/forge-${version}-installer.jar"; # Forge doesn't seem to like newer shas sha1 = "e97821e5431bdcaa46e12048769922e2cdb5e2e1"; }; unpackCmd = "mkdir -p src; cp $curSrc src/forge-${version}-installer.jar"; nativeBuildInputs = [ jre_headless ]; # Somewhat evil pre-install step to run through the network # dependency resolution forge needs. This is also common for gradle # projects, so I think this is ok-ish here, though ideally I'd # identify all the dependencies and package them as well. deps = stdenv.mkDerivation { name = "${name}-deps"; inherit src unpackCmd nativeBuildInputs; buildPhase = '' java -jar forge-${version}-installer.jar --installServer installation ''; installPhase = '' mkdir -p $out # The installer will patch the Minecraft server jar in some # fashion, and include it in "libraries". This'd be fine, if the # patch was bit-for-bit reproducible. Sadly it is not, so we # defer this to the *real* package build. rm -r installation/libraries/net/minecraft/ cp -r installation/* $out ''; outputHashAlgo = "sha256"; outputHashMode = "recursive"; outputHash = "lHAuKgwD2gzTb+n3pLgpXsDGYSLF/EYlGy0TfCoU5uQ="; }; in stdenv.mkDerivation rec { inherit name version src unpackCmd nativeBuildInputs; buildPhase = '' mkdir -p installation # Take the input deps and patch the Minecraft server jar into our # libraries to create the package. cp -rv ${deps}/* installation/ chmod -R +w installation/ java -jar forge-${version}-installer.jar --offline --installServer installation/ ''; installPhase = '' mkdir -p $out/{bin,lib/forge} cp -r installation/{forge-${version}.jar,libraries} $out/lib/forge/ cat > $out/bin/forge-server << EOF #!${runtimeShell} set -eu # Delete any previous mods directory so that it can be updated ${busybox}/bin/rm -fr mods ${lib.optionalString (mods != null) '' # Copy the specified mods into the directory. Note that, sadly, # forge doesn't support symlinks here. ${busybox}/bin/mkdir -p mods ${busybox}/bin/cp -r '${mods}/mods/'*.jar mods ''} # Delete any previous config directories so that they can be updated ${busybox}/bin/rm -fr config defaultconfigs ${lib.optionalString (modConfig != null) '' # Copy the specified configs into the directory. Forge (and # mods) will try to write here, so we cannot symlink. ${busybox}/bin/mkdir -p config ${busybox}/bin/cp -r '${modConfig}'/* config ${busybox}/bin/chmod -R u+w config ''} ${lib.optionalString (defaultconfigs != null) '' # Copy the specified configs into the directory. Forge (and # mods) will try to write here, so we cannot symlink. ${busybox}/bin/mkdir -p defaultconfigs ${busybox}/bin/cp -r '${defaultconfigs}'/* defaultconfigs ${busybox}/bin/chmod -R u+w defaultconfigs ''} exec ${jre_headless}/bin/java \$@ -jar $out'/lib/forge/forge-${version}.jar' nogui EOF chmod +x $out/bin/forge-server ''; meta = with lib; { description = "Forge Minecraft Server"; homepage = "https://files.minecraftforge.net/"; # Forge itself is open source, but this package pulls in # minecraft. license = licenses.unfreeRedistributable; platforms = platforms.unix; }; }