From d22dae9834e73401694662a9d782e05826cc7172 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 7 Mar 2021 13:15:44 +0800 Subject: [PATCH] feat: Add progress gui for downloading JavaFX --- .../java/org/jackhuang/hmcl/Launcher.java | 5 + .../main/java/org/jackhuang/hmcl/Main.java | 32 +- .../jackhuang/hmcl/game/LauncherHelper.java | 2 +- .../hmcl/util/SelfDependencyPatcher.java | 297 +++++++++++------- .../resources/assets/lang/I18N.properties | 3 +- .../resources/assets/lang/I18N_zh.properties | 3 +- .../assets/lang/I18N_zh_CN.properties | 3 +- .../hmcl/launch/DefaultLauncher.java | 2 +- .../org/jackhuang/hmcl/task/TaskExecutor.java | 2 +- .../jackhuang/hmcl/util/ReflectionHelper.java | 16 + .../hmcl/util/platform/JavaVersion.java | 9 +- build.gradle | 8 + 12 files changed, 240 insertions(+), 142 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index 219c62ada..66df693e8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -25,6 +25,7 @@ import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.AsyncTaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.upgrade.UpdateChecker; +import org.jackhuang.hmcl.upgrade.UpdateHandler; import org.jackhuang.hmcl.util.CrashReporter; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; @@ -84,6 +85,10 @@ public final class Launcher extends Application { } public static void main(String[] args) { + if (UpdateHandler.processArguments(args)) { + return; + } + Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER); AsyncTaskExecutor.setUncaughtExceptionHandler(new CrashReporter(false)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Main.java b/HMCL/src/main/java/org/jackhuang/hmcl/Main.java index 38553be83..72a024d0f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Main.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Main.java @@ -17,7 +17,6 @@ */ package org.jackhuang.hmcl; -import org.jackhuang.hmcl.upgrade.UpdateHandler; import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.SelfDependencyPatcher; @@ -26,6 +25,8 @@ import javax.swing.*; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -36,6 +37,7 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Collections; +import java.util.function.Consumer; import java.util.logging.Level; import static org.jackhuang.hmcl.util.Lang.thread; @@ -59,13 +61,15 @@ public final class Main { Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs")); - checkJavaFX(); - - if (UpdateHandler.processArguments(args)) { - return; - } - - Launcher.main(args); + checkJavaFX(classLoader -> { + try { + Class c = Class.forName("org.jackhuang.hmcl.Launcher", true, classLoader); + Method method = c.getDeclaredMethod("main"); + method.invoke(null, (Object) args); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new InternalError(e); + } + }); } private static void checkDirectoryPath() { @@ -77,8 +81,16 @@ public final class Main { } } - private static void checkJavaFX() { - SelfDependencyPatcher.patch(); + private static void checkJavaFX(Consumer runnable) { + try { + SelfDependencyPatcher.runInJavaFxEnvironment(runnable); + } catch (SelfDependencyPatcher.PatchException e) { + LOG.log(Level.SEVERE, "unable to patch JVM", e); + showErrorAndExit(i18n("fatal.javafx.missing")); + } catch (SelfDependencyPatcher.IncompatibleVersionException e) { + LOG.log(Level.SEVERE, "unable to patch JVM", e); + showErrorAndExit(i18n("fatal.javafx.incompatible")); + } } private static void checkDSTRootCAX3() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index de5f4d6c2..47a391038 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -340,7 +340,7 @@ public final class LauncherHelper { } // LaunchWrapper 1.12 will crash because of assuming the system class loader is an instance of URLClassLoader. - if (!flag && java.getParsedVersion() >= JavaVersion.JAVA_9_AND_LATER + if (!flag && java.getParsedVersion() >= JavaVersion.JAVA_9 && version.getMainClass().equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN) && version.getLibraries().stream() .filter(library -> "launchwrapper".equals(library.getArtifactId())) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/SelfDependencyPatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/SelfDependencyPatcher.java index fbe2ea637..2e30cddbf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/SelfDependencyPatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/SelfDependencyPatcher.java @@ -1,8 +1,11 @@ package org.jackhuang.hmcl.util; -import com.nqzero.permit.Permit; +import org.jackhuang.hmcl.util.io.ChecksumMismatchException; +import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; +import javax.swing.*; +import java.awt.*; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -12,15 +15,18 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.List; import java.util.*; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.function.Consumer; import java.util.logging.Level; -import java.util.regex.Matcher; import static java.lang.Class.forName; -import static javax.swing.JOptionPane.ERROR_MESSAGE; -import static javax.swing.JOptionPane.showMessageDialog; import static org.jackhuang.hmcl.Metadata.HMCL_DIRECTORY; import static org.jackhuang.hmcl.util.Logging.LOG; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.platform.JavaVersion.CURRENT_JAVA; /** * Utility for patching self when missing dependencies. @@ -83,32 +89,39 @@ public class SelfDependencyPatcher { /** * Patch in any missing dependencies, if any. */ - public static void patch() { - if (getVmVersion() > 8) { - Permit.godMode(); - Permit.unLog(); + public static void runInJavaFxEnvironment(Consumer runnable) throws PatchException, IncompatibleVersionException { + if (CURRENT_JAVA.getParsedVersion() > 8) { patchReflectionFilters(); } // Do nothing if JavaFX is detected try { try { - forName("javafx.application.Platform", false, ClassLoader.getSystemClassLoader()); - return; - } catch(Exception ignored) { + forName("javafx.application.Application"); + runnable.accept(SelfDependencyPatcher.class.getClassLoader()); + } catch (Exception ignored) { } - } catch(UnsupportedClassVersionError error) { + } catch (UnsupportedClassVersionError error) { // Loading the JavaFX class was unsupported. // We are probably on 8 and its on 11 - showIncompatibleVersion(); - return; + throw new IncompatibleVersionException(); } // So the problem with Java 8 is that some distributions DO NOT BUNDLE JAVAFX // Why is this a problem? OpenJFX does not come in public bundles prior to Java 11 // So you're out of luck unless you change your JDK or update Java. - if (getVmVersion() < 11) { - showIncompatibleVersion(); - return; + if (CURRENT_JAVA.getParsedVersion() < 11) { + throw new IncompatibleVersionException(); } + + // We can only self-patch JavaFX on x86 platform. + // For ARM support, user's manual patch is required. + switch (System.getProperty("os.arch")) { + case "amd64": + case "x86": + break; + default: + throw new IncompatibleVersionException(); + } + // Otherwise we're free to download in Java 11+ LOG.info("Missing JavaFX dependencies, attempting to patch in missing classes"); // Check if dependencies need to be downloaded @@ -116,9 +129,8 @@ public class SelfDependencyPatcher { LOG.info(" - No local cache, downloading dependencies..."); try { fetchDependencies(); - } catch(IOException ex) { - logError(ex, "Failed to download dependencies!"); - System.exit(-1); + } catch (Exception ex) { + throw new PatchException("Failed to download dependencies", ex); } } else { LOG.info(" - Local cache found!"); @@ -126,17 +138,35 @@ public class SelfDependencyPatcher { // Add the dependencies try { loadFromCache(); - } catch(IOException ex) { - logError(ex, ex.getMessage()); - System.exit(-1); - } catch(ReflectiveOperationException ex) { - logError(ex, "Failed to add dependencies to classpath!"); - System.exit(-1); + } catch (IOException ex) { + throw new PatchException("Failed to load JavaFX cache", ex); + } catch (ReflectiveOperationException ex) { + throw new PatchException("Failed to add dependencies to classpath!", ex); } LOG.info(" - Done!"); } +// /** +// * Inject them into the current classpath. +// * +// * @throws IOException When the locally cached dependency urls cannot be resolved. +// * @throws ReflectiveOperationException When the call to add these urls to the system classpath failed. +// */ +// private static void loadFromCache(Consumer runnable) throws IOException, ReflectiveOperationException { +// LOG.info(" - Loading dependencies..."); +// // Get Jar URLs +// List jarUrls = new ArrayList<>(); +// Files.walk(DEPENDENCIES_DIR_PATH).forEach(path -> { +// try { +// jarUrls.add(path.toUri().toURL()); +// } catch (MalformedURLException ex) { +// LOG.log(Level.WARNING, "Failed to convert '" + path.toFile().getAbsolutePath() + "' to URL", ex); +// } +// }); +// ClassLoader classLoader = new URLClassLoader(jarUrls.toArray(new URL[0]), SelfDependencyPatcher.class.getClassLoader()); +// runnable.accept(classLoader); +// } /** * Inject them into the current classpath. * @@ -153,7 +183,7 @@ public class SelfDependencyPatcher { try { jarUrls.add(path.toUri().toURL()); } catch(MalformedURLException ex) { - logError(ex, "Failed to convert '%s' to URL", path.toFile().getAbsolutePath()); + LOG.log(Level.WARNING, "Failed to convert '" + path.toFile().getAbsolutePath() + "' to URL", ex); } }); // Fetch UCP of application's ClassLoader @@ -162,7 +192,7 @@ public class SelfDependencyPatcher { Object appClassLoader = clsClassLoaders.getDeclaredMethod("appClassLoader").invoke(null); Class ucpOwner = appClassLoader.getClass(); // Field removed in 16, but still exists in parent class "BuiltinClassLoader" - if (getVmVersion() >= 16) + if (CURRENT_JAVA.getParsedVersion() >= 16) ucpOwner = ucpOwner.getSuperclass(); Field fieldUCP = ucpOwner.getDeclaredField("ucp"); fieldUCP.setAccessible(true); @@ -175,40 +205,45 @@ public class SelfDependencyPatcher { addURL.invoke(ucp, url); } - /** - * Display a message detailing why self-patching cannot continue. - */ - private static void showIncompatibleVersion() { - String message = "Application cannot self-patch below Java 11 on this JVM. " + - "Please run using JDK 11 or higher or use a JDK that bundles JavaFX.\n" + - " - Your JDK does not bundle JavaFX\n" + - " - Downloadable JFX bundles only come with 11 support or higher."; - showMessageDialog(null, message, "Error: Cannot self-patch", ERROR_MESSAGE); - // LOG and exit - LOG.severe(message); - System.exit(-1); - } - /** * Download dependencies. * - * @throws IOException - * When the files cannot be fetched or saved. + * @throws IOException When the files cannot be fetched or saved. */ - private static void fetchDependencies() throws IOException { + private static void fetchDependencies() throws Exception { // Get dir to store dependencies in Path dependenciesDir = DEPENDENCIES_DIR_PATH; if (!Files.isDirectory(dependenciesDir)) { Files.createDirectories(dependenciesDir); } - // Download each dependency - List dependencies = getLatestDependencies(); - for(String dependencyPattern : dependencies) { - String dependencyUrlPath = String.format(dependencyPattern, getMvnName()); - URL depURL = new URL(dependencyUrlPath); - Path dependencyFilePath = DEPENDENCIES_DIR_PATH.resolve(getFileName(dependencyUrlPath)); - Files.copy(depURL.openStream(), dependencyFilePath, StandardCopyOption.REPLACE_EXISTING); - } + ProgressFrame dialog = new ProgressFrame(i18n("download.javafx")); + + ForkJoinTask task = ForkJoinPool.commonPool().submit(() -> { + // Download each dependency + List dependencies = getLatestDependencies(); + for (int i = 0; i < dependencies.size(); i++) { + String dependencyUrlPath = dependencies.get(i); + URL depURL = new URL(dependencyUrlPath); + Path dependencyFilePath = DEPENDENCIES_DIR_PATH.resolve(getFileName(dependencyUrlPath)); + int finalI = i; + SwingUtilities.invokeLater(() -> { + dialog.setStatus(dependencyUrlPath); + dialog.setProgress(finalI, dependencies.size()); + }); + String expectedHash = NetworkUtils.doGet(NetworkUtils.toURL(dependencyUrlPath + ".sha1")); + Files.copy(depURL.openStream(), dependencyFilePath, StandardCopyOption.REPLACE_EXISTING); + String actualHash = Hex.encodeHex(DigestUtils.digest("SHA-1", dependencyFilePath)); + if (!expectedHash.equalsIgnoreCase(actualHash)) { + throw new ChecksumMismatchException("SHA-1", expectedHash, actualHash); + } + } + + return null; + }); + + dialog.setVisible(true); + + task.get(); } /** @@ -222,9 +257,7 @@ public class SelfDependencyPatcher { } /** - * @param url - * Full url path. - * + * @param url Full url path. * @return Name of file at url. */ private static String getFileName(String url) { @@ -232,23 +265,22 @@ public class SelfDependencyPatcher { } /** - * @param component - * Name of the component. - * + * @param component Name of the component. * @return Formed URL for the component. */ private static String jfxUrl(String component, String version) { - // Add platform specific identifier to the end. - // https://repo1.maven.org/maven2/org/openjfx/javafx-%s/%s/javafx-%s-%s - return String.format("https://maven.aliyun.com/repository/central/org/openjfx/javafx-%s/%s/javafx-%s-%s", - component, version, component, version) + "-%s.jar"; + // https://repo1.maven.org/maven2/org/openjfx/javafx-%s/%s/javafx-%s-%s-%s.jar + return String.format("https://maven.aliyun.com/repository/central/org/openjfx/javafx-%s/%s/javafx-%s-%s-%s.jar", + component, version, component, version, getMvnName()); +// return String.format("https://bmclapi.bangbang93.com/maven/org/openjfx/javafx-%s/%s/javafx-%s-%s-%s.jar", +// component, version, component, version, getMvnName()); } /** * @return Latest JavaFX supported version for. */ private static int getLatestSupportedJfxVersion() { - int version = getVmVersion(); + int version = CURRENT_JAVA.getParsedVersion(); while (version >= 11) { List dependencies = JFX_DEPENDENCIES.get(version); if (dependencies != null) @@ -269,40 +301,14 @@ public class SelfDependencyPatcher { throw new AssertionError("Failed to get latest JFX artifact urls"); } - private static void logError(Throwable t, String msg, Object... args) { - LOG.log(Level.SEVERE, t, () -> compile(msg, args)); - } - - /** - * Compiles message with "{}" arg patterns. - * - * @param msg - * Message pattern. - * @param args - * Values to pass. - * - * @return Compiled message with inlined arg values. - */ - private static String compile(String msg, Object[] args) { - int c = 0; - while(msg.contains("{}")) { - // Failsafe, shouldn't occur if logging is written correctly - if (c == args.length) - return msg; - // Replace arg in pattern - Object arg = args[c]; - String argStr = arg == null ? "null" : arg.toString(); - msg = msg.replaceFirst("\\{}", Matcher.quoteReplacement(argStr)); - c++; - } - return msg; - } - private static String getMvnName() { switch (OperatingSystem.CURRENT_OS) { - case LINUX: return "linux"; - case OSX: return "mac"; - default: return "win"; + case LINUX: + return "linux"; + case OSX: + return "mac"; + default: + return "win"; } } @@ -321,12 +327,12 @@ public class SelfDependencyPatcher { Field[] fields; try { Method m = Class.class.getDeclaredMethod("getDeclaredFieldsImpl"); - m.setAccessible(true); + ReflectionHelper.setAccessible(m); fields = (Field[]) m.invoke(klass); } catch (NoSuchMethodException | InvocationTargetException ex) { try { Method m = Class.class.getDeclaredMethod("getDeclaredFields0", Boolean.TYPE); - m.setAccessible(true); + ReflectionHelper.setAccessible(m); fields = (Field[]) m.invoke(klass, false); } catch (InvocationTargetException | NoSuchMethodException ex1) { ex.addSuppressed(ex1); @@ -337,7 +343,7 @@ public class SelfDependencyPatcher { for (Field field : fields) { String name = field.getName(); if ("fieldFilterMap".equals(name) || "methodFilterMap".equals(name)) { - field.setAccessible(true); + ReflectionHelper.setAccessible(field); field.set(null, new HashMap<>(0)); if (++c == 2) { return; @@ -346,33 +352,80 @@ public class SelfDependencyPatcher { } throw new RuntimeException("One of field patches did not apply properly. " + "Expected to patch two fields, but patched: " + c); - } catch (IllegalAccessException ex) { + } catch (IllegalAccessException | InvocationTargetException ex) { throw new RuntimeException("Unable to patch reflection filters", ex); } } - private static int vmVersion = -1; - - /** - * @return running VM version. - */ - public static int getVmVersion() { - if (vmVersion < 0) { - // Check for class version, ez - String property = System.getProperty("java.class.version", ""); - if (!property.isEmpty()) - return vmVersion = (int) (Float.parseFloat(property) - 44); - // Odd, not found. Try the spec version - LOG.warning("Using fallback vm-version fetch, no value for 'java.class.version'"); - property = System.getProperty("java.vm.specification.version", ""); - if (property.contains(".")) - return vmVersion = (int) Float.parseFloat(property.substring(property.indexOf('.') + 1)); - else if (!property.isEmpty()) - return vmVersion = Integer.parseInt(property); - // Very odd - LOG.warning("Fallback vm-version fetch failed, defaulting to 8"); - return 8; + public static class PatchException extends Exception { + PatchException(String message, Throwable cause) { + super(message, cause); + } + } + + public static class IncompatibleVersionException extends Exception { + } + + public static class ProgressFrame extends JDialog { + private int totalTasks = 0; + private int finishedTasks = 0; + + private final JProgressBar progressBar; + private final JLabel progressText; + + public ProgressFrame(String title) { + super(); + + JPanel panel = new JPanel(); + + setResizable(false); + setTitle(title); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setBounds(100, 100, 600, 150); + setContentPane(panel); + setLocationRelativeTo(null); + + GridBagLayout gridBagLayout = new GridBagLayout(); + gridBagLayout.columnWidths = new int[]{600, 0}; + gridBagLayout.rowHeights = new int[]{0, 0, 0, 200}; + gridBagLayout.columnWeights = new double[]{1.0, Double.MIN_VALUE}; + gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 1.0}; + panel.setLayout(gridBagLayout); + + progressText = new JLabel(""); + GridBagConstraints gbc_lblProgressText = new GridBagConstraints(); + gbc_lblProgressText.insets = new Insets(10, 0, 5, 0); + gbc_lblProgressText.gridx = 0; + gbc_lblProgressText.gridy = 0; + panel.add(progressText, gbc_lblProgressText); + + progressBar = new JProgressBar(); + GridBagConstraints gbc_progressBar = new GridBagConstraints(); + gbc_progressBar.insets = new Insets(0, 25, 5, 25); + gbc_progressBar.fill = GridBagConstraints.HORIZONTAL; + gbc_progressBar.gridx = 0; + gbc_progressBar.gridy = 1; + panel.add(progressBar, gbc_progressBar); + + JButton btnCancel = new JButton(i18n("button.cancel")); + btnCancel.addActionListener(e -> { + System.exit(-1); + }); + GridBagConstraints gbc_btnCancel = new GridBagConstraints(); + gbc_btnCancel.insets = new Insets(0, 25, 5, 25); + gbc_btnCancel.fill = GridBagConstraints.HORIZONTAL; + gbc_btnCancel.gridx = 0; + gbc_btnCancel.gridy = 2; + panel.add(btnCancel, gbc_btnCancel); + } + + public void setStatus(String status) { + progressText.setText(status); + } + + public void setProgress(int current, int total) { + progressBar.setValue(current); + progressBar.setMaximum(total); } - return vmVersion; } } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 89a3f996c..b4f23f4ee 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -113,7 +113,8 @@ extension.mod=Mod file extension.png=Image file extension.sh=Bash shell -fatal.missing_javafx=JavaFX is missing.\nIf you are using Java 11 or later, please downgrade to Java 8 or 10.\nIf you are using OpenJDK, please ensure OpenJFX is included. +fatal.javafx.incompatible=Application cannot patch JavaFX on current Java environment below 11.\nPlease run this app using JDK 11 or higher or a JDK with JavaFX bundled. +fatal.javafx.missing=JavaFX is missing.\nIf you are using Java 11 or later, please downgrade to Java 8 or 10.\nIf you are using OpenJDK, please ensure OpenJFX is included. fatal.missing_dst_root_ca_x3=The DST Root CA X3 certificate is missing on the current Java platform.\nYou can still use Hello Minecraft! Launcher, but it will be unable to connect to some sites (such as sites that use certificates issued by Let's Encrypt), which may cause the launcher not to function properly.\nPlease upgrade your Java Runtime to 8u101 or later to resolve the problem. fatal.config_loading_failure=The configuration is not accessible.\nPlease ensure Hello Minecraft! Launcher has read and write access to "%s" and the files in it. fatal.migration_requires_manual_reboot=The update is complete. Please reopen Hello Minecraft! Launcher. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 1657c2925..a5509d81a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -113,7 +113,8 @@ extension.mod=模組檔案 extension.png=圖片檔案 extension.sh=Bash 指令碼 -fatal.missing_javafx=找不到 JavaFX。\n如果您使用的是 Java 11 或更高版本,請降級到 Java 8 或 10。\n如果您使用的是 OpenJDK,請確保其包含 OpenJFX。 +fatal.javafx.incompatible=缺少 JavaFX 運行環境。\nHMCL 無法在低於 Java 11 且缺少 JavaFX 的 Java 環境上自行補全 JavaFX 運行環境。\n請更換如 Oracle Java 8 等包含 JavaFX 的 Java 運行環境,或者更新到 Java 11 或更高版本。 +fatal.javafx.missing=找不到 JavaFX。\n如果您使用的是 Java 11 或更高版本,請降級到 Java 8 或 10。\n如果您使用的是 OpenJDK,請確保其包含 OpenJFX。 fatal.missing_dst_root_ca_x3=目前的 Java 平台缺少 DST Root CA X3 憑證。\n您依然可以使用 Hello Minecraft! Launcher,但會無法連線到部分網站 (如使用 Lets Encrypt 憑證的站台),這可能會使 Hello Minecraft! Launcher 無法正常運作。\n請將您的 Java 升級到 8u101 以上來解決此問題。 fatal.config_loading_failure=Hello Minecraft! Launcher 無法載入設定檔案。\n請確保 Hello Minecraft! Launcher 對 "%s" 目錄及該目錄下的檔案擁有讀寫權限。 fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即將升級完成,請重新開啟 Hello Minecraft! Launcher。 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index b2795a3d3..5a0345f79 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -113,7 +113,8 @@ extension.mod=模组文件 extension.png=图片文件 extension.sh=Bash 脚本 -fatal.missing_javafx=JavaFX 缺失。\n如果您使用的是 Java 11 或更高版本,请降级到 Java 8 或 10。\n如果您使用的是 OpenJDK,请确保其包含 OpenJFX。 +fatal.javafx.incompatible=缺少 JavaFX 运行环境。\nHMCL 无法在低于 Java 11 且缺少 JavaFX 的 Java 环境上自行补全 JavaFX 运行环境。\n请更换如 Oracle Java 8 等包含 JavaFX 的 Java 运行环境,或者更新到 Java 11 或更高版本。 +fatal.javafx.missing=缺少 JavaFX 运行环境。\n如果您使用的是 Java 11 或更高版本,请降级到 Oracle Java 8 或 10,或者安装 Bell-Soft Liberica Full JRE。\n如果您使用的是其他 OpenJDK,请确保其包含 OpenJFX。 fatal.missing_dst_root_ca_x3=当前 Java 平台缺少 DST Root CA X3 证书。\n您依然可以使用 Hello Minecraft! Launcher,但将无法连接到部分站点(如使用 Lets Encrypt 证书的站点),这可能会使 HMCL 无法正常工作。\n请将您的 Java 升级到 8u101 以上以解决此问题。 fatal.config_loading_failure=Hello Minecraft! Launcher 无法加载配置文件。\n请确保 Hello Minecraft! Launcher 对 "%s" 目录及该目录下的文件拥有读写权限。 fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即将完成升级,请重新打开 Hello Minecraft! Launcher。 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index e0e8b73ff..990c9fe61 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -231,7 +231,7 @@ public class DefaultLauncher extends Launcher { } private final Map> forbiddens = mapOf( - pair("-Xincgc", () -> options.getJava().getParsedVersion() >= JavaVersion.JAVA_9_AND_LATER) + pair("-Xincgc", () -> options.getJava().getParsedVersion() >= JavaVersion.JAVA_9) ); protected Map> getForbiddens() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java index 5b0b8b076..cc0ec8a5e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java @@ -66,7 +66,7 @@ public abstract class TaskExecutor { return cancelled.get(); } - public int getRunningTasks() { + public int getTaskCount() { return totTask.get(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java index d6d5d84ab..3fbacd57e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java @@ -17,6 +17,9 @@ */ package org.jackhuang.hmcl.util; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.function.Predicate; /** @@ -24,6 +27,19 @@ import java.util.function.Predicate; * @author huangyuhui */ public final class ReflectionHelper { + private static Method accessible0; + + static { + try { + accessible0 = AccessibleObject.class.getDeclaredMethod("setAccessible0", boolean.class); + accessible0.setAccessible(true); + } catch (Throwable ex) { + } + } + + public static void setAccessible(AccessibleObject obj) throws InvocationTargetException, IllegalAccessException { + accessible0.invoke(obj, true); + } /** * Get caller, this method is caller sensitive. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java index c2cafc864..180bfdf7c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java @@ -80,6 +80,7 @@ public final class JavaVersion { /** * The major version of Java installation. * + * @see org.jackhuang.hmcl.util.platform.JavaVersion#JAVA_9 * @see org.jackhuang.hmcl.util.platform.JavaVersion#JAVA_8 * @see org.jackhuang.hmcl.util.platform.JavaVersion#JAVA_7 * @see org.jackhuang.hmcl.util.platform.JavaVersion#UNKNOWN @@ -92,15 +93,15 @@ public final class JavaVersion { private static final Pattern VERSION = Pattern.compile("^(?[0-9]+)"); public static final int UNKNOWN = -1; - public static final int JAVA_7 = 70; - public static final int JAVA_8 = 80; - public static final int JAVA_9_AND_LATER = 90; + public static final int JAVA_7 = 7; + public static final int JAVA_8 = 8; + public static final int JAVA_9 = 9; private static int parseVersion(String version) { Matcher matcher = VERSION.matcher(version); if (matcher.find()) { int head = Lang.parseInt(matcher.group(), -1); - if (head > 1) return JAVA_9_AND_LATER; + if (head > 1) return head; } if (version.contains("1.8")) return JAVA_8; diff --git a/build.gradle b/build.gradle index 35f8059c5..b74c0c7e5 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,14 @@ subprojects { compile group: 'com.nqzero', name: 'permit-reflect', version: '0.3' compileOnly group: 'org.jetbrains', name: 'annotations', version: '16.0.3' +// compileOnly group: 'org.openjfx', name: 'javafx-base', version: '15', classifier: 'win' +// compileOnly group: 'org.openjfx', name: 'javafx-controls', version: '15', classifier: 'win' +// compileOnly group: 'org.openjfx', name: 'javafx-fxml', version: '15', classifier: 'win' +// compileOnly group: 'org.openjfx', name: 'javafx-graphics', version: '15', classifier: 'win' +// compileOnly group: 'org.openjfx', name: 'javafx-media', version: '15', classifier: 'win' +// compileOnly group: 'org.openjfx', name: 'javafx-swing', version: '15', classifier: 'win' +// compileOnly group: 'org.openjfx', name: 'javafx-web', version: '15', classifier: 'win' + testCompile group: 'junit', name: 'junit', version: '4.12' }