From fedc2f140efe92fa7e16a726187e2c2e01d8ef73 Mon Sep 17 00:00:00 2001 From: Fernando Rodrigues Date: Wed, 30 Oct 2024 08:00:14 +1000 Subject: [PATCH] starsector: add packaging infrastructure for parsing mod files This adds a the mkStarsectorMod helper function. It can produce Starsector mod packages that are then passed to linkFarm to be processed by the game. Signed-off-by: Fernando Rodrigues --- pkgs/by-name/st/starsector/mods.nix | 110 +++++++++++++++++++++++++ pkgs/by-name/st/starsector/package.nix | 47 ++++++++++- 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 pkgs/by-name/st/starsector/mods.nix diff --git a/pkgs/by-name/st/starsector/mods.nix b/pkgs/by-name/st/starsector/mods.nix new file mode 100644 index 00000000000000..bdc7650e3cdd45 --- /dev/null +++ b/pkgs/by-name/st/starsector/mods.nix @@ -0,0 +1,110 @@ +{ + lib, + stdenvNoCC, + fetchzip, + runCommand, + unrar, + starsector, + jq, + dos2unix, +}: + +let + semiOptionalInput = + input: bool: + "mkStarsectorMod needs an '${input}' attribute, or 'isStandardSrc' set to '${toString bool}'."; + mkStarsectorMod = + { + pname, + version, + isStandardSrc ? true, + src ? if isStandardSrc then "" else throw semiOptionalInput "src" true, + url ? if !isStandardSrc then "" else throw semiOptionalInput "url" false, + hash ? if !isStandardSrc then "" else throw semiOptionalInput "hash" false, + author ? "", + prettyName ? "", + summary ? "", + description ? "", + forumURL ? "", + }: + + stdenvNoCC.mkDerivation (finalAttrs: { + inherit version pname; + + src = + if isStandardSrc then + fetchzip { + inherit url hash; + nativeBuildInputs = lib.optional (lib.hasSuffix ".rar" url) unrar; + postFetch = '' + if test -e "$out"/mod_info.json; then + return 0; else + results="$(find $out -type d -exec test -e '{}'/mod_info.json \; -print)" + if [ "$(echo "$results" | wc -l)" = 1 ]; then + mv "$results"/* . && rm -rf "$results"; else; + echo "Starsector mod: '${pname}' has more than one mod_info.json." && exit 1 + fi + fi + ''; + } + else + src; + + dontConfigure = true; + dontBuild = true; + + installPhase = "mkdir $out && cp -prvd * $out"; + + passthru = { + inherit author prettyName forumURL; + tests = { + gameVersionMatches = + runCommand "${finalAttrs.pname}-gameVersionMatchTest" + { + nativeBuildInputs = [ + jq + dos2unix + ]; + } + '' + dos2unix ${finalAttrs.finalPackage}/mod_info.json + modVersion="$(jq -n -f ${finalAttrs.finalPackage}/mod_info.json | jq -cMj .gameVersion)" + gameVersion="${starsector.version}" + if [ "$gameVersion" = "$modVersion" ]; then + echo "Mod version matches!" && echo "Success: ${finalAttrs.pname}" >> $out + else + echo "Mod version did not match! Tried comparing '$gameVersion' to '$modVersion'." + exit 1 + fi + ''; + }; + }; + + meta = { + description = "Starsector Mod: ${prettyName}"; + longDescription = + (if (lib.hasPrefix summary description) then description else "${summary}\n${description}") + + "\n- by ${author}."; + homepage = forumURL; + sourceProvenance = lib.optional (lib.pathIsDirectory ( + finalAttrs.src + /jars + )) lib.sourceTypes.binaryBytecode; + inherit (starsector.meta) + license + maintainers + platforms + badPlatforms + ; + }; + }); +in +{ + inherit mkStarsectorMod; +} + +# TODO: Somehow parse the very non-standard mess that are +# Starsector mods. There are mod packages that include two mods +# in a single zip, some mods just straight up use invalid JSON, +# while others can only be accessed through impermanent source +# directories and/or lack a versioned URL. Oh, did I mention the +# lack of consistent line endings? diff --git a/pkgs/by-name/st/starsector/package.nix b/pkgs/by-name/st/starsector/package.nix index fb2b51a06a4209..dc9615f01222e5 100644 --- a/pkgs/by-name/st/starsector/package.nix +++ b/pkgs/by-name/st/starsector/package.nix @@ -1,5 +1,7 @@ { lib, + callPackage, + linkFarmFromDrvs, fetchzip, libGL, makeWrapper, @@ -13,10 +15,24 @@ curl, gnugrep, common-updater-scripts, + + starsectorMods ? [ ], }: +assert lib.assertMsg ( + builtins.isList starsectorMods + && builtins.all (x: x) ( + map (n: lib.isDerivation (builtins.elemAt starsectorMods n)) ( + lib.range 0 ((builtins.length starsectorMods) - 1) + ) + ) +) "The definition for 'starsectorMods' is not of type 'list of derivation'."; + let openjdk = openjdk8; + + mkStarsectorMod = lib.recurseIntoAttrs (callPackage ./mods.nix { }); + mergedModPath = linkFarmFromDrvs "mergedModPath" starsectorMods; in stdenv.mkDerivation (finalAttrs: { @@ -90,7 +106,11 @@ stdenv.mkDerivation (finalAttrs: { --replace-fail "./jre_linux/bin/java" "${openjdk}/bin/java" \ --replace-fail "./native/linux" "$out/share/starsector/native/linux" \ '' - # We also point the mod, screenshot, and save directories to $XDG_DATA_HOME. + # Set the mods folder to the merged store directory if we're using the mod plugins. + + lib.optionalString (starsectorMods != [ ]) '' + --replace-fail "./mods" "${mergedModPath}" \ + '' + # We also point the mod (if `starsectorMods == [ ]`), screenshot, and save directories to $XDG_DATA_HOME. + '' --replace-fail "=." "=\''${XDG_DATA_HOME:-\$HOME/.local/share}/starsector" \ '' @@ -120,10 +140,35 @@ stdenv.mkDerivation (finalAttrs: { }; text = builtins.readFile ./update.sh; }; + inherit mkStarsectorMod; }; meta = { description = "Open-world, single-player space combat, roleplaying, exploration, and economic game"; + longDescription = '' + Starsector can be extended with third-party modifications from the [Fractal Softworks Forum](https://fractalsoftworks.com/forum/index.php?&board=8.0). + By default, mods can be imperatively installed in `$HOME/.local/share/starsector/mods`, but you can also override the `starsector` derivation to install mods declaratively, like so: + ```nix + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + (starsector.override { + starsectorMods = [ + (starsector.mkStarsectorMod { + pname = "myFavouriteMod"; + version = "1.0.0"; + url = "https://somefilerepository.example/yourMod/modFile.zip"; + hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }) + # ... more mod derivations. + ]; + }) + ]; + } + ``` + This will create a merged store path with the specified mods, and Nix will handle fetching and installing. + **Note:** as the mods will be stored inside the read-only /nix/store, they will not be able to write files during runtime, which may cause game crashes if they attempt to do anyway. + ''; homepage = "https://fractalsoftworks.com"; downloadPage = finalAttrs.meta.homepage + "/preorder"; changelog = finalAttrs.meta.homepage + "/blog";