Unverified Commit d3563eab authored by Sandro Jäckel's avatar Sandro Jäckel Committed by GitHub
Browse files

Merge pull request #276292 from MayNiklas/papermc-multi-version

parents 921495ab ef321a0e
Loading
Loading
Loading
Loading
+14 −49
Original line number Diff line number Diff line
{
  lib,
  stdenvNoCC,
  fetchurl,
  jre,
  makeBinaryWrapper,
}:
stdenvNoCC.mkDerivation (finalAttrs: {
  pname = "papermc";
  version = "1.20.4.435";

  src =
{ callPackage, lib, ... }:
let
      mcVersion = lib.versions.pad 3 finalAttrs.version;
      buildNum = builtins.elemAt (lib.splitVersion finalAttrs.version) 3;
  versions = lib.importJSON ./versions.json;
  latestVersion = lib.last (builtins.sort lib.versionOlder (builtins.attrNames versions));
  escapeVersion = builtins.replaceStrings [ "." ] [ "_" ];
  packages = lib.mapAttrs'
    (version: value: {
      name = "papermc-${escapeVersion version}";
      value = callPackage ./derivation.nix { inherit (value) version hash; };
    })
    versions;
in
    fetchurl {
      url = "https://papermc.io/api/v2/projects/paper/versions/${mcVersion}/builds/${buildNum}/downloads/paper-${mcVersion}-${buildNum}.jar";
      hash = "sha256-NrIsYLoAAWORw/S26NDFjYBVwpNITJxuWGZow3696wM=";
    };

  installPhase = ''
    runHook preInstall

    install -D $src $out/share/papermc/papermc.jar

    makeWrapper ${lib.getExe jre} "$out/bin/minecraft-server" \
      --append-flags "-jar $out/share/papermc/papermc.jar nogui"

    runHook postInstall
  '';

  nativeBuildInputs = [
    makeBinaryWrapper
  ];

  dontUnpack = true;
  preferLocalBuild = true;
  allowSubstitutes = false;

  meta = {
    description = "High-performance Minecraft Server";
    homepage = "https://papermc.io/";
    sourceProvenance = with lib.sourceTypes; [ binaryBytecode ];
    license = lib.licenses.gpl3Only;
    platforms = lib.platforms.unix;
    maintainers = with lib.maintainers; [ aaronjanse neonfuz ];
    mainProgram = "minecraft-server";
  };
lib.recurseIntoAttrs (packages // {
  papermc = builtins.getAttr "papermc-${escapeVersion latestVersion}" packages;
})
+50 −0
Original line number Diff line number Diff line
{ lib, stdenvNoCC, fetchurl, makeBinaryWrapper, jre, version, hash }:

stdenvNoCC.mkDerivation {
  pname = "papermc";
  inherit version;

  src =
    let
      version-split = lib.strings.splitString "-" version;
      mcVersion = builtins.elemAt version-split 0;
      buildNum = builtins.elemAt version-split 1;
    in
    fetchurl {
      url = "https://papermc.io/api/v2/projects/paper/versions/${mcVersion}/builds/${buildNum}/downloads/paper-${version}.jar";
      inherit hash;
    };

  installPhase = ''
    runHook preInstall

    install -D $src $out/share/papermc/papermc.jar

    makeWrapper ${lib.getExe jre} "$out/bin/minecraft-server" \
      --append-flags "-jar $out/share/papermc/papermc.jar nogui"

    runHook postInstall
  '';

  nativeBuildInputs = [
    makeBinaryWrapper
  ];

  dontUnpack = true;
  preferLocalBuild = true;
  allowSubstitutes = false;

  passthru = {
    updateScript = ./update.py;
  };

  meta = {
    description = "High-performance Minecraft Server";
    homepage = "https://papermc.io/";
    sourceProvenance = with lib.sourceTypes; [ binaryBytecode ];
    license = lib.licenses.gpl3Only;
    platforms = lib.platforms.unix;
    maintainers = with lib.maintainers; [ aaronjanse neonfuz MayNiklas ];
    mainProgram = "minecraft-server";
  };
}
+145 −0
Original line number Diff line number Diff line
#!/usr/bin/env nix-shell
#! nix-shell -i python -p "python3.withPackages (ps: with ps; [ps.requests ])"

import hashlib
import base64
import json

import requests


class Version:
    def __init__(self, name: str):
        self.name: str = name
        self.hash: str | None = None
        self.build_number: int | None = None

    @property
    def full_name(self):
        v_name = f"{self.name}-{self.build_number}"

        # this will probably never happen because the download of a build with NoneType in URL would fail
        if not self.name or not self.build_number:
            print(f"Warning: version '{v_name}' contains NoneType!")

        return v_name


class VersionManager:
    def __init__(self, base_url: str = "https://api.papermc.io/v2/projects/paper"):
        self.versions: list[Version] = []
        self.base_url: str = base_url

    def fetch_versions(self, not_before_minor_version: int = 18):
        """
        Fetch all versions after given minor release
        """

        response = requests.get(self.base_url)

        try:
            response.raise_for_status()

        except requests.exceptions.HTTPError as e:
            print(e)
            return

        # we only want versions that are no pre-releases
        release_versions = filter(
            lambda v_name: 'pre' not in v_name, response.json()["versions"])

        for version_name in release_versions:

            # split version string, convert to list ot int
            version_split = version_name.split(".")
            version_split = list(map(int, version_split))

            # check if version is higher than 1.<not_before_sub_version>
            if (version_split[0] > 1) or (version_split[0] == 1 and version_split[1] >= not_before_minor_version):
                self.versions.append(Version(version_name))

    def fetch_latest_version_builds(self):
        """
        Set latest build number to each version
        """

        for version in self.versions:
            url = f"{self.base_url}/versions/{version.name}"
            response = requests.get(url)

            # check that we've got a good response
            try:
                response.raise_for_status()

            except requests.exceptions.HTTPError as e:
                print(e)
                return

            # the highest build in response.json()['builds']:
            latest_build = response.json()['builds'][-1]
            version.build_number = latest_build

    def generate_version_hashes(self):
        """
        Generate and set the hashes for all registered versions (versions will are downloaded to memory)
        """

        for version in self.versions:
            url = f"{self.base_url}/versions/{version.name}/builds/{version.build_number}/downloads/paper-{version.full_name}.jar"
            version.hash = self.download_and_generate_sha256_hash(url)

    def versions_to_json(self):
        return json.dumps(
            {version.name: {'hash': version.hash, 'version': version.full_name}
                for version in self.versions},
            indent=4
        )

    def write_versions(self, file_name: str):
        """ write all processed versions to json """
        # save json to versions.json
        with open(file_name, 'w') as f:
            f.write(self.versions_to_json() + "\n")

    @staticmethod
    def download_and_generate_sha256_hash(url: str) -> str | None:
        """
        Fetch the tarball from the given URL.
        Then generate a sha256 hash of the tarball.
        """

        try:
            # Download the file from the URL
            response = requests.get(url)
            response.raise_for_status()

        except requests.exceptions.RequestException as e:
            print(f"Error: {e}")
            return None

        # Create a new SHA-256 hash object
        sha256_hash = hashlib.sha256()

        # Update the hash object with chunks of the downloaded content
        for byte_block in response.iter_content(4096):
            sha256_hash.update(byte_block)

        # Get the hexadecimal representation of the hash
        hash_value = sha256_hash.digest()

        # Encode the hash value in base64
        base64_hash = base64.b64encode(hash_value).decode('utf-8')

        # Format it as "sha256-{base64_hash}"
        sri_representation = f"sha256-{base64_hash}"

        return sri_representation


if __name__ == '__main__':
    version_manager = VersionManager()

    version_manager.fetch_versions()
    version_manager.fetch_latest_version_builds()
    version_manager.generate_version_hashes()
    version_manager.write_versions(file_name="versions.json")
+50 −0
Original line number Diff line number Diff line
{
    "1.18": {
        "hash": "sha256-PJlfINrk5OIdVVT6yVegqKXIW9W/NJFfrEtPFuDvEBs=",
        "version": "1.18-66"
    },
    "1.18.1": {
        "hash": "sha256-qUkXpEcsLLyZB6FcZmu7eE+V7Ne1PHe8CP5xED5Uh/U=",
        "version": "1.18.1-216"
    },
    "1.18.2": {
        "hash": "sha256-BXjxj01jK0lLRo7FaztBS1tW/qCH7n05z23N9MnQHwU=",
        "version": "1.18.2-388"
    },
    "1.19": {
        "hash": "sha256-DTnKzFGneysHHhzoYvy/C0pL1mjMfosxNZjYT6Cfq6w=",
        "version": "1.19-81"
    },
    "1.19.1": {
        "hash": "sha256-Wv4jofreksVHEk+odLx9kI+mdvSfCYefqHYiS2Lp1Rs=",
        "version": "1.19.1-111"
    },
    "1.19.2": {
        "hash": "sha256-LrXHRZ7JS83Fl+1xHVSaOrSw/aE+QSoHkqGgabWQOGQ=",
        "version": "1.19.2-307"
    },
    "1.19.3": {
        "hash": "sha256-MAfyxjjV8E7TK2raozBT/jY0zPp0NFyD0+pJgtONtdw=",
        "version": "1.19.3-448"
    },
    "1.19.4": {
        "hash": "sha256-5YfXjLo+me+MS8JM8gzDvbvonjOwtXIHBEavTra+XM8=",
        "version": "1.19.4-550"
    },
    "1.20": {
        "hash": "sha256-HkzPwFmfSR7m/uRFXTciMyrF14WE/M1Vy7O1HhFQRQU=",
        "version": "1.20-17"
    },
    "1.20.1": {
        "hash": "sha256-I0qbMgmBAMb8EWZk1k42zNtYtbZJrw+AvMywiwJV6uo=",
        "version": "1.20.1-196"
    },
    "1.20.2": {
        "hash": "sha256-ujQKg1rEC4Vjqn7aHNZHmhGnYjQJyJosNc2ddJDtF6c=",
        "version": "1.20.2-318"
    },
    "1.20.4": {
        "hash": "sha256-NrIsYLoAAWORw/S26NDFjYBVwpNITJxuWGZow3696wM=",
        "version": "1.20.4-435"
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -37702,7 +37702,9 @@ with pkgs;
  pacvim = callPackage ../games/pacvim { };
  papermc = callPackage ../games/papermc { };
  papermcServers = callPackages ../games/papermc { };
  papermc = papermcServers.papermc;
  path-of-building = qt6Packages.callPackage ../games/path-of-building {};