From 87aa35c3af8084274838211c77fea6368890f101 Mon Sep 17 00:00:00 2001 From: tomikun <60690056+alexytomi@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:25:31 +0800 Subject: [PATCH 1/2] fix: Slow modpack import performance and better progress bar handling --- .../net/kdt/pojavlaunch/LauncherActivity.java | 35 ++- .../modloaders/modpacks/api/CommonApi.java | 52 +++-- .../modpacks/api/CurseforgeApi.java | 4 +- .../modloaders/modpacks/api/ModpackApi.java | 5 +- .../modpacks/api/ModpackInstaller.java | 216 ++++++++---------- .../modloaders/modpacks/api/ModrinthApi.java | 4 +- 6 files changed, 160 insertions(+), 156 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java index d846bd7c9..f6b3e2cff 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java @@ -10,8 +10,10 @@ import android.Manifest; import android.app.NotificationManager; import android.content.Context; import android.content.pm.PackageManager; +import android.database.Cursor; import android.os.Build; import android.os.Bundle; +import android.provider.OpenableColumns; import android.util.Log; import android.view.View; import android.widget.ImageButton; @@ -60,11 +62,14 @@ import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles; import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.lang.ref.WeakReference; import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.util.List; +import java.util.Locale; public class LauncherActivity extends BaseActivity { public static final String SETTING_FRAGMENT_TAG = "SETTINGS_FRAGMENT"; @@ -78,14 +83,42 @@ public class LauncherActivity extends BaseActivity { if(data != null) { PojavApplication.sExecutorService.execute(() -> { try { - ModLoader loaderInfo = new CommonApi(getString(R.string.curseforge_api_key)).importModpack(this, data); + // Copy ZIP file to cache + long fileSize = -1; + try (Cursor returnCursor = getContentResolver().query(data, new String[]{OpenableColumns.SIZE}, null, null, null)) { + if (returnCursor != null && returnCursor.moveToFirst()) { + fileSize = returnCursor.getLong(0); + } + } + File modpackFile = new File(Tools.DIR_CACHE, "import_modpack_placeholdername.cf"); + try (InputStream inputStream = getContentResolver().openInputStream(data)){ + FileOutputStream output = new FileOutputStream(modpackFile); + byte[] b = new byte[262144]; + int read; + int readTotal = 0; + while ((read = inputStream.read(b)) != -1) { + output.write(b, 0, read); + readTotal += read; + String readMB = fileSize > 0 ? String.format(Locale.US, "%.2f", readTotal / (1024.0 * 1024.0)) : "unknown"; + String totalMB = fileSize > 0 ? String.format(Locale.US, "%.2f", fileSize / (1024.0 * 1024.0)) : "unknown"; + int progress = fileSize > 0 ? (int) ((readTotal * 100L) / fileSize) : 0; + ProgressLayout.setProgress(ProgressLayout.INSTALL_MODPACK, progress, R.string.import_modpack_copy, readMB, totalMB); + } + output.flush(); + output.close(); + } + ModLoader loaderInfo = new CommonApi(getString(R.string.curseforge_api_key)).importModpack(modpackFile); + modpackFile.delete(); if (loaderInfo == null) return; loaderInfo.getDownloadTask(new NotificationDownloadListener(this, loaderInfo)).run(); } catch (IOException e) { Tools.showErrorRemote(this, R.string.modpack_install_download_failed, e); + ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK); } catch (IllegalArgumentException e) { Tools.showError(this, R.string.not_modpack_file, e); + ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK); } catch (NoSuchAlgorithmException e) { + ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK); // Should literally never happen because SHA-1 is required Java spec throw new RuntimeException(e); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java index 7383e2c2b..6bedbfb92 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java @@ -14,6 +14,7 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; +import net.kdt.pojavlaunch.utils.ZipUtils; import org.jdom2.IllegalDataException; @@ -26,6 +27,7 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; /** @@ -124,8 +126,8 @@ public class CommonApi implements ModpackApi { } @Override - public ModLoader importModpack(Activity activity, Uri zipUri) throws IOException, NoSuchAlgorithmException { - return getModpackApi(activity, zipUri).importModpack(activity, zipUri); + public ModLoader importModpack(File modpackFile) throws IOException, NoSuchAlgorithmException { + return getModpackApi(modpackFile).importModpack(modpackFile); } private @NonNull ModpackApi getModpackApi(int apiSource) { @@ -139,29 +141,37 @@ public class CommonApi implements ModpackApi { } } - private @NonNull ModpackApi getModpackApi(Activity activity, Uri zipUri){ - String modrinthPackInfoFileName = "modrinth.index.json"; - String curseforgePackInfoFileName = "manifest.json"; - InputStream inputStream = null; - try { - inputStream = activity.getContentResolver().openInputStream(zipUri); - ZipInputStream zipInputStream = new ZipInputStream(inputStream); - ZipEntry zipEntry; + private @NonNull ModpackApi getModpackApi(@NonNull File modpackFile) throws IOException { + try (ZipFile zip = new ZipFile(modpackFile)) { boolean isModrinth; boolean isCurseforge; - while ((zipEntry = zipInputStream.getNextEntry()) != null) { - isModrinth = zipEntry.getName().equals(modrinthPackInfoFileName); - isCurseforge = zipEntry.getName().equals(curseforgePackInfoFileName); - if(isModrinth) { - return mModrinthApi; - } else if (isCurseforge) { - return mCurseforgeApi; - } + + try { + ZipUtils.getEntryStream(zip, "modrinth.index.json"); + isModrinth = true; + } catch (IOException ignored) { + isModrinth = false; } - } catch (Exception e) { - throw new RuntimeException(e); + + try { + ZipUtils.getEntryStream(zip, "manifest.json"); + isCurseforge = true; + } catch (IOException ignored) { + isCurseforge = false; + } + + if (isModrinth && isCurseforge) { + String name = modpackFile.getName(); + int dot = name.lastIndexOf('.'); + String extension = (dot == -1) ? "" : name.substring(dot + 1); + if (extension.equalsIgnoreCase("mrpack")) return mModrinthApi; + throw new IOException("Ambiguous file contains both modrinth.index.json and manifest.json. Cannot determine modpack format."); + } + if (isModrinth) return mModrinthApi; + if (isCurseforge) return mCurseforgeApi; + + throw new IllegalArgumentException("Zip provided does not contain a manifest file."); } - throw new IllegalArgumentException("Zip provided does not contain a manifest file"); } /** Fuse the arrays in a way that's fair for every endpoint */ diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java index a414861bd..5ef5ab1cf 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java @@ -176,8 +176,8 @@ public class CurseforgeApi implements ModpackApi{ } @Override - public ModLoader importModpack(Activity activity, Uri zipUri) throws IOException, NoSuchAlgorithmException { - return ModpackInstaller.importModpack(activity, zipUri, this::installCurseforgeZip); + public ModLoader importModpack(File modpackFile) throws IOException, NoSuchAlgorithmException { + return ModpackInstaller.importModpack(modpackFile, Constants.SOURCE_CURSEFORGE, this::installCurseforgeZip); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java index a6ea9c8dc..6ae2a4657 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java @@ -81,8 +81,7 @@ public interface ModpackApi { * Imports the mod(pack) from a file. * May require the download of additional files. * May requires launching the installation of a modloader - * @param activity any activity - * @param zipUri URI to DocumentsUI selected zip file + * @param modpackFile Zip file to mrpack or cf zip pack */ - ModLoader importModpack(Activity activity, Uri zipUri) throws IOException, NoSuchAlgorithmException; + ModLoader importModpack(File modpackFile) throws IOException, NoSuchAlgorithmException; } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackInstaller.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackInstaller.java index 79be2c1ef..fa2125ca8 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackInstaller.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackInstaller.java @@ -1,10 +1,5 @@ package net.kdt.pojavlaunch.modloaders.modpacks.api; -import android.app.Activity; -import android.database.Cursor; -import android.net.Uri; -import android.provider.OpenableColumns; - import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.kdt.mcgui.ProgressLayout; @@ -12,26 +7,23 @@ import com.kdt.mcgui.ProgressLayout; import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.modloaders.modpacks.imagecache.ModIconCache; +import net.kdt.pojavlaunch.modloaders.modpacks.models.Constants; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail; import net.kdt.pojavlaunch.progresskeeper.DownloaderProgressWrapper; import net.kdt.pojavlaunch.utils.DownloadUtils; +import net.kdt.pojavlaunch.utils.ZipUtils; import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles; import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile; - -import java.io.BufferedReader; import java.io.File; -import java.io.FileOutputStream; +import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Locale; import java.util.concurrent.Callable; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; +import java.util.zip.ZipFile; public class ModpackInstaller { @@ -65,6 +57,7 @@ public class ModpackInstaller { modLoaderInfo = installFunction.installModpack(modpackFile, new File(Tools.DIR_GAME_HOME, "custom_instances/"+modpackName)); } finally { + //noinspection ResultOfMethodCallIgnored It's cache, who cares modpackFile.delete(); ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK); } @@ -86,123 +79,92 @@ public class ModpackInstaller { return modLoaderInfo; } - public static ModLoader importModpack(Activity activity, Uri zipUri, InstallFunction installFunction) throws IOException, NoSuchAlgorithmException { + public static ModLoader importModpack(File modpackFile, int apiSource, InstallFunction installFunction) throws IOException, NoSuchAlgorithmException { ProgressLayout.setProgress(ProgressLayout.INSTALL_MODPACK, 1, R.string.import_modpack_start); - String modrinthPackInfoFileName = "modrinth.index.json"; - String curseforgePackInfoFileName = "manifest.json"; - InputStream inputStream = activity.getContentResolver().openInputStream(zipUri); - if (inputStream == null) throw new IOException("Can't open modpack file, try again?"); - ZipInputStream zipInputStream = new ZipInputStream(inputStream); - ZipEntry zipEntry; - while ((zipEntry = zipInputStream.getNextEntry()) != null) { - boolean isModrinth = zipEntry.getName().equals(modrinthPackInfoFileName); - boolean isCurseforge = zipEntry.getName().equals(curseforgePackInfoFileName); - if (!(isModrinth || isCurseforge)) continue; - // Read Manifest JSON - BufferedReader reader = new BufferedReader(new InputStreamReader(zipInputStream)); - String str; - StringBuilder jsonString = new StringBuilder(); - while ((str = reader.readLine()) != null) { - jsonString.append(str).append("\n"); - } - zipInputStream.close(); - - // Hash the ZIP File - inputStream = activity.getContentResolver().openInputStream(zipUri); - if (inputStream == null) throw new IOException("Can't open modpack file, try again?"); - MessageDigest algorithm = MessageDigest.getInstance("SHA-1"); - DigestInputStream hashingStream = new DigestInputStream(inputStream, algorithm); - - long fileSize = -1; - long readSize = 0; - try (Cursor returnCursor = activity.getContentResolver().query(zipUri, new String[]{OpenableColumns.SIZE}, null, null, null)) { - if (returnCursor != null && returnCursor.moveToFirst()) { - fileSize = returnCursor.getLong(0); - } - } - - byte[] buffer = new byte[262144]; - while (true) { - int n = hashingStream.read(buffer); - if (n == -1) break; - readSize += n; - String readMB = fileSize > 0 ? String.format(Locale.US, "%.2f", readSize / (1024.0 * 1024.0)) : "unknown"; - String totalMB = fileSize > 0 ? String.format(Locale.US, "%.2f",fileSize / (1024.0 * 1024.0)) : "unknown"; - int progress = fileSize > 0 ? (int) ((readSize * 100L) / fileSize) : 0; - ProgressLayout.setProgress(ProgressLayout.INSTALL_MODPACK, progress, R.string.import_modpack_hash, readMB, totalMB); - } - hashingStream.close(); - byte[] digest = algorithm.digest(); - StringBuilder sb = new StringBuilder(digest.length * 2); - for (byte b : digest) { - sb.append(String.format("%02x", b)); - } - String hash = sb.toString(); - - ProgressLayout.setProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.import_modpack_json); - - // Parse the JSON to prepare for instance creation - JsonObject packInfoJson = JsonParser.parseString(jsonString.toString()).getAsJsonObject(); - String modpackName = ""; - String modpackVersion = ""; - String modpackMcVersion = ""; - if(isModrinth){ - try { - modpackName = packInfoJson.get("name").getAsString(); - modpackVersion = packInfoJson.get("versionId").getAsString(); - modpackMcVersion = packInfoJson.get("dependencies").getAsJsonObject().get("minecraft").getAsString(); - } catch (RuntimeException ignored) {} - } else { - try { - modpackName = packInfoJson.get("name").getAsString(); - modpackVersion = packInfoJson.get("version").getAsString(); - modpackMcVersion = packInfoJson.get("minecraft").getAsJsonObject().get("version").getAsString(); - } catch (RuntimeException ignored) {} - } - if(modpackName.isBlank() || modpackVersion.isBlank() || modpackMcVersion.isBlank()) throw new IOException("Corrupt Modpack manifest file."); - // Added a for because there is an awkward __ that I can't be bothered to fix - // FO only deduplication be like: - String profileFolderName = String.join(" ", modpackName, modpackVersion, "for", modpackMcVersion, hash); - profileFolderName = profileFolderName.trim().replaceAll("[\\\\/:*?\"<>| \\t\\n]", "_"); - - // Copy ZIP file to cache - File modpackFile = new File(Tools.DIR_CACHE, profileFolderName + ".cf"); - inputStream = activity.getContentResolver().openInputStream(zipUri); - if (inputStream == null) throw new IOException("Can't open modpack file, try again?"); - FileOutputStream output = new FileOutputStream(modpackFile); - byte[] b = new byte[262144]; - int read; - int readTotal = 0; - while ((read = inputStream.read(b)) != -1) { - output.write(b, 0, read); - readTotal += read; - String readMB = fileSize > 0 ? String.format(Locale.US, "%.2f", readTotal / (1024.0 * 1024.0)) : "unknown"; - String totalMB = fileSize > 0 ? String.format(Locale.US, "%.2f", fileSize / (1024.0 * 1024.0)) : "unknown"; - int progress = fileSize > 0 ? (int) ((readTotal * 100L) / fileSize) : 0; - ProgressLayout.setProgress(ProgressLayout.INSTALL_MODPACK, progress, R.string.import_modpack_copy, readMB, totalMB); - } - output.flush(); - output.close(); - - // Install the actual pack into custom_instances - ModLoader modLoaderInfo = installFunction.installModpack(modpackFile, new File(Tools.DIR_GAME_HOME, "custom_instances/"+profileFolderName)); - // We have to do this because installModpack doesn't clean up after itself - ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK); - modpackFile.delete(); - - // Create the instance (We don't have a picture guys) - MinecraftProfile profile = MinecraftProfile.getDefaultProfile(); - profile.gameDir = "./custom_instances/" + profileFolderName; - profile.name = modpackName; - if (!modpackMcVersion.isBlank()) profile.lastVersionId = modpackMcVersion; - if (modLoaderInfo != null && modLoaderInfo.getVersionId() != null) - profile.lastVersionId = modLoaderInfo.getVersionId(); - LauncherProfiles.mainProfileJson.profiles.put(profileFolderName, profile); - LauncherProfiles.write(); - - return modLoaderInfo; + // modpackFile is deleted in LauncherActivity, no need to delete here. + if (modpackFile == null) throw new IOException("Can't open modpack file, try again?"); + String manifestFileName; + switch (apiSource) { + case Constants.SOURCE_CURSEFORGE: + manifestFileName = "manifest.json"; + break; + case Constants.SOURCE_MODRINTH: + manifestFileName = "modrinth.index.json"; + break; + default: + throw new UnsupportedOperationException("Unknown API source: " + apiSource); } - throw new IOException("Can't find manifest file in modpack provided"); + // Read Manifest JSON + JsonObject manifestFile = JsonParser.parseString(Tools.read(ZipUtils.getEntryStream( + new ZipFile(modpackFile), manifestFileName))).getAsJsonObject(); + + // Parse the JSON to prepare for instance creation + ProgressLayout.setProgress(ProgressLayout.INSTALL_MODPACK, 1, R.string.import_modpack_json); + String modpackName = ""; + String modpackVersion = ""; + String modpackMcVersion = ""; + + switch (apiSource) { + case Constants.SOURCE_CURSEFORGE: + try { + modpackName = manifestFile.get("name").getAsString(); + modpackVersion = manifestFile.get("version").getAsString(); + modpackMcVersion = manifestFile.get("minecraft").getAsJsonObject().get("version").getAsString(); + } catch (RuntimeException ignored) {} + break; + case Constants.SOURCE_MODRINTH: + try { + modpackName = manifestFile.get("name").getAsString(); + modpackVersion = manifestFile.get("versionId").getAsString(); + modpackMcVersion = manifestFile.get("dependencies").getAsJsonObject().get("minecraft").getAsString(); + } catch (RuntimeException ignored) {} + break; + default: + throw new UnsupportedOperationException("Unknown API source: " + apiSource); + } + if(modpackName.isBlank() || modpackVersion.isBlank() || modpackMcVersion.isBlank()) throw new IOException("Corrupt Modpack manifest file."); + + // Hash the ZIP File, can't use getSha1 cause progress bar + MessageDigest algorithm = MessageDigest.getInstance("SHA-1"); + //noinspection IOStreamConstructor It will reccomend you use an API26 function like a dumb + DigestInputStream hashingStream = new DigestInputStream(new FileInputStream(modpackFile), algorithm); + long fileSize = modpackFile.length(); + long readSize = 0; + byte[] buffer = new byte[262144]; + while (true) { + int n = hashingStream.read(buffer); + if (n == -1) break; + readSize += n; + String readMB = fileSize > 0 ? String.format(Locale.US, "%.2f", readSize / (1024.0 * 1024.0)) : "unknown"; + String totalMB = fileSize > 0 ? String.format(Locale.US, "%.2f",fileSize / (1024.0 * 1024.0)) : "unknown"; + int progress = fileSize > 0 ? (int) ((readSize * 100L) / fileSize) : 0; + ProgressLayout.setProgress(ProgressLayout.INSTALL_MODPACK, progress, R.string.import_modpack_hash, readMB, totalMB); + } + hashingStream.close(); + byte[] digest = algorithm.digest(); + StringBuilder sb = new StringBuilder(digest.length * 2); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + String hash = sb.toString(); + String profileFolderName = String.join(" ", modpackName, modpackVersion, "for", modpackMcVersion, hash); + profileFolderName = profileFolderName.trim().replaceAll("[\\\\/:*?\"<>| \\t\\n]", "_"); + + // Install the actual pack into custom_instances + ModLoader modLoaderInfo = installFunction.installModpack(modpackFile, new File(Tools.DIR_GAME_HOME, "custom_instances/"+profileFolderName)); + ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK); + + // Create the instance (We don't have a picture guys) + MinecraftProfile profile = MinecraftProfile.getDefaultProfile(); + profile.gameDir = "./custom_instances/" + profileFolderName; + profile.name = modpackName; + if (!modpackMcVersion.isBlank()) profile.lastVersionId = modpackMcVersion; + if (modLoaderInfo != null && modLoaderInfo.getVersionId() != null) + profile.lastVersionId = modLoaderInfo.getVersionId(); + LauncherProfiles.mainProfileJson.profiles.put(profileFolderName, profile); + LauncherProfiles.write(); + + return modLoaderInfo; } interface InstallFunction { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java index f7ec62197..c0df18649 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModrinthApi.java @@ -160,8 +160,8 @@ public class ModrinthApi implements ModpackApi{ } @Override - public ModLoader importModpack(Activity activity, Uri zipUri) throws IOException, NoSuchAlgorithmException { - return ModpackInstaller.importModpack(activity, zipUri, this::installMrpack); + public ModLoader importModpack(File modpackFile) throws IOException, NoSuchAlgorithmException { + return ModpackInstaller.importModpack(modpackFile, Constants.SOURCE_MODRINTH, this::installMrpack); } private static ModLoader createInfo(ModrinthIndex modrinthIndex) { From cf131e13aa0e64026b3519284215c3f31364545a Mon Sep 17 00:00:00 2001 From: tomikun <60690056+alexytomi@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:31:53 +0800 Subject: [PATCH 2/2] fix: Block import button from being pressed if task is ongoing This button is not safe for doing multiple packs at once --- .../src/main/java/com/kdt/mcgui/ProgressLayout.java | 2 +- .../src/main/java/net/kdt/pojavlaunch/LauncherActivity.java | 2 +- .../kdt/pojavlaunch/fragments/ModpackCreateFragment.java | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java b/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java index 089fa17d8..48de93e10 100644 --- a/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java +++ b/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java @@ -73,7 +73,7 @@ public class ProgressLayout extends ConstraintLayout implements View.OnClickList } } - public boolean hasProcesses(){ + public static boolean hasProcesses(){ return ProgressKeeper.getTaskCount() > 0; } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java index f6b3e2cff..8124b0cef 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java @@ -171,7 +171,7 @@ public class LauncherActivity extends BaseActivity { }; private final ExtraListener mLaunchGameListener = (key, value) -> { - if(mProgressLayout.hasProcesses()){ + if(ProgressLayout.hasProcesses()){ Toast.makeText(this, R.string.tasks_ongoing, Toast.LENGTH_LONG).show(); return false; } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ModpackCreateFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ModpackCreateFragment.java index 69aac3520..f83b33b3b 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ModpackCreateFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ModpackCreateFragment.java @@ -6,6 +6,7 @@ import static net.kdt.pojavlaunch.Tools.hasOnlineProfile; import android.app.Activity; import android.os.Bundle; import android.view.View; +import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; @@ -13,6 +14,7 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.kdt.mcgui.MineButton; +import com.kdt.mcgui.ProgressLayout; import net.kdt.pojavlaunch.BaseActivity; import net.kdt.pojavlaunch.LauncherActivity; @@ -36,6 +38,10 @@ public class ModpackCreateFragment extends Fragment { Activity launcheractivity = requireActivity(); if (!(launcheractivity instanceof LauncherActivity)) throw new IllegalStateException("Cannot import modpack without LauncherActivity"); + if(ProgressLayout.hasProcesses()){ + Toast.makeText(launcheractivity, R.string.tasks_ongoing, Toast.LENGTH_LONG).show(); + return; + } ((LauncherActivity) launcheractivity).modpackImportLauncher.launch(null); });; }