feat: Add progress gui for downloading JavaFX
This commit is contained in:
@@ -25,6 +25,7 @@ import org.jackhuang.hmcl.task.Schedulers;
|
|||||||
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
|
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||||
|
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||||
import org.jackhuang.hmcl.util.CrashReporter;
|
import org.jackhuang.hmcl.util.CrashReporter;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
@@ -84,6 +85,10 @@ public final class Launcher extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
if (UpdateHandler.processArguments(args)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
|
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
|
||||||
AsyncTaskExecutor.setUncaughtExceptionHandler(new CrashReporter(false));
|
AsyncTaskExecutor.setUncaughtExceptionHandler(new CrashReporter(false));
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl;
|
package org.jackhuang.hmcl;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
|
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
|
||||||
|
|
||||||
@@ -26,6 +25,8 @@ import javax.swing.*;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -36,6 +37,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||||
@@ -59,13 +61,15 @@ public final class Main {
|
|||||||
|
|
||||||
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
||||||
|
|
||||||
checkJavaFX();
|
checkJavaFX(classLoader -> {
|
||||||
|
try {
|
||||||
if (UpdateHandler.processArguments(args)) {
|
Class<?> c = Class.forName("org.jackhuang.hmcl.Launcher", true, classLoader);
|
||||||
return;
|
Method method = c.getDeclaredMethod("main");
|
||||||
}
|
method.invoke(null, (Object) args);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
Launcher.main(args);
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkDirectoryPath() {
|
private static void checkDirectoryPath() {
|
||||||
@@ -77,8 +81,16 @@ public final class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkJavaFX() {
|
private static void checkJavaFX(Consumer<ClassLoader> runnable) {
|
||||||
SelfDependencyPatcher.patch();
|
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() {
|
private static void checkDSTRootCAX3() {
|
||||||
|
|||||||
@@ -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.
|
// 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.getMainClass().equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)
|
||||||
&& version.getLibraries().stream()
|
&& version.getLibraries().stream()
|
||||||
.filter(library -> "launchwrapper".equals(library.getArtifactId()))
|
.filter(library -> "launchwrapper".equals(library.getArtifactId()))
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package org.jackhuang.hmcl.util;
|
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 org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -12,15 +15,18 @@ import java.net.URL;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.List;
|
||||||
import java.util.*;
|
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.logging.Level;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
|
|
||||||
import static java.lang.Class.forName;
|
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.Metadata.HMCL_DIRECTORY;
|
||||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
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.
|
* Utility for patching self when missing dependencies.
|
||||||
@@ -83,32 +89,39 @@ public class SelfDependencyPatcher {
|
|||||||
/**
|
/**
|
||||||
* Patch in any missing dependencies, if any.
|
* Patch in any missing dependencies, if any.
|
||||||
*/
|
*/
|
||||||
public static void patch() {
|
public static void runInJavaFxEnvironment(Consumer<ClassLoader> runnable) throws PatchException, IncompatibleVersionException {
|
||||||
if (getVmVersion() > 8) {
|
if (CURRENT_JAVA.getParsedVersion() > 8) {
|
||||||
Permit.godMode();
|
|
||||||
Permit.unLog();
|
|
||||||
patchReflectionFilters();
|
patchReflectionFilters();
|
||||||
}
|
}
|
||||||
// Do nothing if JavaFX is detected
|
// Do nothing if JavaFX is detected
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
forName("javafx.application.Platform", false, ClassLoader.getSystemClassLoader());
|
forName("javafx.application.Application");
|
||||||
return;
|
runnable.accept(SelfDependencyPatcher.class.getClassLoader());
|
||||||
} catch(Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
} catch(UnsupportedClassVersionError error) {
|
} catch (UnsupportedClassVersionError error) {
|
||||||
// Loading the JavaFX class was unsupported.
|
// Loading the JavaFX class was unsupported.
|
||||||
// We are probably on 8 and its on 11
|
// We are probably on 8 and its on 11
|
||||||
showIncompatibleVersion();
|
throw new IncompatibleVersionException();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// So the problem with Java 8 is that some distributions DO NOT BUNDLE JAVAFX
|
// 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
|
// 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.
|
// So you're out of luck unless you change your JDK or update Java.
|
||||||
if (getVmVersion() < 11) {
|
if (CURRENT_JAVA.getParsedVersion() < 11) {
|
||||||
showIncompatibleVersion();
|
throw new IncompatibleVersionException();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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+
|
// Otherwise we're free to download in Java 11+
|
||||||
LOG.info("Missing JavaFX dependencies, attempting to patch in missing classes");
|
LOG.info("Missing JavaFX dependencies, attempting to patch in missing classes");
|
||||||
// Check if dependencies need to be downloaded
|
// Check if dependencies need to be downloaded
|
||||||
@@ -116,9 +129,8 @@ public class SelfDependencyPatcher {
|
|||||||
LOG.info(" - No local cache, downloading dependencies...");
|
LOG.info(" - No local cache, downloading dependencies...");
|
||||||
try {
|
try {
|
||||||
fetchDependencies();
|
fetchDependencies();
|
||||||
} catch(IOException ex) {
|
} catch (Exception ex) {
|
||||||
logError(ex, "Failed to download dependencies!");
|
throw new PatchException("Failed to download dependencies", ex);
|
||||||
System.exit(-1);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.info(" - Local cache found!");
|
LOG.info(" - Local cache found!");
|
||||||
@@ -126,17 +138,35 @@ public class SelfDependencyPatcher {
|
|||||||
// Add the dependencies
|
// Add the dependencies
|
||||||
try {
|
try {
|
||||||
loadFromCache();
|
loadFromCache();
|
||||||
} catch(IOException ex) {
|
} catch (IOException ex) {
|
||||||
logError(ex, ex.getMessage());
|
throw new PatchException("Failed to load JavaFX cache", ex);
|
||||||
System.exit(-1);
|
} catch (ReflectiveOperationException ex) {
|
||||||
} catch(ReflectiveOperationException ex) {
|
throw new PatchException("Failed to add dependencies to classpath!", ex);
|
||||||
logError(ex, "Failed to add dependencies to classpath!");
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
}
|
||||||
LOG.info(" - Done!");
|
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<ClassLoader> runnable) throws IOException, ReflectiveOperationException {
|
||||||
|
// LOG.info(" - Loading dependencies...");
|
||||||
|
// // Get Jar URLs
|
||||||
|
// List<URL> 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.
|
* Inject them into the current classpath.
|
||||||
*
|
*
|
||||||
@@ -153,7 +183,7 @@ public class SelfDependencyPatcher {
|
|||||||
try {
|
try {
|
||||||
jarUrls.add(path.toUri().toURL());
|
jarUrls.add(path.toUri().toURL());
|
||||||
} catch(MalformedURLException ex) {
|
} 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
|
// Fetch UCP of application's ClassLoader
|
||||||
@@ -162,7 +192,7 @@ public class SelfDependencyPatcher {
|
|||||||
Object appClassLoader = clsClassLoaders.getDeclaredMethod("appClassLoader").invoke(null);
|
Object appClassLoader = clsClassLoaders.getDeclaredMethod("appClassLoader").invoke(null);
|
||||||
Class<?> ucpOwner = appClassLoader.getClass();
|
Class<?> ucpOwner = appClassLoader.getClass();
|
||||||
// Field removed in 16, but still exists in parent class "BuiltinClassLoader"
|
// Field removed in 16, but still exists in parent class "BuiltinClassLoader"
|
||||||
if (getVmVersion() >= 16)
|
if (CURRENT_JAVA.getParsedVersion() >= 16)
|
||||||
ucpOwner = ucpOwner.getSuperclass();
|
ucpOwner = ucpOwner.getSuperclass();
|
||||||
Field fieldUCP = ucpOwner.getDeclaredField("ucp");
|
Field fieldUCP = ucpOwner.getDeclaredField("ucp");
|
||||||
fieldUCP.setAccessible(true);
|
fieldUCP.setAccessible(true);
|
||||||
@@ -175,40 +205,45 @@ public class SelfDependencyPatcher {
|
|||||||
addURL.invoke(ucp, url);
|
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.
|
* Download dependencies.
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException When the files cannot be fetched or saved.
|
||||||
* 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
|
// Get dir to store dependencies in
|
||||||
Path dependenciesDir = DEPENDENCIES_DIR_PATH;
|
Path dependenciesDir = DEPENDENCIES_DIR_PATH;
|
||||||
if (!Files.isDirectory(dependenciesDir)) {
|
if (!Files.isDirectory(dependenciesDir)) {
|
||||||
Files.createDirectories(dependenciesDir);
|
Files.createDirectories(dependenciesDir);
|
||||||
}
|
}
|
||||||
// Download each dependency
|
ProgressFrame dialog = new ProgressFrame(i18n("download.javafx"));
|
||||||
List<String> dependencies = getLatestDependencies();
|
|
||||||
for(String dependencyPattern : dependencies) {
|
ForkJoinTask<Void> task = ForkJoinPool.commonPool().submit(() -> {
|
||||||
String dependencyUrlPath = String.format(dependencyPattern, getMvnName());
|
// Download each dependency
|
||||||
URL depURL = new URL(dependencyUrlPath);
|
List<String> dependencies = getLatestDependencies();
|
||||||
Path dependencyFilePath = DEPENDENCIES_DIR_PATH.resolve(getFileName(dependencyUrlPath));
|
for (int i = 0; i < dependencies.size(); i++) {
|
||||||
Files.copy(depURL.openStream(), dependencyFilePath, StandardCopyOption.REPLACE_EXISTING);
|
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
|
* @param url Full url path.
|
||||||
* Full url path.
|
|
||||||
*
|
|
||||||
* @return Name of file at url.
|
* @return Name of file at url.
|
||||||
*/
|
*/
|
||||||
private static String getFileName(String url) {
|
private static String getFileName(String url) {
|
||||||
@@ -232,23 +265,22 @@ public class SelfDependencyPatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param component
|
* @param component Name of the component.
|
||||||
* Name of the component.
|
|
||||||
*
|
|
||||||
* @return Formed URL for the component.
|
* @return Formed URL for the component.
|
||||||
*/
|
*/
|
||||||
private static String jfxUrl(String component, String version) {
|
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-%s.jar
|
||||||
// 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-%s.jar",
|
||||||
return String.format("https://maven.aliyun.com/repository/central/org/openjfx/javafx-%s/%s/javafx-%s-%s",
|
component, version, component, version, getMvnName());
|
||||||
component, version, component, version) + "-%s.jar";
|
// 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.
|
* @return Latest JavaFX supported version for.
|
||||||
*/
|
*/
|
||||||
private static int getLatestSupportedJfxVersion() {
|
private static int getLatestSupportedJfxVersion() {
|
||||||
int version = getVmVersion();
|
int version = CURRENT_JAVA.getParsedVersion();
|
||||||
while (version >= 11) {
|
while (version >= 11) {
|
||||||
List<String> dependencies = JFX_DEPENDENCIES.get(version);
|
List<String> dependencies = JFX_DEPENDENCIES.get(version);
|
||||||
if (dependencies != null)
|
if (dependencies != null)
|
||||||
@@ -269,40 +301,14 @@ public class SelfDependencyPatcher {
|
|||||||
throw new AssertionError("Failed to get latest JFX artifact urls");
|
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() {
|
private static String getMvnName() {
|
||||||
switch (OperatingSystem.CURRENT_OS) {
|
switch (OperatingSystem.CURRENT_OS) {
|
||||||
case LINUX: return "linux";
|
case LINUX:
|
||||||
case OSX: return "mac";
|
return "linux";
|
||||||
default: return "win";
|
case OSX:
|
||||||
|
return "mac";
|
||||||
|
default:
|
||||||
|
return "win";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,12 +327,12 @@ public class SelfDependencyPatcher {
|
|||||||
Field[] fields;
|
Field[] fields;
|
||||||
try {
|
try {
|
||||||
Method m = Class.class.getDeclaredMethod("getDeclaredFieldsImpl");
|
Method m = Class.class.getDeclaredMethod("getDeclaredFieldsImpl");
|
||||||
m.setAccessible(true);
|
ReflectionHelper.setAccessible(m);
|
||||||
fields = (Field[]) m.invoke(klass);
|
fields = (Field[]) m.invoke(klass);
|
||||||
} catch (NoSuchMethodException | InvocationTargetException ex) {
|
} catch (NoSuchMethodException | InvocationTargetException ex) {
|
||||||
try {
|
try {
|
||||||
Method m = Class.class.getDeclaredMethod("getDeclaredFields0", Boolean.TYPE);
|
Method m = Class.class.getDeclaredMethod("getDeclaredFields0", Boolean.TYPE);
|
||||||
m.setAccessible(true);
|
ReflectionHelper.setAccessible(m);
|
||||||
fields = (Field[]) m.invoke(klass, false);
|
fields = (Field[]) m.invoke(klass, false);
|
||||||
} catch (InvocationTargetException | NoSuchMethodException ex1) {
|
} catch (InvocationTargetException | NoSuchMethodException ex1) {
|
||||||
ex.addSuppressed(ex1);
|
ex.addSuppressed(ex1);
|
||||||
@@ -337,7 +343,7 @@ public class SelfDependencyPatcher {
|
|||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
String name = field.getName();
|
String name = field.getName();
|
||||||
if ("fieldFilterMap".equals(name) || "methodFilterMap".equals(name)) {
|
if ("fieldFilterMap".equals(name) || "methodFilterMap".equals(name)) {
|
||||||
field.setAccessible(true);
|
ReflectionHelper.setAccessible(field);
|
||||||
field.set(null, new HashMap<>(0));
|
field.set(null, new HashMap<>(0));
|
||||||
if (++c == 2) {
|
if (++c == 2) {
|
||||||
return;
|
return;
|
||||||
@@ -346,33 +352,80 @@ public class SelfDependencyPatcher {
|
|||||||
}
|
}
|
||||||
throw new RuntimeException("One of field patches did not apply properly. " +
|
throw new RuntimeException("One of field patches did not apply properly. " +
|
||||||
"Expected to patch two fields, but patched: " + c);
|
"Expected to patch two fields, but patched: " + c);
|
||||||
} catch (IllegalAccessException ex) {
|
} catch (IllegalAccessException | InvocationTargetException ex) {
|
||||||
throw new RuntimeException("Unable to patch reflection filters", ex);
|
throw new RuntimeException("Unable to patch reflection filters", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int vmVersion = -1;
|
public static class PatchException extends Exception {
|
||||||
|
PatchException(String message, Throwable cause) {
|
||||||
/**
|
super(message, cause);
|
||||||
* @return running VM version.
|
}
|
||||||
*/
|
}
|
||||||
public static int getVmVersion() {
|
|
||||||
if (vmVersion < 0) {
|
public static class IncompatibleVersionException extends Exception {
|
||||||
// Check for class version, ez
|
}
|
||||||
String property = System.getProperty("java.class.version", "");
|
|
||||||
if (!property.isEmpty())
|
public static class ProgressFrame extends JDialog {
|
||||||
return vmVersion = (int) (Float.parseFloat(property) - 44);
|
private int totalTasks = 0;
|
||||||
// Odd, not found. Try the spec version
|
private int finishedTasks = 0;
|
||||||
LOG.warning("Using fallback vm-version fetch, no value for 'java.class.version'");
|
|
||||||
property = System.getProperty("java.vm.specification.version", "");
|
private final JProgressBar progressBar;
|
||||||
if (property.contains("."))
|
private final JLabel progressText;
|
||||||
return vmVersion = (int) Float.parseFloat(property.substring(property.indexOf('.') + 1));
|
|
||||||
else if (!property.isEmpty())
|
public ProgressFrame(String title) {
|
||||||
return vmVersion = Integer.parseInt(property);
|
super();
|
||||||
// Very odd
|
|
||||||
LOG.warning("Fallback vm-version fetch failed, defaulting to 8");
|
JPanel panel = new JPanel();
|
||||||
return 8;
|
|
||||||
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ extension.mod=Mod file
|
|||||||
extension.png=Image file
|
extension.png=Image file
|
||||||
extension.sh=Bash shell
|
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.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.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.
|
fatal.migration_requires_manual_reboot=The update is complete. Please reopen Hello Minecraft! Launcher.
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ extension.mod=模組檔案
|
|||||||
extension.png=圖片檔案
|
extension.png=圖片檔案
|
||||||
extension.sh=Bash 指令碼
|
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.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.config_loading_failure=Hello Minecraft! Launcher 無法載入設定檔案。\n請確保 Hello Minecraft! Launcher 對 "%s" 目錄及該目錄下的檔案擁有讀寫權限。
|
||||||
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即將升級完成,請重新開啟 Hello Minecraft! Launcher。
|
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即將升級完成,請重新開啟 Hello Minecraft! Launcher。
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ extension.mod=模组文件
|
|||||||
extension.png=图片文件
|
extension.png=图片文件
|
||||||
extension.sh=Bash 脚本
|
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.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.config_loading_failure=Hello Minecraft! Launcher 无法加载配置文件。\n请确保 Hello Minecraft! Launcher 对 "%s" 目录及该目录下的文件拥有读写权限。
|
||||||
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即将完成升级,请重新打开 Hello Minecraft! Launcher。
|
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即将完成升级,请重新打开 Hello Minecraft! Launcher。
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, Supplier<Boolean>> forbiddens = mapOf(
|
private final Map<String, Supplier<Boolean>> forbiddens = mapOf(
|
||||||
pair("-Xincgc", () -> options.getJava().getParsedVersion() >= JavaVersion.JAVA_9_AND_LATER)
|
pair("-Xincgc", () -> options.getJava().getParsedVersion() >= JavaVersion.JAVA_9)
|
||||||
);
|
);
|
||||||
|
|
||||||
protected Map<String, Supplier<Boolean>> getForbiddens() {
|
protected Map<String, Supplier<Boolean>> getForbiddens() {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public abstract class TaskExecutor {
|
|||||||
return cancelled.get();
|
return cancelled.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRunningTasks() {
|
public int getTaskCount() {
|
||||||
return totTask.get();
|
return totTask.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util;
|
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;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,6 +27,19 @@ import java.util.function.Predicate;
|
|||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
public final class ReflectionHelper {
|
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.
|
* Get caller, this method is caller sensitive.
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public final class JavaVersion {
|
|||||||
/**
|
/**
|
||||||
* The major version of Java installation.
|
* 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_8
|
||||||
* @see org.jackhuang.hmcl.util.platform.JavaVersion#JAVA_7
|
* @see org.jackhuang.hmcl.util.platform.JavaVersion#JAVA_7
|
||||||
* @see org.jackhuang.hmcl.util.platform.JavaVersion#UNKNOWN
|
* @see org.jackhuang.hmcl.util.platform.JavaVersion#UNKNOWN
|
||||||
@@ -92,15 +93,15 @@ public final class JavaVersion {
|
|||||||
private static final Pattern VERSION = Pattern.compile("^(?<version>[0-9]+)");
|
private static final Pattern VERSION = Pattern.compile("^(?<version>[0-9]+)");
|
||||||
|
|
||||||
public static final int UNKNOWN = -1;
|
public static final int UNKNOWN = -1;
|
||||||
public static final int JAVA_7 = 70;
|
public static final int JAVA_7 = 7;
|
||||||
public static final int JAVA_8 = 80;
|
public static final int JAVA_8 = 8;
|
||||||
public static final int JAVA_9_AND_LATER = 90;
|
public static final int JAVA_9 = 9;
|
||||||
|
|
||||||
private static int parseVersion(String version) {
|
private static int parseVersion(String version) {
|
||||||
Matcher matcher = VERSION.matcher(version);
|
Matcher matcher = VERSION.matcher(version);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
int head = Lang.parseInt(matcher.group(), -1);
|
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"))
|
if (version.contains("1.8"))
|
||||||
return JAVA_8;
|
return JAVA_8;
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ subprojects {
|
|||||||
compile group: 'com.nqzero', name: 'permit-reflect', version: '0.3'
|
compile group: 'com.nqzero', name: 'permit-reflect', version: '0.3'
|
||||||
compileOnly group: 'org.jetbrains', name: 'annotations', version: '16.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'
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user