Unverified Commit 971dc6d6 authored by Aleksana's avatar Aleksana Committed by GitHub
Browse files

hmcl: add patch jar to fix terracotta (#465171)

parents 371480f2 0e3b99e2
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
From 541d055bf55703aa9e2c5a5e04a397f1d25efe28 Mon Sep 17 00:00:00 2001
From: Moraxyc <i@qaq.li>
Date: Wed, 29 Oct 2025 00:09:56 +0800
Subject: [PATCH 2/3] nix: use terracotta from nix

---
 .../org/jackhuang/hmcl/terracotta/TerracottaNative.java   | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java
index 079537978..68954b09e 100644
--- a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java
+++ b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java
@@ -47,7 +47,7 @@ public final class TerracottaNative {
 
     public TerracottaNative(List<URI> links, Path path, FileDownloadTask.IntegrityCheck checking) {
         this.links = links;
-        this.path = path;
+        this.path = Path.of("@TERRACOTTA_BIN@");
         this.checking = checking;
     }
 
@@ -130,9 +130,7 @@ public final class TerracottaNative {
 
     public ITerracottaProvider.Status status() throws IOException {
         if (Files.exists(path)) {
-            if (DigestUtils.digestToString(checking.getAlgorithm(), path).equalsIgnoreCase(checking.getChecksum())) {
-                return ITerracottaProvider.Status.READY;
-            }
+            return ITerracottaProvider.Status.READY;
         }
 
         try {
@@ -144,4 +142,4 @@ public final class TerracottaNative {
         }
         return ITerracottaProvider.Status.NOT_EXIST;
     }
-}
+}
\ No newline at end of file
-- 
2.51.0
+27 −0
Original line number Diff line number Diff line
From bcf95a23380424f2ca82649d93e9e0bdf8274b74 Mon Sep 17 00:00:00 2001
From: Moraxyc <i@qaq.li>
Date: Fri, 31 Oct 2025 14:23:54 +0800
Subject: [PATCH 3/3] nix: skip terracotta existence check on darwin

---
 .../org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java
index 00626f8b6..59125962d 100644
--- a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java
+++ b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java
@@ -45,10 +45,6 @@ public final class MacOSProvider implements ITerracottaProvider {
     public Status status() throws IOException {
         assert binary != null;
 
-        if (!Files.exists(Path.of("/Applications/terracotta.app"))) {
-            return Status.NOT_EXIST;
-        }
-
         return binary.status();
     }
 
-- 
2.51.0
+120 −61
Original line number Diff line number Diff line
@@ -2,18 +2,26 @@
  lib,
  stdenv,
  fetchurl,
  replaceVars,
  terracotta,
  makeDesktopItem,
  makeWrapper,
  wrapGAppsHook3,
  copyDesktopItems,
  imagemagick,
  desktopToDarwinBundle,
  jdk,
  jdk17,
  jdk21,
  hmclJdk ? jdk,
  hmclJdk ? jdk.override {
    # Required by jar file
    enableJavaFX = true;
  },
  buildPackages,
  hmclJdkBuild ? buildPackages.jdk.override {
    enableJavaFX = true;
  },
  minecraftJdks ? [
    jdk
    hmclJdk
    jdk17
    jdk21
  ],
  xorg,
  glib,
@@ -40,13 +48,63 @@ stdenv.mkDerivation (finalAttrs: {
    hash = "sha256-bgZsQ/5CUeOkbahIV0hQSPHrYfK+EaAIV6uMZzpLOVM=";
  };

  icon = fetchurl {
    url = "https://github.com/HMCL-dev/HMCL/raw/release-${finalAttrs.version}/HMCL/src/main/resources/assets/img/icon@8x.png";
    hash = "sha256-1OVq4ujA2ZHboB7zEk7004kYgl9YcoM4qLq154MZMGo=";
  # - HMCL prompts users to download prebuilt Terracotta binary for
  #   multi-user functionality, which is messy and doesn’t work on NixOS.
  # - Building from source isn’t feasible because HMCL’s code relies on
  #   Microsoft OAuth, CurseForge, and other API keys that upstream doesn’t
  #   allow in custom builds, causing features to break.
  # - Our workaround is to compile only the Java files that handle
  #   Terracotta downloads, package them into a patch jar that overrides
  #   the original classes, and have it load the original jar. This preserves
  #   the original jar’s integrity check and avoids modifying the upstream jar.
  terracottaNativeJava = fetchurl {
    name = "hmcl-terracotta-native-java-${finalAttrs.version}";
    url = "https://raw.githubusercontent.com/HMCL-dev/HMCL/v${finalAttrs.version}/${finalAttrs.terracottaNativeJavaPath}";
    hash = "sha256-sg8gBOMNdITmHeYByYriYp05ja1vtWPF/wuqdGmkgiA=";
  };
  macOSProviderJava = fetchurl {
    name = "hmcl-macos-provider-java-${finalAttrs.version}";
    url = "https://raw.githubusercontent.com/HMCL-dev/HMCL/v${finalAttrs.version}/${finalAttrs.macOSProviderJavaPath}";
    hash = "sha256-V8FNPPkq6/P3/HKcqKkAy6Ya1kUI3oEMfjEc8XdExgo=";
  };
  terracottaNativeJavaPath = "HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java";
  macOSProviderJavaPath = "HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java";

  dontUnpack = true;

  prePatch = ''
    install -Dm644 $terracottaNativeJava $terracottaNativeJavaPath
    install -Dm644 $macOSProviderJava $macOSProviderJavaPath
  '';

  patches = [
    (replaceVars ./0002-nix-use-terracotta-from-nix.patch {
      TERRACOTTA_BIN = lib.getExe terracotta;
    })
    ./0003-nix-skip-terracotta-existence-check-on-darwin.patch
  ];

  buildPhase = ''
    runHook preBuild

    # Build only classes we modified
    javac -cp $src -d out $terracottaNativeJavaPath $macOSProviderJavaPath

    # Extract MANIFEST.MF from original jar
    # We need Main-Class, Add-Opens, etc
    jar xf $src META-INF/MANIFEST.MF
    # Remove last empty line; otherwise file is invalid
    sed -i '/^[[:space:]]*$/d' META-INF/MANIFEST.MF
    # Let our patch jar be the entrace and load hmcl.jar
    echo "Class-Path: $out/lib/hmcl/hmcl.jar" >> META-INF/MANIFEST.MF

    # Package our patch jar
    # Reserve link to terracotta by not applying zip; nix cannot detect path from zipped jar
    jar cvf0m patch.jar META-INF/MANIFEST.MF -C out .

    runHook postBuild
  '';

  dontWrapGApps = true;

  desktopItems = [
@@ -62,31 +120,16 @@ stdenv.mkDerivation (finalAttrs: {

  nativeBuildInputs = [
    gobject-introspection
    makeWrapper
    wrapGAppsHook3
    copyDesktopItems
    imagemagick
    hmclJdkBuild
  ]
  ++ lib.optionals stdenv.hostPlatform.isDarwin [
    desktopToDarwinBundle
  ];

  installPhase = ''
    runHook preInstall

    mkdir -p $out/{bin,lib/hmcl}
    cp $src $out/lib/hmcl/hmcl.jar

    for n in 16 32 48 64 96 128 256
    do
      size=$n"x"$n
      mkdir -p $out/share/icons/hicolor/$size/apps
      magick ${finalAttrs.icon} -resize $size $out/share/icons/hicolor/$size/apps/hmcl.png
    done

    runHook postInstall
  '';

  fixupPhase =
    let
      libpath = lib.makeLibraryPath (
        [
  runtimeDeps = [
    libGL
    glfw
    glib
@@ -104,22 +147,38 @@ stdenv.mkDerivation (finalAttrs: {
    libpulseaudio
    wayland
    alsa-lib
        ]
      );
      hmclJdk' = hmclJdk.override {
        enableJavaFX = true; # Necessary for hardware acceleration.
      };
    in
  ];

  installPhase = ''
    runHook preInstall

    install -Dm444 $src $out/lib/hmcl/hmcl.jar
    install -Dm444 patch.jar $out/lib/hmcl/hmcl-terracotta-patch.jar

    jar xf $src assets/img
  ''
  + lib.optionalString stdenv.hostPlatform.isLinux ''
    install -Dm444 assets/img/icon-title.png $out/share/icons/hicolor/24x24/apps/hmcl.png
    install -Dm444 assets/img/icon.png $out/share/icons/hicolor/32x32/apps/hmcl.png
    install -Dm444 assets/img/icon-title@2x.png $out/share/icons/hicolor/48x48/apps/hmcl.png
    install -Dm444 assets/img/icon@2x.png $out/share/icons/hicolor/64x64/apps/hmcl.png
    install -Dm444 assets/img/icon@4x.png $out/share/icons/hicolor/128x128/apps/hmcl.png
    install -Dm444 assets/img/icon@8x.png $out/share/icons/hicolor/256x256/apps/hmcl.png
  ''
      runHook preFixup
  + lib.optionalString stdenv.hostPlatform.isDarwin ''
    install -Dm444 assets/img/icon-mac.png $out/share/icons/hicolor/512x512/apps/hmcl.png
  ''
  + ''
    runHook postInstall
  '';

      makeBinaryWrapper ${hmclJdk'}/bin/java $out/bin/hmcl \
        --add-flags "-jar $out/lib/hmcl/hmcl.jar" \
        --set LD_LIBRARY_PATH ${libpath} \
  postFixup = ''
    makeShellWrapper ${hmclJdk}/bin/java $out/bin/hmcl \
      --add-flags "-jar $out/lib/hmcl/hmcl-terracotta-patch.jar" \
      --set LD_LIBRARY_PATH ${lib.makeLibraryPath finalAttrs.runtimeDeps} \
      --prefix PATH : "${lib.makeBinPath minecraftJdks}" \
      --run 'cd $HOME' \
      ''${gappsWrapperArgs[@]}

      runHook postFixup
  '';

  passthru.updateScript = lib.getExe (callPackage ./update.nix { });
@@ -129,15 +188,15 @@ stdenv.mkDerivation (finalAttrs: {
    description = "Minecraft Launcher which is multi-functional, cross-platform and popular";
    changelog = "https://docs.hmcl.net/changelog/stable.html";
    mainProgram = "hmcl";
    sourceProvenance = with lib.sourceTypes; [ binaryBytecode ];
    sourceProvenance = with lib.sourceTypes; [
      fromSource # Our patch jar is built from source
      binaryBytecode
    ];
    license = lib.licenses.gpl3Only;
    longDescription = ''
      Hello Minecraft! Launcher (HMCL) is a free, open-source, and cross-platform Minecraft launcher.
      It provides comprehensive support for managing multiple game versions and mod loaders,
      including Forge, NeoForge, Fabric, Quilt, LiteLoader, and OptiFine.

      Note: HMCL manages the Terracotta binary internally. On NixOS, Terracotta-related features
      require `programs.nix-ld` to be enabled, as the runtime-downloaded binary is not patched.
    '';
    maintainers = with lib.maintainers; [
      daru-san
+2 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ writeShellApplication {
    fi

    nix-update hmcl --version="$version"
    update-source-version hmcl --source-key=icon --ignore-same-version
    update-source-version hmcl --source-key=terracottaNativeJava --ignore-same-version
    update-source-version hmcl --source-key=macOSProviderJava --ignore-same-version
  '';
}