Compare commits

..
17 Commits
Author SHA1 Message Date
huangyuhui eba0990538 Collect log when launcher crashed 2018-03-01 01:02:05 +08:00
huangyuhui 1bcef2bab0 Added event logs. Cloese #244 2018-03-01 00:27:07 +08:00
huangyuhui f83777e1d2 Terminate when exclaimation mark is in the path 2018-02-28 23:34:31 +08:00
huangyuhui 9fa48090ce Show indeterminate progress bar when loading versions in MainPage 2018-02-28 22:15:46 +08:00
huangyuhui 7c8ac4fee8 more precise warning in authlib injector add server page 2018-02-28 20:36:32 +08:00
huangyuhui e75ad64483 Fixed IllegalStateException when clicked the empty space of list in InstallTypePage 2018-02-27 23:27:26 +08:00
huangyuhui 17d1af181d Profile page title changed to display name 2018-02-27 23:25:10 +08:00
huangyuhui a72d27a431 May fix VersionNotFoundException when building main page version items 2018-02-27 23:18:19 +08:00
huangyuhui 4e9fc57aa4 Fix NullPointerException when launcher launched two game at the same time 2018-02-27 23:12:53 +08:00
huangyuhui 494b27cc22 May fix RemoteVersion not found exception 2018-02-27 23:05:05 +08:00
huangyuhui 3606186975 Fix NullPointerException when selected an version item in download.VersionsPage 2018-02-27 20:05:05 +08:00
huangyuhui 41dbdfc016 Fix NullPointerException when refreshing versions again 2018-02-27 19:58:43 +08:00
huangyuhui 655e5ca7e0 Fixed missed encoding in Log4jHandler 2018-02-27 11:13:07 +08:00
huangyuhui 3067d323de Fixed NullPointerException when switched profile 2018-02-26 20:16:14 +08:00
huangyuhui 1404a080ad library download optimization 2018-02-26 19:56:06 +08:00
huangyuhui cbb2a1b575 Unique duplicated libraries in minecraft json file 2018-02-26 11:21:34 +08:00
huangyuhui 30b276ec1a Forge xz library supported 2018-02-26 11:14:53 +08:00
113 changed files with 1369 additions and 754 deletions

No files matched your search

@@ -0,0 +1,144 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.upgrade.AppDataUpgrader;
import org.jackhuang.hmcl.upgrade.IUpgrader;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.*;
import java.io.File;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public final class Launcher extends Application {
@Override
public void start(Stage primaryStage) {
Thread.currentThread().setUncaughtExceptionHandler(CRASH_REPORTER);
try {
// When launcher visibility is set to "hide and reopen" without Platform.implicitExit = false,
// Stage.show() cannot work again because JavaFX Toolkit have already shut down.
Platform.setImplicitExit(false);
Controllers.initialize(primaryStage);
primaryStage.setResizable(false);
primaryStage.setScene(Controllers.getScene());
primaryStage.show();
} catch (Throwable e) {
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
}
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
try {
// NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher");
Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER;
UPGRADER.parseArguments(VersionNumber.asVersion(VERSION), Arrays.asList(args));
Logging.LOG.info("*** " + TITLE + " ***");
UPDATE_CHECKER.process(false)
.then(Task.of(Schedulers.javafx(), () -> {
if (UPDATE_CHECKER.isOutOfDate())
Controllers.showUpdate();
}))
.start();
launch(args);
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
}
public static void stopApplication() {
JFXUtilities.runInFX(() -> {
stopWithoutPlatform();
Platform.exit();
});
}
public static void stopWithoutPlatform() {
JFXUtilities.runInFX(() -> {
if (Controllers.getStage() == null)
return;
Controllers.getStage().close();
Logging.LOG.info("Shutting down executor services.");
Schedulers.shutdown();
Controllers.shutdown();
Lang.executeDelayed(OperatingSystem::forceGC, TimeUnit.SECONDS, 5, true);
});
}
public static File getWorkingDirectory(String folder) {
String home = System.getProperty("user.home", ".");
switch (OperatingSystem.CURRENT_OS) {
case LINUX:
return new File(home, "." + folder + "/");
case WINDOWS:
String appdata = System.getenv("APPDATA");
return new File(Lang.nonNull(appdata, home), "." + folder + "/");
case OSX:
return new File(home, "Library/Application Support/" + folder);
default:
return new File(home, folder + "/");
}
}
public static String i18n(String key) {
try {
return RESOURCE_BUNDLE.getString(key);
} catch (Exception e) {
Logging.LOG.log(Level.SEVERE, "Cannot find key " + key + " in resource bundle", e);
return key;
}
}
public static String i18n(String key, Object... formatArgs) {
return String.format(i18n(key), formatArgs);
}
public static final File MINECRAFT_DIRECTORY = getWorkingDirectory("minecraft");
public static final File HMCL_DIRECTORY = getWorkingDirectory("hmcl");
public static final String VERSION = "@HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING@";
public static final String NAME = "HMCL";
public static final String TITLE = NAME + " " + VERSION;
public static final ResourceBundle RESOURCE_BUNDLE = Settings.INSTANCE.getLocale().getResourceBundle();
public static final UpdateChecker UPDATE_CHECKER = new UpdateChecker(VersionNumber.asVersion(VERSION));
public static final IUpgrader UPGRADER = new AppDataUpgrader();
public static final CrashReporter CRASH_REPORTER = new CrashReporter();
public static final String CONTACT = "http://huangyuhui.duapp.com/hmcl.php";
public static final String PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
}
+14 -116
View File
@@ -17,126 +17,24 @@
*/
package org.jackhuang.hmcl;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.upgrade.AppDataUpgrader;
import org.jackhuang.hmcl.upgrade.IUpgrader;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.*;
import org.apache.commons.compress.utils.Charsets;
import org.jackhuang.hmcl.util.Logging;
import javax.swing.JOptionPane;
import java.io.File;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public final class Main extends Application {
@Override
public void start(Stage primaryStage) {
Thread.currentThread().setUncaughtExceptionHandler(CRASH_REPORTER);
try {
// When launcher visibility is set to "hide and reopen" without Platform.implicitExit = false,
// Stage.show() cannot work again because JavaFX Toolkit have already shut down.
Platform.setImplicitExit(false);
Controllers.initialize(primaryStage);
primaryStage.setResizable(false);
primaryStage.setScene(Controllers.getScene());
primaryStage.show();
} catch (Throwable e) {
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
}
public final class Main {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
String currentDirectory = new File("").getAbsolutePath();
Logging.LOG.info("Current directory: " + currentDirectory);
if (currentDirectory.contains("!")) {
Logging.LOG.severe("Exclamation mark(!) is not allowed in the path where HMCL is in. Forcibly exit.");
try {
// NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher");
Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER;
UPGRADER.parseArguments(VersionNumber.asVersion(VERSION), Arrays.asList(args));
Logging.LOG.info("*** " + TITLE + " ***");
UPDATE_CHECKER.process(false)
.then(Task.of(Schedulers.javafx(), () -> {
if (UPDATE_CHECKER.isOutOfDate())
Controllers.showUpdate();
}))
.start();
launch(args);
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
// No Chinese translation because both Swing and JavaFX cannot render Chinese character properly when exclamation mark exists in the path.
String message = "Exclamation mark(!) is not allowed in the path where HMCL is in.\nThe path is " + currentDirectory;
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
} else
Launcher.main(args);
}
public static void stopApplication() {
JFXUtilities.runInFX(() -> {
stopWithoutPlatform();
Platform.exit();
});
}
public static void stopWithoutPlatform() {
JFXUtilities.runInFX(() -> {
Controllers.getStage().close();
Logging.LOG.info("Shutting down executor services.");
Schedulers.shutdown();
Controllers.shutdown();
Lang.executeDelayed(OperatingSystem::forceGC, TimeUnit.SECONDS, 5, true);
});
}
public static String i18n(String key) {
try {
return RESOURCE_BUNDLE.getString(key);
} catch (Exception e) {
Logging.LOG.log(Level.SEVERE, "Cannot find key " + key + " in resource bundle", e);
return key;
}
}
public static String i18n(String key, Object... formatArgs) {
return String.format(i18n(key), formatArgs);
}
public static File getWorkingDirectory(String folder) {
String home = System.getProperty("user.home", ".");
switch (OperatingSystem.CURRENT_OS) {
case LINUX:
return new File(home, "." + folder + "/");
case WINDOWS:
String appdata = System.getenv("APPDATA");
return new File(Lang.nonNull(appdata, home), "." + folder + "/");
case OSX:
return new File(home, "Library/Application Support/" + folder);
default:
return new File(home, folder + "/");
}
}
public static final File MINECRAFT_DIRECTORY = getWorkingDirectory("minecraft");
public static final File HMCL_DIRECTORY = getWorkingDirectory("hmcl");
public static final String VERSION = "@HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING@";
public static final String NAME = "HMCL";
public static final String TITLE = NAME + " " + VERSION;
public static final ResourceBundle RESOURCE_BUNDLE = Settings.INSTANCE.getLocale().getResourceBundle();
public static final UpdateChecker UPDATE_CHECKER = new UpdateChecker(VersionNumber.asVersion(VERSION));
public static final IUpgrader UPGRADER = new AppDataUpgrader();
public static final CrashReporter CRASH_REPORTER = new CrashReporter();
public static final String CONTACT = "http://huangyuhui.duapp.com/hmcl.php";
public static final String PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
}
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.game;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.Image;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.Texture;
@@ -43,7 +43,7 @@ public final class AccountHelper {
public static final AccountHelper INSTANCE = new AccountHelper();
private AccountHelper() {}
public static final File SKIN_DIR = new File(Main.HMCL_DIRECTORY, "skins");
public static final File SKIN_DIR = new File(Launcher.HMCL_DIRECTORY, "skins");
public static void loadSkins() {
loadSkins(Proxy.NO_PROXY);
@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.launch.DefaultLauncher;
import org.jackhuang.hmcl.launch.ProcessListener;
@@ -45,7 +45,7 @@ public final class HMCLGameLauncher extends DefaultLauncher {
protected void appendJvmArgs(List<String> result) {
super.appendJvmArgs(result);
result.add("-Dminecraft.launcher.version=" + Main.VERSION);
result.add("-Dminecraft.launcher.brand=" + Main.NAME);
result.add("-Dminecraft.launcher.version=" + Launcher.VERSION);
result.add("-Dminecraft.launcher.brand=" + Launcher.NAME);
}
}
@@ -26,7 +26,6 @@ import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
@@ -19,13 +19,12 @@ package org.jackhuang.hmcl.game;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.application.Platform;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.ServerDisconnectException;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.MaintainTask;
import org.jackhuang.hmcl.launch.*;
import org.jackhuang.hmcl.mod.CurseCompletionTask;
import org.jackhuang.hmcl.setting.LauncherVisibility;
@@ -55,30 +54,27 @@ public final class LauncherHelper {
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
private final TaskExecutorDialogPane launchingStepsPane = new TaskExecutorDialogPane(() -> {});
public void launch(String selectedVersion, File scriptFile) {
Profile profile = Settings.INSTANCE.getSelectedProfile();
GameRepository repository = profile.getRepository();
Account account = Settings.INSTANCE.getSelectedAccount();
public void launch(Profile profile, Account account, String selectedVersion, File scriptFile) {
if (account == null)
throw new IllegalStateException("No account");
throw new IllegalArgumentException("No account");
Version version = repository.getVersion(selectedVersion);
GameRepository repository = profile.getRepository();
Version version = repository.getResolvedVersion(selectedVersion);
VersionSetting setting = profile.getVersionSetting(selectedVersion);
Platform.runLater(() -> {
try {
checkGameState(profile, setting, version, () -> Schedulers.newThread().schedule(() -> launch0(selectedVersion, scriptFile)));
checkGameState(profile, setting, version, () -> Schedulers.newThread().schedule(() -> launch0(profile, account, selectedVersion, scriptFile)));
} catch (InterruptedException ignore) {
}
});
}
private void launch0(String selectedVersion, File scriptFile) {
Profile profile = Settings.INSTANCE.getSelectedProfile();
private void launch0(Profile profile, Account account, String selectedVersion, File scriptFile) {
GameRepository repository = profile.getRepository();
DefaultDependencyManager dependencyManager = profile.getDependency();
Version version = repository.getResolvedVersion(selectedVersion);
Account account = Settings.INSTANCE.getSelectedAccount();
VersionSetting setting = profile.getVersionSetting(selectedVersion);
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
@@ -93,7 +89,7 @@ public final class LauncherHelper {
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
.then(new CurseCompletionTask(dependencyManager, selectedVersion))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
.then(Task.of(Main.i18n("account.methods"), variables -> {
.then(Task.of(Launcher.i18n("account.methods"), variables -> {
try {
try {
variables.set("account", account.logIn());
@@ -117,12 +113,12 @@ public final class LauncherHelper {
.then(variables -> {
DefaultLauncher launcher = variables.get("launcher");
if (scriptFile == null) {
return new LaunchTask<>(launcher::launch).setName(Main.i18n("version.launch"));
return new LaunchTask<>(launcher::launch).setName(Launcher.i18n("version.launch"));
} else {
return new LaunchTask<>(() -> {
launcher.makeLaunchScript(scriptFile);
return null;
}).setName(Main.i18n("version.launch_script"));
}).setName(Launcher.i18n("version.launch_script"));
}
})
.then(Task.of(variables -> {
@@ -130,7 +126,7 @@ public final class LauncherHelper {
ManagedProcess process = variables.get(LaunchTask.LAUNCH_ID);
PROCESSES.add(process);
if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE)
Main.stopApplication();
Launcher.stopApplication();
else
launchingStepsPane.setCancel(() -> {
process.stop();
@@ -138,7 +134,7 @@ public final class LauncherHelper {
});
} else
Platform.runLater(() ->
Controllers.dialog(Main.i18n("version.launch_script.success", scriptFile.getAbsolutePath())));
Controllers.dialog(Launcher.i18n("version.launch_script.success", scriptFile.getAbsolutePath())));
}))
.executor();
@@ -160,7 +156,7 @@ public final class LauncherHelper {
Platform.runLater(() -> {
if (executor.getLastException() != null)
Controllers.dialog(I18nException.getStackTrace(executor.getLastException()),
scriptFile == null ? Main.i18n("launch.failed") : Main.i18n("version.launch_script.failed"),
scriptFile == null ? Launcher.i18n("launch.failed") : Launcher.i18n("version.launch_script.failed"),
MessageBox.ERROR_MESSAGE, Controllers::closeDialog);
else
Controllers.closeDialog();
@@ -180,35 +176,35 @@ public final class LauncherHelper {
VersionNumber gameVersion = VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse("Unknown"));
JavaVersion java = setting.getJavaVersion();
if (java == null) {
Controllers.dialog(Main.i18n("launch.wrong_javadir"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.wrong_javadir"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
setting.setJava(null);
java = JavaVersion.fromCurrentEnvironment();
flag = true;
}
if (java.getParsedVersion() < JavaVersion.JAVA_8) {
Controllers.dialog(Main.i18n("launch.advice.newer_java"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.advice.newer_java"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
if (java.getParsedVersion() >= JavaVersion.JAVA_9 && gameVersion.compareTo(VersionNumber.asVersion("1.12.5")) < 0 && version.getMainClass().contains("launchwrapper")) {
Controllers.dialog(Main.i18n("launch.advice.java9"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
Controllers.dialog(Launcher.i18n("launch.advice.java9"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
suggest = false;
flag = true;
}
if (java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
org.jackhuang.hmcl.util.Platform.IS_64_BIT) {
Controllers.dialog(Main.i18n("launch.advice.different_platform"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.advice.different_platform"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
if (java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
setting.getMaxMemory() > 1.5 * 1024) {
Controllers.dialog(Main.i18n("launch.advice.too_large_memory_for_32bit"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.advice.too_large_memory_for_32bit"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
if (OperatingSystem.TOTAL_MEMORY > 0 && OperatingSystem.TOTAL_MEMORY < setting.getMaxMemory()) {
Controllers.dialog(Main.i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
@@ -245,8 +241,8 @@ public final class LauncherHelper {
Platform.runLater(() -> {
// Shut down the platform when user closed log window.
Platform.setImplicitExit(true);
// If we use Main.stop(), log window will be halt immediately.
Main.stopWithoutPlatform();
// If we use Launcher.stop(), log window will be halt immediately.
Launcher.stopWithoutPlatform();
});
break;
}
@@ -264,11 +260,11 @@ public final class LauncherHelper {
try {
setResult(supplier.get());
} catch (PermissionException e) {
throw new I18nException(Main.i18n("launch.failed.executable_permission"), e);
throw new I18nException(Launcher.i18n("launch.failed.executable_permission"), e);
} catch (ProcessCreationException e) {
throw new I18nException(Main.i18n("launch.failed.creating_process") + e.getLocalizedMessage(), e);
throw new I18nException(Launcher.i18n("launch.failed.creating_process") + e.getLocalizedMessage(), e);
} catch (NotDecompressingNativesException e) {
throw new I18nException(Main.i18n("launch.failed.decompressing_natives") + e.getLocalizedMessage(), e);
throw new I18nException(Launcher.i18n("launch.failed.decompressing_natives") + e.getLocalizedMessage(), e);
}
}
@@ -385,10 +381,10 @@ public final class LauncherHelper {
switch (exitType) {
case JVM_ERROR:
logWindow.setTitle(Main.i18n("launch.failed.cannot_create_jvm"));
logWindow.setTitle(Launcher.i18n("launch.failed.cannot_create_jvm"));
break;
case APPLICATION_ERROR:
logWindow.setTitle(Main.i18n("launch.failed.exited_abnormally"));
logWindow.setTitle(Launcher.i18n("launch.failed.exited_abnormally"));
break;
}
@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
public enum LoadingState {
DEPENDENCIES("launch.state.dependencies"),
@@ -33,6 +33,6 @@ public enum LoadingState {
}
public String getLocalizedMessage() {
return Main.i18n(key);
return Launcher.i18n(key);
}
}
@@ -17,8 +17,7 @@
*/
package org.jackhuang.hmcl.setting;
import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
@@ -74,8 +73,8 @@ public final class Accounts {
private static String downloadAuthlibInjector() throws Exception {
AuthlibInjectorBuildInfo buildInfo = AuthlibInjectorBuildInfo.requestBuildInfo();
File jar = new File(Main.HMCL_DIRECTORY, "authlib-injector.jar");
File local = new File(Main.HMCL_DIRECTORY, "authlib-injector.txt");
File jar = new File(Launcher.HMCL_DIRECTORY, "authlib-injector.jar");
File local = new File(Launcher.HMCL_DIRECTORY, "authlib-injector.txt");
int buildNumber = 0;
try {
buildNumber = Integer.parseInt(FileUtils.readText(local));
@@ -88,17 +87,13 @@ public final class Accounts {
return jar.getAbsolutePath();
}
public static String getAuthlibInjectorServerName(String serverIp) {
public static String getAuthlibInjectorServerName(String serverIp) throws Exception {
if (AUTHLIB_INJECTOR_SERVER_NAMES.containsKey(serverIp))
return AUTHLIB_INJECTOR_SERVER_NAMES.get(serverIp);
else {
try {
AuthlibInjectorServerResponse response = Constants.GSON.fromJson(NetworkUtils.doGet(NetworkUtils.toURL(serverIp)), AuthlibInjectorServerResponse.class);
AUTHLIB_INJECTOR_SERVER_NAMES.put(serverIp, response.getMeta().getServerName());
return response.getMeta().getServerName();
} catch (JsonParseException | IOException | NullPointerException e) {
return null;
}
AuthlibInjectorServerResponse response = Constants.GSON.fromJson(NetworkUtils.doGet(NetworkUtils.toURL(serverIp)), AuthlibInjectorServerResponse.class);
AUTHLIB_INJECTOR_SERVER_NAMES.put(serverIp, response.getMeta().getServerName());
return response.getMeta().getServerName();
}
}
}
@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.setting;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.util.JavaVersion;
import java.util.*;
@@ -35,7 +35,7 @@ final class Config {
private String backgroundImage = null;
@SerializedName("commonpath")
private String commonDirectory = Main.MINECRAFT_DIRECTORY.getAbsolutePath();
private String commonDirectory = Launcher.MINECRAFT_DIRECTORY.getAbsolutePath();
@SerializedName("hasProxy")
private boolean hasProxy = false;
@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.MojangDownloadProvider;
import org.jackhuang.hmcl.util.Lang;
import java.util.Arrays;
import java.util.List;
public final class DownloadProviders {
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.ui.construct.UTF8Control;
import org.jackhuang.hmcl.util.Lang;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
public final class Profiles {
private Profiles() {
@@ -26,9 +26,9 @@ public final class Profiles {
public static String getProfileDisplayName(Profile profile) {
switch (profile.getName()) {
case Settings.DEFAULT_PROFILE:
return Main.i18n("profile.default");
return Launcher.i18n("profile.default");
case Settings.HOME_PROFILE:
return Main.i18n("profile.home");
return Launcher.i18n("profile.home");
default:
return profile.getName();
}
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.util.Lang;
import java.net.Proxy;
import java.util.Arrays;
import java.util.List;
/**
@@ -23,7 +23,7 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
@@ -525,13 +525,13 @@ public class Settings {
private void checkProfileMap() {
if (getProfileMap().isEmpty()) {
getProfileMap().put(DEFAULT_PROFILE, new Profile(DEFAULT_PROFILE));
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY));
}
}
private void onProfileChanged() {
getSelectedProfile().getRepository().refreshVersionsAsync().start();
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile()));
getSelectedProfile().getRepository().refreshVersionsAsync().start();
}
private void profileNameChanged(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.setting;
import com.google.gson.*;
import javafx.beans.InvalidationListener;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.game.LaunchOptions;
import org.jackhuang.hmcl.util.*;
@@ -482,8 +482,8 @@ public final class VersionSetting {
return new LaunchOptions.Builder()
.setGameDir(gameDir)
.setJava(javaVersion)
.setVersionName(Main.TITLE)
.setProfileName(Main.TITLE)
.setVersionName(Launcher.TITLE)
.setProfileName(Launcher.TITLE)
.setMinecraftArgs(getMinecraftArgs())
.setJavaArgs(getJavaArgs())
.setMaxMemory(getMaxMemory())
@@ -34,7 +34,7 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
@@ -49,16 +49,19 @@ import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jackhuang.hmcl.ui.construct.IconedItem;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Logging;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class AccountsPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account"));
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("account"));
@FXML
private ScrollPane scrollPane;
@@ -82,7 +85,7 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
FXUtils.smoothScrolling(scrollPane);
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"), Main.i18n("account.methods.authlib_injector"));
cboType.getItems().setAll(Launcher.i18n("account.methods.offline"), Launcher.i18n("account.methods.yggdrasil"), Launcher.i18n("account.methods.authlib_injector"));
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
txtPassword.setVisible(newValue.intValue() != 0);
lblPassword.setVisible(newValue.intValue() != 0);
@@ -98,7 +101,7 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
txtPassword.setOnAction(e -> onCreationAccept());
txtUsername.setOnAction(e -> onCreationAccept());
txtUsername.getValidators().add(new Validator(Main.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
txtUsername.getValidators().add(new Validator(Launcher.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), account -> {
for (Node node : masonryPane.getChildren())
@@ -133,7 +136,14 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
public void loadServers() {
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
.map(serverURL -> new TwoLineListItem(Accounts.getAuthlibInjectorServerName(serverURL), serverURL))
.flatMap(serverURL -> {
try {
return Stream.of(new TwoLineListItem(Accounts.getAuthlibInjectorServerName(serverURL), serverURL));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Authlib-injector server root " + serverURL + " cannot be recognized.", e);
return Stream.empty();
}
})
.collect(Collectors.toList()))
.subscribe(Task.of(Schedulers.javafx(), variables -> {
cboServers.getItems().setAll(variables.<Collection<TwoLineListItem>>get("list"));
@@ -217,25 +227,25 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
public static String accountException(Exception exception) {
if (exception instanceof InvalidCredentialsException) {
return Main.i18n("account.failed.invalid_credentials");
return Launcher.i18n("account.failed.invalid_credentials");
} else if (exception instanceof NoCharacterException) {
return Main.i18n("account.failed.no_charactor");
return Launcher.i18n("account.failed.no_charactor");
} else if (exception instanceof ServerDisconnectException) {
return Main.i18n("account.failed.connect_authentication_server");
return Launcher.i18n("account.failed.connect_authentication_server");
} else if (exception instanceof InvalidTokenException) {
return Main.i18n("account.failed.invalid_token");
return Launcher.i18n("account.failed.invalid_token");
} else if (exception instanceof InvalidPasswordException) {
return Main.i18n("account.failed.invalid_password");
return Launcher.i18n("account.failed.invalid_password");
} else {
return exception.getClass() + ": " + exception.getLocalizedMessage();
}
}
public static String accountType(Account account) {
if (account instanceof OfflineAccount) return Main.i18n("account.methods.offline");
else if (account instanceof AuthlibInjectorAccount) return Main.i18n("account.methods.authlib_injector");
else if (account instanceof YggdrasilAccount) return Main.i18n("account.methods.yggdrasil");
else throw new Error(Main.i18n("account.methods.no_method") + ": " + account);
if (account instanceof OfflineAccount) return Launcher.i18n("account.methods.offline");
else if (account instanceof AuthlibInjectorAccount) return Launcher.i18n("account.methods.authlib_injector");
else if (account instanceof YggdrasilAccount) return Launcher.i18n("account.methods.yggdrasil");
else throw new Error(Launcher.i18n("account.methods.no_method") + ": " + account);
}
private static class Selector extends BorderPane implements CharacterSelector {
@@ -248,11 +258,11 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
{
setStyle("-fx-padding: 8px;");
cancel.setText(Main.i18n("button.cancel"));
cancel.setText(Launcher.i18n("button.cancel"));
StackPane.setAlignment(cancel, Pos.BOTTOM_RIGHT);
cancel.setOnMouseClicked(e -> latch.countDown());
listBox.startCategory(Main.i18n("account.choose"));
listBox.startCategory(Launcher.i18n("account.choose"));
setCenter(listBox);
@@ -9,7 +9,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Settings;
@@ -18,14 +18,16 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.NetworkUtils;
import java.util.Collection;
import java.util.Objects;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account.injector.server"));
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("account.injector.server"));
@FXML private ScrollPane scrollPane;
@FXML private StackPane addServerContainer;
@@ -69,7 +71,14 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
spinner.setVisible(true);
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
.map(serverURL -> new AuthlibInjectorServerItem(new AuthlibInjectorServerInfo(serverURL, Accounts.getAuthlibInjectorServerName(serverURL)), this::removeServer))
.flatMap(serverURL -> {
try {
return Stream.of(new AuthlibInjectorServerItem(new AuthlibInjectorServerInfo(serverURL, Accounts.getAuthlibInjectorServerName(serverURL)), this::removeServer));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Authlib-injector server root " + serverURL + " cannot be recognized.", e);
return Stream.empty();
}
})
.collect(Collectors.toList()))
.subscribe(Task.of(Schedulers.javafx(), variables -> {
listPane.getChildren().setAll(variables.<Collection<? extends Node>>get("list"));
@@ -105,7 +114,7 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
progressBar.setVisible(true);
addServerPane.setDisable(true);
Task.ofResult("serverName", () -> Objects.requireNonNull(Accounts.getAuthlibInjectorServerName(serverIp)))
Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(serverIp))
.finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
progressBar.setVisible(false);
addServerPane.setDisable(false);
@@ -23,7 +23,7 @@ import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
@@ -93,7 +93,7 @@ public final class Controllers {
public static void initialize(Stage stage) {
Controllers.stage = stage;
decorator = new Decorator(stage, getMainPage(), Main.TITLE, false, true);
decorator = new Decorator(stage, getMainPage(), Launcher.TITLE, false, true);
decorator.showPage(null);
leftPaneController = new LeftPaneController(decorator.getLeftPane());
@@ -110,7 +110,7 @@ public final class Controllers {
stage.setMaxHeight(521);
stage.getIcons().add(new Image("/assets/img/icon.png"));
stage.setTitle(Main.TITLE);
stage.setTitle(Launcher.TITLE);
}
public static Region getDialogContent() {
@@ -27,7 +27,7 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
/**
* @author huangyuhui
@@ -36,10 +36,10 @@ public class CrashWindow extends Stage {
public CrashWindow(String text) {
Label lblCrash = new Label();
if (Main.UPDATE_CHECKER.isOutOfDate())
lblCrash.setText(Main.i18n("launcher.crash_out_dated"));
if (Launcher.UPDATE_CHECKER.isOutOfDate())
lblCrash.setText(Launcher.i18n("launcher.crash_out_dated"));
else
lblCrash.setText(Main.i18n("launcher.crash"));
lblCrash.setText(Launcher.i18n("launcher.crash"));
lblCrash.setWrapText(true);
TextArea textArea = new TextArea();
@@ -47,8 +47,8 @@ public class CrashWindow extends Stage {
textArea.setEditable(false);
Button btnContact = new Button();
btnContact.setText(Main.i18n("launcher.contact"));
btnContact.setOnMouseClicked(event -> FXUtils.openLink(Main.CONTACT));
btnContact.setText(Launcher.i18n("launcher.contact"));
btnContact.setOnMouseClicked(event -> FXUtils.openLink(Launcher.CONTACT));
HBox box = new HBox();
box.setStyle("-fx-padding: 8px;");
box.getChildren().add(btnContact);
@@ -65,7 +65,7 @@ public class CrashWindow extends Stage {
Scene scene = new Scene(pane, 800, 480);
setScene(scene);
getIcons().add(new Image("/assets/img/icon.png"));
setTitle(Main.i18n("message.error"));
setTitle(Launcher.i18n("message.error"));
setOnCloseRequest(e -> System.exit(1));
}
@@ -45,7 +45,7 @@ import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.EnumBackgroundImage;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.setting.Theme;
@@ -74,7 +74,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
private static final SVGGlyph close = Lang.apply(new SVGGlyph(0, "CLOSE", "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z", Color.WHITE),
glyph -> { glyph.setPrefSize(12, 12); glyph.setSize(12, 12); });
private final ObjectProperty<Runnable> onCloseButtonAction = new SimpleObjectProperty<>(Main::stopApplication);
private final ObjectProperty<Runnable> onCloseButtonAction = new SimpleObjectProperty<>(Launcher::stopApplication);
private final BooleanProperty customMaximize = new SimpleBooleanProperty(false);
private final Stage primaryStage;
@@ -144,7 +144,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
FXUtils.loadFXML(this, "/assets/fxml/decorator.fxml");
updatePane.setCursor(Cursor.HAND);
updatePane.setOnMouseClicked(event -> Main.UPDATE_CHECKER.checkOutdate());
updatePane.setOnMouseClicked(event -> Launcher.UPDATE_CHECKER.checkOutdate());
primaryStage.initStyle(StageStyle.UNDECORATED);
btnClose.setGraphic(close);
@@ -487,6 +487,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
}
private void setContent(Node content, AnimationProducer animation) {
isWizardPageNow = false;
animationHandler.setContent(content, animation);
if (content instanceof Region) {
@@ -494,11 +495,8 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
FXUtils.setOverflowHidden((Region) content, true);
}
if (content instanceof Refreshable)
refreshNavButton.setVisible(true);
if (content != mainPage)
backNavButton.setVisible(true);
refreshNavButton.setVisible(content instanceof Refreshable);
backNavButton.setVisible(content != mainPage);
String prefix = category == null ? "" : category + " - ";
@@ -513,6 +511,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
private String category;
private Node nowPage;
private boolean isWizardPageNow;
public Node getNowPage() {
return nowPage;
@@ -576,6 +575,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
contentPlaceHolder.getStyleClass().removeAll("gray-background", "white-background");
contentPlaceHolder.getStyleClass().add("white-background");
setContent(page, nav.getAnimation().getAnimationProducer());
isWizardPageNow = true;
}
@FXML
@@ -591,7 +591,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
@FXML
private void onBack() {
if (wizardController.canPrev())
if (isWizardPageNow && wizardController.canPrev())
wizardController.onPrev(true);
else
onCloseNav();
@@ -41,7 +41,7 @@ import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.util.*;
import java.io.File;
@@ -184,7 +184,7 @@ public final class FXUtils {
}
public static void loadFXML(Node node, String absolutePath) {
FXMLLoader loader = new FXMLLoader(node.getClass().getResource(absolutePath), Main.RESOURCE_BUNDLE);
FXMLLoader loader = new FXMLLoader(node.getClass().getResource(absolutePath), Launcher.RESOURCE_BUNDLE);
loader.setRoot(node);
loader.setController(node);
Lang.invoke((ExceptionalSupplier<Object, IOException>) loader::load);
@@ -21,7 +21,7 @@ import com.jfoenix.effects.JFXDepthManager;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import java.util.function.Consumer;
@@ -44,7 +44,7 @@ public class InstallerItem extends BorderPane {
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1);
lblInstallerArtifact.setText(artifact);
lblInstallerVersion.setText(Main.i18n("archive.version") + ": " + version);
lblInstallerVersion.setText(Launcher.i18n("archive.version") + ": " + version);
}
@FXML
@@ -25,7 +25,7 @@ import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent;
@@ -60,18 +60,18 @@ public final class LeftPaneController {
public LeftPaneController(AdvancedListBox leftPane) {
this.leftPane = leftPane;
leftPane.startCategory(Main.i18n("account").toUpperCase())
leftPane.startCategory(Launcher.i18n("account").toUpperCase())
.add(Lang.apply(new RipplerContainer(accountItem), rippler -> {
rippler.setOnMouseClicked(e -> Controllers.navigate(new AccountsPage()));
accountItem.setOnSettingsButtonClicked(() -> Controllers.navigate(new AccountsPage()));
}))
.startCategory(Main.i18n("launcher").toUpperCase())
.add(Lang.apply(new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20), Main.i18n("settings.launcher")), iconedItem -> {
.startCategory(Launcher.i18n("launcher").toUpperCase())
.add(Lang.apply(new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20), Launcher.i18n("settings.launcher")), iconedItem -> {
iconedItem.prefWidthProperty().bind(leftPane.widthProperty());
iconedItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage()));
}))
.add(new ClassTitle(Lang.apply(new BorderPane(), borderPane -> {
borderPane.setLeft(Lang.apply(new VBox(), vBox -> vBox.getChildren().setAll(new Text(Main.i18n("profile.title").toUpperCase()))));
borderPane.setLeft(Lang.apply(new VBox(), vBox -> vBox.getChildren().setAll(new Text(Launcher.i18n("profile.title").toUpperCase()))));
JFXButton addProfileButton = new JFXButton();
addProfileButton.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10));
addProfileButton.getStyleClass().add("toggle-icon-tiny");
@@ -87,8 +87,8 @@ public final class LeftPaneController {
FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), it -> {
if (it == null) {
accountItem.setVersionName(Main.i18n("account.missing"));
accountItem.setGameVersion(Main.i18n("message.unknown"));
accountItem.setVersionName(Launcher.i18n("account.missing"));
accountItem.setGameVersion(Launcher.i18n("message.unknown"));
} else {
accountItem.setVersionName(it.getCharacter());
accountItem.setGameVersion(AccountsPage.accountType(it));
@@ -110,7 +110,7 @@ public final class LeftPaneController {
if (node instanceof RipplerContainer && node.getProperties().get("profile") instanceof String) {
boolean current = Objects.equals(node.getProperties().get("profile"), profile.getName());
((RipplerContainer) node).setSelected(current);
((VersionListItem) ((RipplerContainer) node).getContainer()).setGameVersion(current ? Main.i18n("profile.selected") : "");
((VersionListItem) ((RipplerContainer) node).getContainer()).setGameVersion(current ? Launcher.i18n("profile.selected") : "");
}
}
});
@@ -149,7 +149,7 @@ public final class LeftPaneController {
Controllers.closeDialog();
checkAccount();
})).executor(),
Main.i18n("modpack.installing"), "", null);
Launcher.i18n("modpack.installing"), "", null);
flag = false;
} catch (UnsupportedModpackException ignore) {
}
@@ -30,7 +30,7 @@ import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.event.Event;
import org.jackhuang.hmcl.event.EventManager;
import org.jackhuang.hmcl.game.LauncherHelper;
@@ -63,7 +63,7 @@ public final class LogWindow extends Stage {
public LogWindow() {
setScene(new Scene(impl, 800, 480));
getScene().getStylesheets().addAll(Settings.INSTANCE.getTheme().getStylesheets());
setTitle(Main.i18n("logwindow.title"));
setTitle(Launcher.i18n("logwindow.title"));
getIcons().add(new Image("/assets/img/icon.png"));
}
@@ -17,11 +17,8 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXMasonryPane;
import com.jfoenix.controls.JFXPopup;
import javafx.application.Platform;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.*;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
@@ -30,11 +27,11 @@ import javafx.scene.image.Image;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent;
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
@@ -52,25 +49,28 @@ import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public final class MainPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("main_page"));
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("main_page"));
private Profile profile;
private String rightClickedVersion;
private HMCLGameRepository rightClickedRepository;
@FXML
private JFXButton btnRefresh;
@FXML
private StackPane contentPane;
@FXML
private JFXButton btnAdd;
@FXML
private JFXSpinner spinner;
@FXML
private JFXMasonryPane masonryPane;
@FXML
private JFXListView versionList;
@@ -79,124 +79,134 @@ public final class MainPage extends StackPane implements DecoratorPage {
{
FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(() -> Platform.runLater(this::loadVersions));
EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading);
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged);
loadingVersions();
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> {
if (event.getSource() == profile.getRepository())
loadVersions((HMCLGameRepository) event.getSource());
});
EventBus.EVENT_BUS.channel(RefreshingVersionsEvent.class).register(event -> {
if (event.getSource() == profile.getRepository())
JFXUtilities.runInFXAndWait(this::loadingVersions);
});
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(event -> {
this.profile = event.getProfile();
});
versionPopup = new JFXPopup(versionList);
getChildren().remove(versionList);
btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Main.i18n("install")));
FXUtils.installTooltip(btnAdd, Main.i18n("install"));
btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Launcher.i18n("install")));
FXUtils.installTooltip(btnAdd, Launcher.i18n("install"));
btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersionsAsync().start());
FXUtils.installTooltip(btnRefresh, Main.i18n("button.refresh"));
FXUtils.installTooltip(btnRefresh, Launcher.i18n("button.refresh"));
}
private Node buildNode(Profile profile, String version, String game) {
private Node buildNode(HMCLGameRepository repository, Version version, String game) {
Profile profile = repository.getProfile();
String id = version.getId();
VersionItem item = new VersionItem();
item.setUpdate(profile.getRepository().isModpack(version));
item.setUpdate(repository.isModpack(id));
item.setGameVersion(game);
item.setVersionName(version);
item.setVersionName(id);
StringBuilder libraries = new StringBuilder();
for (Library library : profile.getRepository().getVersion(version).getLibraries()) {
for (Library library : version.getLibraries()) {
if (library.getGroupId().equalsIgnoreCase("net.minecraftforge") && library.getArtifactId().equalsIgnoreCase("forge")) {
libraries.append(Main.i18n("install.installer.forge")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)forge", "").replace(game, "").trim(), "-"), "-")).append("\n");
libraries.append(Launcher.i18n("install.installer.forge")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)forge", "").replace(game, "").trim(), "-"), "-")).append("\n");
}
if (library.getGroupId().equalsIgnoreCase("com.mumfrey") && library.getArtifactId().equalsIgnoreCase("liteloader")) {
libraries.append(Main.i18n("install.installer.liteloader")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)liteloader", "").replace(game, "").trim(), "-"), "-")).append("\n");
libraries.append(Launcher.i18n("install.installer.liteloader")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)liteloader", "").replace(game, "").trim(), "-"), "-")).append("\n");
}
if (library.getGroupId().equalsIgnoreCase("net.optifine") && library.getArtifactId().equalsIgnoreCase("optifine")) {
libraries.append(Main.i18n("install.installer.optifine")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)optifine", "").replace(game, "").trim(), "-"), "-")).append("\n");
libraries.append(Launcher.i18n("install.installer.optifine")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)optifine", "").replace(game, "").trim(), "-"), "-")).append("\n");
}
}
item.setLibraries(libraries.toString());
item.setOnLaunchButtonClicked(e -> {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
Controllers.dialog(Launcher.i18n("login.empty_username"));
else
LauncherHelper.INSTANCE.launch(version, null);
LauncherHelper.INSTANCE.launch(profile, Settings.INSTANCE.getSelectedAccount(), id, null);
});
item.setOnScriptButtonClicked(e -> {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
Controllers.dialog(Launcher.i18n("login.empty_username"));
else {
FileChooser chooser = new FileChooser();
chooser.setInitialDirectory(profile.getRepository().getRunDirectory(version));
chooser.setTitle(Main.i18n("version.launch_script.save"));
chooser.setInitialDirectory(repository.getRunDirectory(id));
chooser.setTitle(Launcher.i18n("version.launch_script.save"));
chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
? new FileChooser.ExtensionFilter(Main.i18n("extension.bat"), "*.bat")
: new FileChooser.ExtensionFilter(Main.i18n("extension.sh"), "*.sh"));
? new FileChooser.ExtensionFilter(Launcher.i18n("extension.bat"), "*.bat")
: new FileChooser.ExtensionFilter(Launcher.i18n("extension.sh"), "*.sh"));
File file = chooser.showSaveDialog(Controllers.getStage());
if (file != null)
LauncherHelper.INSTANCE.launch(version, file);
LauncherHelper.INSTANCE.launch(profile, Settings.INSTANCE.getSelectedAccount(), id, file);
}
});
item.setOnSettingsButtonClicked(e -> {
Controllers.getDecorator().showPage(Controllers.getVersionPage());
Controllers.getVersionPage().load(version, profile);
Controllers.getVersionPage().load(id, profile);
});
item.setOnUpdateButtonClicked(event -> {
FileChooser chooser = new FileChooser();
chooser.setTitle(Main.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip"));
chooser.setTitle(Launcher.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("modpack"), "*.zip"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile != null) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(null);
try {
TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, version, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(version)))
TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, id, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(id)))
.then(Task.of(Schedulers.javafx(), Controllers::closeDialog)).executor();
pane.setExecutor(executor);
pane.setTitle(Main.i18n("modpack.update"));
pane.setTitle(Launcher.i18n("modpack.update"));
executor.start();
Controllers.dialog(pane);
} catch (UnsupportedModpackException e) {
Controllers.dialog(Main.i18n("modpack.unsupported"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.dialog(Launcher.i18n("modpack.unsupported"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE);
} catch (MismatchedModpackTypeException e) {
Controllers.dialog(Main.i18n("modpack.mismatched_type"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.dialog(Launcher.i18n("modpack.mismatched_type"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE);
} catch (IOException e) {
Controllers.dialog(Main.i18n("modpack.invalid"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.dialog(Launcher.i18n("modpack.invalid"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE);
}
}
});
item.setOnMouseClicked(event -> {
if (event.getButton() == MouseButton.SECONDARY) {
rightClickedVersion = version;
rightClickedVersion = id;
rightClickedRepository = repository;
versionList.getSelectionModel().select(-1);
versionPopup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY());
} else if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
Controllers.dialog(Launcher.i18n("login.empty_username"));
else
LauncherHelper.INSTANCE.launch(version, null);
LauncherHelper.INSTANCE.launch(profile, Settings.INSTANCE.getSelectedAccount(), id, null);
}
});
File iconFile = profile.getRepository().getVersionIcon(version);
File iconFile = repository.getVersionIcon(id);
if (iconFile.exists())
item.setImage(new Image("file:" + iconFile.getAbsolutePath()));
return item;
}
public void onProfilesLoading() {
// TODO: Profiles
private void loadingVersions() {
contentPane.getChildren().setAll(spinner);
FXUtils.resetChildren(masonryPane, Collections.emptyList());
}
public void onProfileChanged(ProfileChangedEvent event) {
Platform.runLater(() -> loadVersions(event.getProfile()));
}
private void loadVersions() {
loadVersions(Settings.INSTANCE.getSelectedProfile());
}
private void loadVersions(Profile profile) {
this.profile = profile;
private void loadVersions(HMCLGameRepository repository) {
List<Node> children = new LinkedList<>();
for (Version version : profile.getRepository().getVersions()) {
children.add(buildNode(profile, version.getId(), GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version.getId())).orElse("Unknown")));
for (Version version : repository.getVersions()) {
children.add(buildNode(repository, version, GameVersion.minecraftVersion(repository.getVersionJar(version.getId())).orElse("Unknown")));
}
FXUtils.resetChildren(masonryPane, children);
JFXUtilities.runInFX(() -> {
if (profile == repository.getProfile()) {
contentPane.getChildren().setAll(masonryPane);
FXUtils.resetChildren(masonryPane, children);
}
});
}
@FXML
@@ -204,16 +214,16 @@ public final class MainPage extends StackPane implements DecoratorPage {
versionPopup.hide();
switch (versionList.getSelectionModel().getSelectedIndex()) {
case 0:
VersionPage.renameVersion(profile, rightClickedVersion);
VersionPage.renameVersion(rightClickedRepository.getProfile(), rightClickedVersion);
break;
case 1:
VersionPage.deleteVersion(profile, rightClickedVersion);
VersionPage.deleteVersion(rightClickedRepository.getProfile(), rightClickedVersion);
break;
case 2:
VersionPage.exportVersion(profile, rightClickedVersion);
VersionPage.exportVersion(rightClickedRepository.getProfile(), rightClickedVersion);
break;
case 3:
FXUtils.openFolder(profile.getRepository().getRunDirectory(rightClickedVersion));
FXUtils.openFolder(rightClickedRepository.getRunDirectory(rightClickedVersion));
break;
default:
throw new Error();
@@ -17,16 +17,16 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.JFXSpinner;
import com.jfoenix.controls.JFXTabPane;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.mod.ModInfo;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.task.Schedulers;
@@ -93,7 +93,7 @@ public final class ModController {
this.versionId = versionId;
Task.of(variables -> {
synchronized (ModController.this) {
Platform.runLater(() -> {
JFXUtilities.runInFX(() -> {
rootPane.getChildren().remove(contentPane);
spinner.setVisible(true);
});
@@ -121,25 +121,24 @@ public final class ModController {
list.add(item);
}
Platform.runLater(() -> {
rootPane.getChildren().add(contentPane);
spinner.setVisible(false);
});
variables.set("list", list);
}
}).finalized(Schedulers.javafx(), variables -> {
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
if (newValue != null && newValue.getUserData() == ModController.this)
modPane.getChildren().setAll(variables.<List<ModItem>>get("list"));
});
}, null).start();
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
rootPane.getChildren().add(contentPane);
spinner.setVisible(false);
if (isDependentsSucceeded)
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
if (newValue != null && newValue.getUserData() == ModController.this)
modPane.getChildren().setAll(variables.<List<ModItem>>get("list"));
});
}).start();
}
@FXML
private void onAdd() {
FileChooser chooser = new FileChooser();
chooser.setTitle(Main.i18n("mods.choose_mod"));
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(Main.i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
chooser.setTitle(Launcher.i18n("mods.choose_mod"));
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(Launcher.i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
List<File> res = chooser.showOpenMultipleDialog(Controllers.getStage());
// It's guaranteed that succeeded and failed are thread safe here.
@@ -161,10 +160,10 @@ public final class ModController {
}).with(Task.of(Schedulers.javafx(), variables -> {
List<String> prompt = new LinkedList<>();
if (!succeeded.isEmpty())
prompt.add(Main.i18n("mods.add.success", String.join(", ", succeeded)));
prompt.add(Launcher.i18n("mods.add.success", String.join(", ", succeeded)));
if (!failed.isEmpty())
prompt.add(Main.i18n("mods.add.failed", String.join(", ", failed)));
Controllers.dialog(String.join("\n", prompt), Main.i18n("mods.add"));
prompt.add(Launcher.i18n("mods.add.failed", String.join(", ", failed)));
Controllers.dialog(String.join("\n", prompt), Launcher.i18n("mods.add"));
loadMods(modManager, versionId);
})).start();
}
@@ -22,7 +22,7 @@ import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.effects.JFXDepthManager;
import javafx.geometry.Pos;
import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.mod.ModInfo;
import org.jackhuang.hmcl.setting.Theme;
@@ -40,7 +40,7 @@ public final class ModItem extends BorderPane {
setCenter(modItem);
JFXButton btnRemove = new JFXButton();
FXUtils.installTooltip(btnRemove, Main.i18n("mods.remove"));
FXUtils.installTooltip(btnRemove, Launcher.i18n("mods.remove"));
btnRemove.setOnMouseClicked(e -> deleteCallback.accept(this));
btnRemove.getStyleClass().add("toggle-icon4");
BorderPane.setAlignment(btnRemove, Pos.CENTER);
@@ -50,7 +50,7 @@ public final class ModItem extends BorderPane {
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1);
modItem.setTitle(info.getFileName());
modItem.setSubtitle(info.getName() + ", " + Main.i18n("archive.version") + ": " + info.getVersion() + ", " + Main.i18n("archive.game_version") + ": " + info.getGameVersion() + ", " + Main.i18n("archive.author") + ": " + info.getAuthors());
modItem.setSubtitle(info.getName() + ", " + Launcher.i18n("archive.version") + ": " + info.getVersion() + ", " + Launcher.i18n("archive.game_version") + ": " + info.getGameVersion() + ", " + Launcher.i18n("archive.author") + ": " + info.getAuthors());
chkEnabled.setSelected(info.isActive());
chkEnabled.selectedProperty().addListener((a, b, newValue) ->
info.activeProperty().set(newValue));
@@ -23,7 +23,7 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.setting.Settings;
@@ -51,15 +51,16 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
*/
public ProfilePage(Profile profile) {
this.profile = profile;
String profileDisplayName = Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse("");
title = new SimpleStringProperty(this, "title",
profile == null ? Main.i18n("profile.new") : Main.i18n("profile") + " - " + profile.getName());
profile == null ? Launcher.i18n("profile.new") : Launcher.i18n("profile") + " - " + profileDisplayName);
location = new SimpleStringProperty(this, "location",
Optional.ofNullable(profile).map(Profile::getGameDir).map(File::getAbsolutePath).orElse(""));
FXUtils.loadFXML(this, "/assets/fxml/profile.fxml");
txtProfileName.setText(Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse(""));
txtProfileName.setText(profileDisplayName);
FXUtils.onChangeAndOperate(txtProfileName.textProperty(), it -> {
btnSave.setDisable(!txtProfileName.validate() || StringUtils.isBlank(getLocation()));
});
@@ -34,9 +34,12 @@ import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.*;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.construct.FileItem;
import org.jackhuang.hmcl.ui.construct.FontComboBox;
import org.jackhuang.hmcl.ui.construct.MultiFileItem;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
@@ -46,7 +49,7 @@ import java.util.Collections;
import java.util.stream.Collectors;
public final class SettingsPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("settings.launcher"));
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("settings.launcher"));
@FXML
private JFXTextField txtProxyHost;
@@ -174,12 +177,12 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
fileCommonLocation.setProperty(Settings.INSTANCE.commonPathProperty());
FXUtils.installTooltip(btnUpdate, Main.i18n("update.tooltip"));
FXUtils.installTooltip(btnUpdate, Launcher.i18n("update.tooltip"));
checkUpdate();
// background
backgroundItem.loadChildren(Collections.singletonList(
backgroundItem.createChildren(Main.i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT)
backgroundItem.createChildren(Launcher.i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT)
));
FXUtils.bindString(backgroundItem.getTxtCustom(), Settings.INSTANCE.backgroundImageProperty());
@@ -196,8 +199,8 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
// theme
JFXColorPicker picker = new JFXColorPicker(Color.web(Settings.INSTANCE.getTheme().getColor()), null);
picker.setCustomColorText(Main.i18n("color.custom"));
picker.setRecentColorsText(Main.i18n("color.recent"));
picker.setCustomColorText(Launcher.i18n("color.custom"));
picker.setRecentColorsText(Launcher.i18n("color.recent"));
picker.getCustomColors().setAll(Arrays.stream(Theme.VALUES).map(Theme::getColor).map(Color::web).collect(Collectors.toList()));
picker.setOnAction(e -> {
Theme theme = Theme.custom(Theme.getColorDisplayName(picker.getValue()));
@@ -211,7 +214,7 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
private void initBackgroundItemSubtitle() {
switch (Settings.INSTANCE.getBackgroundImageType()) {
case DEFAULT:
backgroundItem.setSubtitle(Main.i18n("launcher.background.default"));
backgroundItem.setSubtitle(Launcher.i18n("launcher.background.default"));
break;
case CUSTOM:
backgroundItem.setSubtitle(Settings.INSTANCE.getBackgroundImage());
@@ -233,25 +236,25 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
}
public void checkUpdate() {
btnUpdate.setVisible(Main.UPDATE_CHECKER.isOutOfDate());
btnUpdate.setVisible(Launcher.UPDATE_CHECKER.isOutOfDate());
if (Main.UPDATE_CHECKER.isOutOfDate()) {
lblUpdateSub.setText(Main.i18n("update.newest_version", Main.UPDATE_CHECKER.getNewVersion().toString()));
if (Launcher.UPDATE_CHECKER.isOutOfDate()) {
lblUpdateSub.setText(Launcher.i18n("update.newest_version", Launcher.UPDATE_CHECKER.getNewVersion().toString()));
lblUpdateSub.getStyleClass().setAll("update-label");
lblUpdate.setText(Main.i18n("update.found"));
lblUpdate.setText(Launcher.i18n("update.found"));
lblUpdate.getStyleClass().setAll("update-label");
} else {
lblUpdateSub.setText(Main.i18n("update.latest"));
lblUpdateSub.setText(Launcher.i18n("update.latest"));
lblUpdateSub.getStyleClass().setAll("subtitle-label");
lblUpdate.setText(Main.i18n("update"));
lblUpdate.setText(Launcher.i18n("update"));
lblUpdate.getStyleClass().setAll();
}
}
@FXML
private void onUpdate() {
Main.UPDATE_CHECKER.checkOutdate();
Launcher.UPDATE_CHECKER.checkOutdate();
}
}
@@ -32,7 +32,7 @@ import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import java.util.Optional;
@@ -73,10 +73,10 @@ public final class VersionItem extends StackPane {
btnLaunch.setGraphic(SVG.launch(Theme.blackFillBinding(), 15, 15));
btnScript.setGraphic(SVG.script(Theme.blackFillBinding(), 15, 15));
FXUtils.installTooltip(btnSettings, Main.i18n("version.settings"));
FXUtils.installTooltip(btnUpdate, Main.i18n("version.update"));
FXUtils.installTooltip(btnLaunch, Main.i18n("version.launch"));
FXUtils.installTooltip(btnScript, Main.i18n("version.launch_script"));
FXUtils.installTooltip(btnSettings, Launcher.i18n("version.settings"));
FXUtils.installTooltip(btnUpdate, Launcher.i18n("version.update"));
FXUtils.installTooltip(btnLaunch, Launcher.i18n("version.launch"));
FXUtils.installTooltip(btnScript, Launcher.i18n("version.launch_script"));
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty()));
FXUtils.limitSize(iconView, 32, 32);
@@ -26,7 +26,7 @@ import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Tab;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.export.ExportWizardProvider;
@@ -79,17 +79,17 @@ public final class VersionPage extends StackPane implements DecoratorPage {
browsePopup = new JFXPopup(browseList);
managementPopup = new JFXPopup(managementList);
FXUtils.installTooltip(btnDelete, Main.i18n("version.manage.remove"));
FXUtils.installTooltip(btnBrowseMenu, Main.i18n("settings.game.exploration"));
FXUtils.installTooltip(btnManagementMenu, Main.i18n("settings.game.management"));
FXUtils.installTooltip(btnExport, Main.i18n("modpack.export"));
FXUtils.installTooltip(btnDelete, Launcher.i18n("version.manage.remove"));
FXUtils.installTooltip(btnBrowseMenu, Launcher.i18n("settings.game.exploration"));
FXUtils.installTooltip(btnManagementMenu, Launcher.i18n("settings.game.management"));
FXUtils.installTooltip(btnExport, Launcher.i18n("modpack.export"));
}
public void load(String id, Profile profile) {
this.version = id;
this.profile = profile;
title.set(Main.i18n("settings.game") + " - " + id);
title.set(Launcher.i18n("settings.game") + " - " + id);
versionSettingsController.loadVersionSetting(profile, id);
modController.setParentTab(tabPane);
@@ -185,7 +185,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
}
public static void deleteVersion(Profile profile, String version) {
Controllers.confirmDialog(Main.i18n("version.manage.remove.confirm", version), Main.i18n("message.confirm"), () -> {
Controllers.confirmDialog(Launcher.i18n("version.manage.remove.confirm", version), Launcher.i18n("message.confirm"), () -> {
if (profile.getRepository().removeVersionFromDisk(version)) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
@@ -194,7 +194,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
}
public static void renameVersion(Profile profile, String version) {
Controllers.inputDialog(Main.i18n("version.manage.rename.message"), res -> {
Controllers.inputDialog(Launcher.i18n("version.manage.rename.message"), res -> {
if (profile.getRepository().renameVersion(version, res)) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
@@ -203,6 +203,6 @@ public final class VersionPage extends StackPane implements DecoratorPage {
}
public static void exportVersion(Profile profile, String version) {
Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), Main.i18n("modpack.wizard"));
Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), Launcher.i18n("modpack.wizard"));
}
}
@@ -17,7 +17,10 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*;
import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.controls.JFXToggleButton;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.Node;
@@ -27,7 +30,7 @@ import javafx.scene.control.Toggle;
import javafx.scene.image.Image;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting;
@@ -77,7 +80,7 @@ public final class VersionSettingsController {
@FXML
private void initialize() {
lblPhysicalMemory.setText(Main.i18n("settings.physical_memory") + ": " + OperatingSystem.TOTAL_MEMORY + "MB");
lblPhysicalMemory.setText(Launcher.i18n("settings.physical_memory") + ": " + OperatingSystem.TOTAL_MEMORY + "MB");
FXUtils.smoothScrolling(scroll);
@@ -91,13 +94,13 @@ public final class VersionSettingsController {
javaItem.getExtensionFilters().add(new FileChooser.ExtensionFilter("Java", "java.exe", "javaw.exe"));
gameDirItem.loadChildren(Arrays.asList(
gameDirItem.createChildren(Main.i18n("settings.advanced.game_dir.default"), EnumGameDirectory.ROOT_FOLDER),
gameDirItem.createChildren(Main.i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER)
gameDirItem.createChildren(Launcher.i18n("settings.advanced.game_dir.default"), EnumGameDirectory.ROOT_FOLDER),
gameDirItem.createChildren(Launcher.i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER)
));
globalItem.loadChildren(Arrays.asList(
globalItem.createChildren(Main.i18n("settings.type.global"), true),
globalItem.createChildren(Main.i18n("settings.type.special"), false)
globalItem.createChildren(Launcher.i18n("settings.type.global"), true),
globalItem.createChildren(Launcher.i18n("settings.type.special"), false)
));
}
@@ -189,7 +192,7 @@ public final class VersionSettingsController {
});
versionSetting.usesGlobalProperty().setChangedListenerAndOperate(it ->
globalItem.setSubtitle(Main.i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special")));
globalItem.setSubtitle(Launcher.i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special")));
gameDirItem.getGroup().getToggles().stream()
.filter(it -> it.getUserData() == versionSetting.getGameDirType())
@@ -229,7 +232,7 @@ public final class VersionSettingsController {
@FXML
private void onExploreIcon() {
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("extension.png"), "*.png"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("extension.png"), "*.png"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile != null) {
File iconFile = profile.getRepository().getVersionIcon(versionId);
@@ -26,7 +26,7 @@ import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
@@ -54,7 +54,7 @@ public class FileItem extends BorderPane {
right.setGraphic(SVG.pencil(Theme.blackFillBinding(), 15, 15));
right.getStyleClass().add("toggle-icon4");
right.setOnMouseClicked(e -> onExplore());
FXUtils.installTooltip(right, Main.i18n("button.edit"));
FXUtils.installTooltip(right, Launcher.i18n("button.edit"));
setRight(right);
Tooltip tip = new Tooltip();
@@ -15,7 +15,7 @@ import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
@@ -41,7 +41,7 @@ public final class ImagePickerItem extends BorderPane {
selectButton.onMouseClickedProperty().bind(onSelectButtonClicked);
selectButton.getStyleClass().add("toggle-icon4");
FXUtils.installTooltip(selectButton, Main.i18n("button.edit"));
FXUtils.installTooltip(selectButton, Launcher.i18n("button.edit"));
HBox hBox = new HBox();
hBox.getChildren().setAll(imageView, selectButton);
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.ui.construct;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextInputDialog;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import javax.swing.*;
import java.util.Optional;
@@ -29,7 +29,7 @@ public final class MessageBox {
private MessageBox() {
}
private static final String TITLE = Main.i18n("message.info");
private static final String TITLE = Launcher.i18n("message.info");
/**
* User Operation: Yes
@@ -23,7 +23,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
@@ -92,8 +92,8 @@ public final class MessageDialogPane extends StackPane {
Optional.ofNullable(onCancel).ifPresent(Runnable::run);
});
acceptButton.setText(Main.i18n("button.yes"));
cancelButton.setText(Main.i18n("button.no"));
acceptButton.setText(Launcher.i18n("button.yes"));
cancelButton.setText(Launcher.i18n("button.no"));
actions.getChildren().add(cancelButton);
}
@@ -5,17 +5,15 @@ import com.jfoenix.controls.JFXRadioButton;
import javafx.beans.NamedArg;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.ui.FXUtils;
import java.util.Collection;
@@ -23,8 +21,8 @@ import java.util.Optional;
import java.util.function.Consumer;
public class MultiColorItem extends ComponentList {
private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Main.i18n("selector.custom"));
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Main.i18n("selector.choose_file"));
private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Launcher.i18n("selector.custom"));
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Launcher.i18n("selector.choose_file"));
private final ToggleGroup group = new ToggleGroup();
private final JFXColorPicker colorPicker = new JFXColorPicker();
@@ -38,7 +38,7 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
@@ -49,8 +49,8 @@ import java.util.Collection;
import java.util.function.Consumer;
public class MultiFileItem extends ComponentList {
private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Main.i18n("selector.custom"));
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Main.i18n("selector.choose_file"));
private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Launcher.i18n("selector.custom"));
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Launcher.i18n("selector.choose_file"));
private final BooleanProperty directory = new SimpleBooleanProperty(this, "directory", false);
private final SimpleStringProperty tooltip = new SimpleStringProperty(this, "tooltip");
private final ObservableList<FileChooser.ExtensionFilter> extensionFilters = FXCollections.observableArrayList();
@@ -153,7 +153,7 @@ public class MultiFileItem extends ComponentList {
public void onExploreJavaDir() {
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle(Main.i18n(getChooserTitle()));
chooser.setTitle(Launcher.i18n(getChooserTitle()));
File selectedDir = chooser.showDialog(Controllers.getStage());
if (selectedDir != null)
txtCustom.setText(selectedDir.getAbsolutePath());
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.beans.property.StringProperty;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener;
@@ -38,7 +38,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
Controllers.navigate(null);
});
pane.setTitle(Main.i18n("message.doing"));
pane.setTitle(Launcher.i18n("message.doing"));
pane.setProgress(Double.MAX_VALUE);
if (settings.containsKey("title")) {
Object title = settings.get("title");
@@ -64,7 +64,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
JFXUtilities.runInFX(() -> Controllers.dialog((String) settings.get("success_message"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
else if (!settings.containsKey("forbid_success_message"))
JFXUtilities.runInFX(() -> Controllers.dialog(Main.i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
JFXUtilities.runInFX(() -> Controllers.dialog(Launcher.i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
} else {
if (executor.getLastException() == null)
return;
@@ -72,7 +72,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
if (settings.containsKey("failure_message") && settings.get("failure_message") instanceof String)
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
else if (!settings.containsKey("forbid_failure_message"))
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Main.i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Launcher.i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
}
}
});
@@ -23,7 +23,7 @@ import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameAssetRefreshTask;
@@ -31,10 +31,7 @@ import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
import org.jackhuang.hmcl.mod.CurseCompletionTask;
import org.jackhuang.hmcl.mod.CurseInstallTask;
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
import org.jackhuang.hmcl.mod.MultiMCModpackInstallTask;
import org.jackhuang.hmcl.mod.*;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener;
@@ -60,32 +57,34 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onReady(Task task) {
public void onRunning(Task task) {
if (!task.getSignificance().shouldShow())
return;
if (task instanceof GameAssetRefreshTask) {
task.setName(Main.i18n("assets.download"));
task.setName(Launcher.i18n("assets.download"));
} else if (task instanceof GameAssetDownloadTask) {
task.setName(Main.i18n("assets.download_all"));
task.setName(Launcher.i18n("assets.download_all"));
} else if (task instanceof ForgeInstallTask) {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.forge")));
task.setName(Launcher.i18n("install.installer.install", Launcher.i18n("install.installer.forge")));
} else if (task instanceof LiteLoaderInstallTask) {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.liteloader")));
task.setName(Launcher.i18n("install.installer.install", Launcher.i18n("install.installer.liteloader")));
} else if (task instanceof OptiFineInstallTask) {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine")));
task.setName(Launcher.i18n("install.installer.install", Launcher.i18n("install.installer.optifine")));
} else if (task instanceof CurseCompletionTask) {
task.setName(Main.i18n("modpack.type.curse.completion"));
task.setName(Launcher.i18n("modpack.type.curse.completion"));
} else if (task instanceof ModpackInstallTask) {
task.setName(Launcher.i18n("modpack.installing"));
} else if (task instanceof CurseInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
task.setName(Launcher.i18n("modpack.install", Launcher.i18n("modpack.type.curse")));
} else if (task instanceof MultiMCModpackInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.multimc")));
task.setName(Launcher.i18n("modpack.install", Launcher.i18n("modpack.type.multimc")));
} else if (task instanceof HMCLModpackInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.hmcl")));
task.setName(Launcher.i18n("modpack.install", Launcher.i18n("modpack.type.hmcl")));
} else if (task instanceof HMCLModpackExportTask) {
task.setName(Main.i18n("modpack.export"));
task.setName(Launcher.i18n("modpack.export"));
} else if (task instanceof MinecraftInstanceTask) {
task.setName(Main.i18n("modpack.scan"));
task.setName(Launcher.i18n("modpack.scan"));
}
ProgressListNode node = new ProgressListNode(task);
@@ -22,8 +22,9 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.wizard.WizardController;
@@ -31,8 +32,9 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.Lang;
import java.util.Map;
import java.util.Optional;
import static org.jackhuang.hmcl.Main.i18n;
import static org.jackhuang.hmcl.Launcher.i18n;
class AdditionalInstallersPage extends StackPane implements WizardPage {
private final InstallerWizardProvider provider;
@@ -98,29 +100,33 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
@Override
public String getTitle() {
return Main.i18n("settings.tabs.installers");
return Launcher.i18n("settings.tabs.installers");
}
private String getVersion(String id) {
return Optional.ofNullable(controller.getSettings().get(id)).map(it -> (RemoteVersion<?>) it).map(RemoteVersion::getSelfVersion).orElse(null);
}
@Override
public void onNavigate(Map<String, Object> settings) {
lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
lblGameVersion.setText(Launcher.i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
btnForge.setDisable(provider.getForge() != null);
if (provider.getForge() != null || controller.getSettings().containsKey("forge"))
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + Lang.nonNull(provider.getForge(), controller.getSettings().get("forge")));
lblForge.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.forge")) + ": " + Lang.nonNull(provider.getForge(), getVersion("forge")));
else
lblForge.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.forge")));
lblForge.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.forge")));
btnLiteLoader.setDisable(provider.getLiteLoader() != null);
if (provider.getLiteLoader() != null || controller.getSettings().containsKey("liteloader"))
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + Lang.nonNull(provider.getLiteLoader(), controller.getSettings().get("liteloader")));
lblLiteLoader.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.liteloader")) + ": " + Lang.nonNull(provider.getLiteLoader(), getVersion("liteloader")));
else
lblLiteLoader.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.liteloader")));
lblLiteLoader.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.liteloader")));
btnOptiFine.setDisable(provider.getOptiFine() != null);
if (provider.getOptiFine() != null || controller.getSettings().containsKey("optifine"))
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + Lang.nonNull(provider.getOptiFine(), controller.getSettings().get("optifine")));
lblOptiFine.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.optifine")) + ": " + Lang.nonNull(provider.getOptiFine(), getVersion("optifine")));
else
lblOptiFine.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.optifine")));
lblOptiFine.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.optifine")));
}
@@ -18,9 +18,10 @@
package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.setting.Profile;
@@ -33,7 +34,7 @@ import org.jackhuang.hmcl.util.Lang;
import java.io.File;
import java.util.Map;
import static org.jackhuang.hmcl.Main.i18n;
import static org.jackhuang.hmcl.Launcher.i18n;
public final class DownloadWizardProvider implements WizardProvider {
private Profile profile;
@@ -48,16 +49,16 @@ public final class DownloadWizardProvider implements WizardProvider {
GameBuilder builder = profile.getDependency().gameBuilder();
builder.name((String) settings.get("name"));
builder.gameVersion((String) settings.get("game"));
builder.gameVersion(((RemoteVersion<?>) settings.get("game")).getGameVersion());
if (settings.containsKey("forge"))
builder.version("forge", (String) settings.get("forge"));
builder.version((RemoteVersion<?>) settings.get("forge"));
if (settings.containsKey("liteloader"))
builder.version("liteloader", (String) settings.get("liteloader"));
builder.version((RemoteVersion<?>) settings.get("liteloader"));
if (settings.containsKey("optifine"))
builder.version("optifine", (String) settings.get("optifine"));
builder.version((RemoteVersion<?>) settings.get("optifine"));
return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions());
}
@@ -76,8 +77,8 @@ public final class DownloadWizardProvider implements WizardProvider {
@Override
public Object finish(Map<String, Object> settings) {
settings.put("success_message", Main.i18n("install.success"));
settings.put("failure_message", Main.i18n("install.failed"));
settings.put("success_message", Launcher.i18n("install.success"));
settings.put("failure_message", Launcher.i18n("install.failed"));
switch (Lang.parseInt(settings.get(InstallTypePage.INSTALL_TYPE), -1)) {
case 0: return finishVersionDownloadingAsync(settings);
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.JFXListView;
import javafx.fxml.FXML;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage;
@@ -37,6 +37,8 @@ public final class InstallTypePage extends StackPane implements WizardPage {
FXUtils.loadFXML(this, "/assets/fxml/download/dltype.fxml");
list.setOnMouseClicked(e -> {
if (list.getSelectionModel().getSelectedIndex() < 0)
return;
controller.getSettings().put(INSTALL_TYPE, list.getSelectionModel().getSelectedIndex());
controller.onNext();
});
@@ -49,7 +51,7 @@ public final class InstallTypePage extends StackPane implements WizardPage {
@Override
public String getTitle() {
return Main.i18n("install.select");
return Launcher.i18n("install.select");
}
public static final String INSTALL_TYPE = "INSTALL_TYPE";
@@ -18,8 +18,9 @@
package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Task;
@@ -79,19 +80,19 @@ public final class InstallerWizardProvider implements WizardProvider {
@Override
public Object finish(Map<String, Object> settings) {
settings.put("success_message", Main.i18n("install.success"));
settings.put("failure_message", Main.i18n("install.failed"));
settings.put("success_message", Launcher.i18n("install.success"));
settings.put("failure_message", Launcher.i18n("install.failed"));
Task ret = Task.empty();
if (settings.containsKey("forge"))
ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "forge", (String) settings.get("forge")));
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("forge")));
if (settings.containsKey("liteloader"))
ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "liteloader", (String) settings.get("liteloader")));
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("liteloader")));
if (settings.containsKey("optifine"))
ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "optifine", (String) settings.get("optifine")));
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("optifine")));
return ret.then(profile.getRepository().refreshVersionsAsync());
}
@@ -23,8 +23,9 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.Validator;
@@ -76,51 +77,55 @@ public class InstallersPage extends StackPane implements WizardPage {
FXUtils.loadFXML(this, "/assets/fxml/download/installers.fxml");
String gameVersion = (String) controller.getSettings().get("game");
String gameVersion = ((RemoteVersion<?>) controller.getSettings().get("game")).getGameVersion();
Validator hasVersion = new Validator(s -> !repository.hasVersion(s) && StringUtils.isNotBlank(s));
hasVersion.setMessage(Main.i18n("install.new_game.already_exists"));
hasVersion.setMessage(Launcher.i18n("install.new_game.already_exists"));
txtName.getValidators().add(hasVersion);
txtName.textProperty().addListener(e -> btnInstall.setDisable(!txtName.validate()));
txtName.setText(gameVersion);
btnForge.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 0);
controller.onNext(new VersionsPage(controller, Main.i18n("install.installer.choose", Main.i18n("install.installer.forge")), gameVersion, downloadProvider, "forge", () -> controller.onPrev(false)));
controller.onNext(new VersionsPage(controller, Launcher.i18n("install.installer.choose", Launcher.i18n("install.installer.forge")), gameVersion, downloadProvider, "forge", () -> controller.onPrev(false)));
});
btnLiteLoader.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 1);
controller.onNext(new VersionsPage(controller, Main.i18n("install.installer.choose", Main.i18n("install.installer.liteloader")), gameVersion, downloadProvider, "liteloader", () -> controller.onPrev(false)));
controller.onNext(new VersionsPage(controller, Launcher.i18n("install.installer.choose", Launcher.i18n("install.installer.liteloader")), gameVersion, downloadProvider, "liteloader", () -> controller.onPrev(false)));
});
btnOptiFine.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 2);
controller.onNext(new VersionsPage(controller, Main.i18n("install.installer.choose", Main.i18n("install.installer.optifine")), gameVersion, downloadProvider, "optifine", () -> controller.onPrev(false)));
controller.onNext(new VersionsPage(controller, Launcher.i18n("install.installer.choose", Launcher.i18n("install.installer.optifine")), gameVersion, downloadProvider, "optifine", () -> controller.onPrev(false)));
});
}
@Override
public String getTitle() {
return Main.i18n("install.new_game");
return Launcher.i18n("install.new_game");
}
private String getVersion(String id) {
return ((RemoteVersion<?>) controller.getSettings().get(id)).getSelfVersion();
}
@Override
public void onNavigate(Map<String, Object> settings) {
lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + controller.getSettings().get("game"));
lblGameVersion.setText(Launcher.i18n("install.new_game.current_game_version") + ": " + getVersion("game"));
if (controller.getSettings().containsKey("forge"))
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + controller.getSettings().get("forge"));
lblForge.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.forge")) + ": " + getVersion("forge"));
else
lblForge.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.forge")));
lblForge.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.forge")));
if (controller.getSettings().containsKey("liteloader"))
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + controller.getSettings().get("liteloader"));
lblLiteLoader.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.liteloader")) + ": " + getVersion("liteloader"));
else
lblLiteLoader.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.liteloader")));
lblLiteLoader.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.liteloader")));
if (controller.getSettings().containsKey("optifine"))
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + controller.getSettings().get("optifine"));
lblOptiFine.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.optifine")) + ": " + getVersion("optifine"));
else
lblOptiFine.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.optifine")));
lblOptiFine.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.optifine")));
}
@Override
@@ -25,7 +25,7 @@ import javafx.scene.control.Label;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
@@ -75,16 +75,16 @@ public final class ModpackPage extends StackPane implements WizardPage {
Profile profile = (Profile) controller.getSettings().get("PROFILE");
FileChooser chooser = new FileChooser();
chooser.setTitle(Main.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip"));
chooser.setTitle(Launcher.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("modpack"), "*.zip"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile == null) Platform.runLater(() -> Controllers.navigate(null));
else {
controller.getSettings().put(MODPACK_FILE, selectedFile);
lblModpackLocation.setText(selectedFile.getAbsolutePath());
txtModpackName.getValidators().addAll(
new Validator(Main.i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)),
new Validator(Main.i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str))
new Validator(Launcher.i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)),
new Validator(Launcher.i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str))
);
txtModpackName.textProperty().addListener(e -> btnInstall.setDisable(!txtModpackName.validate()));
@@ -96,7 +96,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
lblAuthor.setText(manifest.getAuthor());
txtModpackName.setText(manifest.getName() + (StringUtils.isBlank(manifest.getVersion()) ? "" : "-" + manifest.getVersion()));
} catch (UnsupportedModpackException e) {
txtModpackName.setText(Main.i18n("modpack.task.install.error"));
txtModpackName.setText(Launcher.i18n("modpack.task.install.error"));
}
}
}
@@ -118,14 +118,14 @@ public final class ModpackPage extends StackPane implements WizardPage {
if (manifest != null) {
WebStage stage = new WebStage();
stage.getWebView().getEngine().loadContent(manifest.getDescription());
stage.setTitle(Main.i18n("modpack.wizard.step.3"));
stage.setTitle(Launcher.i18n("modpack.wizard.step.3"));
stage.showAndWait();
}
}
@Override
public String getTitle() {
return Main.i18n("modpack.task.install");
return Launcher.i18n("modpack.task.install");
}
public static final String MODPACK_FILE = "MODPACK_FILE";
@@ -40,6 +40,7 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public final class VersionsPage extends StackPane implements WizardPage, Refreshable {
@@ -93,7 +94,9 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
chkOld.selectedProperty().addListener(listener);
list.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
controller.getSettings().put(libraryId, newValue.getRemoteVersion().getSelfVersion());
if (newValue == null)
return;
controller.getSettings().put(libraryId, newValue.getRemoteVersion());
callback.run();
});
refresh();
@@ -114,6 +117,7 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
}
else return true;
})
.filter(Objects::nonNull)
.sorted()
.map(VersionsPageItem::new).collect(Collectors.toList());
}
@@ -23,13 +23,15 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersionTag;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.ui.FXUtils;
import java.util.Objects;
/**
* @author huangyuhui
*/
@@ -47,7 +49,7 @@ public final class VersionsPageItem extends StackPane {
private StackPane imageViewContainer;
public VersionsPageItem(RemoteVersion<?> remoteVersion) {
this.remoteVersion = remoteVersion;
this.remoteVersion = Objects.requireNonNull(remoteVersion);
FXUtils.loadFXML(this, "/assets/fxml/download/versions-list-item.fxml");
lblSelfVersion.setText(remoteVersion.getSelfVersion());
@@ -55,15 +57,15 @@ public final class VersionsPageItem extends StackPane {
if (remoteVersion.getTag() instanceof GameRemoteVersionTag) {
switch (((GameRemoteVersionTag) remoteVersion.getTag()).getType()) {
case RELEASE:
lblGameVersion.setText(Main.i18n("version.game.release"));
lblGameVersion.setText(Launcher.i18n("version.game.release"));
imageView.setImage(new Image("/assets/img/icon.png", 32, 32, false, true));
break;
case SNAPSHOT:
lblGameVersion.setText(Main.i18n("version.game.snapshot"));
lblGameVersion.setText(Launcher.i18n("version.game.snapshot"));
imageView.setImage(new Image("/assets/img/command.png", 32, 32, false, true));
break;
default:
lblGameVersion.setText(Main.i18n("version.game.old"));
lblGameVersion.setText(Launcher.i18n("version.game.old"));
imageView.setImage(new Image("/assets/img/grass.png", 32, 32, false, true));
break;
}
@@ -25,7 +25,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.game.ModAdviser;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.FXUtils;
@@ -147,23 +147,23 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
@Override
public String getTitle() {
return Main.i18n("modpack.wizard.step.2.title");
return Launcher.i18n("modpack.wizard.step.2.title");
}
public static final String MODPACK_FILE_SELECTION = "modpack.accepted";
private static final Map<String, String> TRANSLATION = Lang.mapOf(
new Pair<>("minecraft/servers.dat", Main.i18n("modpack.files.servers_dat")),
new Pair<>("minecraft/saves", Main.i18n("modpack.files.saves")),
new Pair<>("minecraft/mods", Main.i18n("modpack.files.mods")),
new Pair<>("minecraft/config", Main.i18n("modpack.files.config")),
new Pair<>("minecraft/liteconfig", Main.i18n("modpack.files.liteconfig")),
new Pair<>("minecraft/resourcepacks", Main.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/resources", Main.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/options.txt", Main.i18n("modpack.files.options_txt")),
new Pair<>("minecraft/optionsshaders.txt", Main.i18n("modpack.files.optionsshaders_txt")),
new Pair<>("minecraft/mods/VoxelMods", Main.i18n("modpack.files.mods.voxelmods")),
new Pair<>("minecraft/dumps", Main.i18n("modpack.files.dumps")),
new Pair<>("minecraft/blueprints", Main.i18n("modpack.files.blueprints")),
new Pair<>("minecraft/scripts", Main.i18n("modpack.files.scripts"))
new Pair<>("minecraft/servers.dat", Launcher.i18n("modpack.files.servers_dat")),
new Pair<>("minecraft/saves", Launcher.i18n("modpack.files.saves")),
new Pair<>("minecraft/mods", Launcher.i18n("modpack.files.mods")),
new Pair<>("minecraft/config", Launcher.i18n("modpack.files.config")),
new Pair<>("minecraft/liteconfig", Launcher.i18n("modpack.files.liteconfig")),
new Pair<>("minecraft/resourcepacks", Launcher.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/resources", Launcher.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/options.txt", Launcher.i18n("modpack.files.options_txt")),
new Pair<>("minecraft/optionsshaders.txt", Launcher.i18n("modpack.files.optionsshaders_txt")),
new Pair<>("minecraft/mods/VoxelMods", Launcher.i18n("modpack.files.mods.voxelmods")),
new Pair<>("minecraft/dumps", Launcher.i18n("modpack.files.dumps")),
new Pair<>("minecraft/blueprints", Launcher.i18n("modpack.files.blueprints")),
new Pair<>("minecraft/scripts", Launcher.i18n("modpack.files.scripts"))
);
}
@@ -26,7 +26,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.ui.Controllers;
@@ -72,8 +72,8 @@ public final class ModpackInfoPage extends StackPane implements WizardPage {
@FXML
private void onNext() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(Main.i18n("modpack.wizard.step.initialization.save"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip"));
fileChooser.setTitle(Launcher.i18n("modpack.wizard.step.initialization.save"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("modpack"), "*.zip"));
File file = fileChooser.showSaveDialog(Controllers.getStage());
if (file == null) {
Controllers.navigate(null);
@@ -100,7 +100,7 @@ public final class ModpackInfoPage extends StackPane implements WizardPage {
@Override
public String getTitle() {
return Main.i18n("modpack.wizard.step.1.title");
return Launcher.i18n("modpack.wizard.step.1.title");
}
public static final String MODPACK_NAME = "modpack.name";
@@ -18,9 +18,8 @@
package org.jackhuang.hmcl.upgrade;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
@@ -100,15 +99,15 @@ public class AppDataUpgrader extends IUpgrader {
checker.requestDownloadLink().then(Task.of(Schedulers.javafx(), variables -> {
Map<String, String> map = variables.get(UpdateChecker.REQUEST_DOWNLOAD_LINK_ID);
if (MessageBox.confirm(Main.i18n("update.newest_version") + version.toString() + "\n"
+ Main.i18n("update.should_open_link"),
if (MessageBox.confirm(Launcher.i18n("update.newest_version") + version.toString() + "\n"
+ Launcher.i18n("update.should_open_link"),
MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
if (map != null && map.containsKey("jar") && !StringUtils.isBlank(map.get("jar")))
try {
String hash = null;
if (map.containsKey("jarsha1"))
hash = map.get("jarsha1");
Controllers.dialog(Main.i18n("message.downloading"));
Controllers.dialog(Launcher.i18n("message.downloading"));
if (new AppDataUpgraderJarTask(NetworkUtils.toURL(map.get("jar")), version.toString(), hash).test()) {
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderJarTask.getSelf(version.toString()).getAbsolutePath())
.directory(new File("").getAbsoluteFile()).start();
@@ -123,7 +122,7 @@ public class AppDataUpgrader extends IUpgrader {
String hash = null;
if (map.containsKey("packsha1"))
hash = map.get("packsha1");
Controllers.dialog(Main.i18n("message.downloading"));
Controllers.dialog(Launcher.i18n("message.downloading"));
if (new AppDataUpgraderPackGzTask(NetworkUtils.toURL(map.get("pack")), version.toString(), hash).test()) {
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderPackGzTask.getSelf(version.toString()).getAbsolutePath())
.directory(new File("").getAbsoluteFile()).start();
@@ -134,20 +133,20 @@ public class AppDataUpgrader extends IUpgrader {
Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex);
}
else {
String url = Main.PUBLISH;
String url = Launcher.PUBLISH;
if (map != null)
if (map.containsKey(OperatingSystem.CURRENT_OS.getCheckedName()))
url = map.get(OperatingSystem.CURRENT_OS.getCheckedName());
else if (map.containsKey(OperatingSystem.UNKNOWN.getCheckedName()))
url = map.get(OperatingSystem.UNKNOWN.getCheckedName());
if (url == null)
url = Main.PUBLISH;
url = Launcher.PUBLISH;
try {
java.awt.Desktop.getDesktop().browse(new URI(url));
} catch (URISyntaxException | IOException e) {
Logging.LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e);
OperatingSystem.setClipboard(url);
MessageBox.show(Main.i18n("update.no_browser"));
MessageBox.show(Launcher.i18n("update.no_browser"));
}
}
})).start();
@@ -155,7 +154,7 @@ public class AppDataUpgrader extends IUpgrader {
public static class AppDataUpgraderPackGzTask extends Task {
public static final File BASE_FOLDER = Main.HMCL_DIRECTORY;
public static final File BASE_FOLDER = Launcher.HMCL_DIRECTORY;
public static final File HMCL_VER_FILE = new File(BASE_FOLDER, "hmclver.json");
public static File getSelf(String ver) {
@@ -205,7 +204,7 @@ public class AppDataUpgrader extends IUpgrader {
public static class AppDataUpgraderJarTask extends Task {
public static final File BASE_FOLDER = Main.getWorkingDirectory("hmcl");
public static final File BASE_FOLDER = Launcher.getWorkingDirectory("hmcl");
public static final File HMCL_VER_FILE = new File(BASE_FOLDER, "hmclver.json");
public static File getSelf(String ver) {
@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.upgrade;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.util.Charsets;
@@ -51,7 +51,7 @@ public class NewFileUpgrader extends IUpgrader {
URL url = requestDownloadLink();
if (url == null) return;
File newf = new File(url.getFile());
Controllers.dialog(Main.i18n("message.downloading"));
Controllers.dialog(Launcher.i18n("message.downloading"));
if (new FileDownloadTask(url, newf).test()) {
try {
new ProcessBuilder(newf.getCanonicalPath(), "--removeOldLauncher", getRealPath())
@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.upgrade;
import com.google.gson.JsonSyntaxException;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.event.Event;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.OutOfDateEvent;
@@ -67,18 +67,18 @@ public final class UpdateChecker {
return new TaskResult<VersionNumber>() {
@Override
public void execute() throws Exception {
if (Main.VERSION.contains("@"))
if (Launcher.VERSION.contains("@"))
return;
if (value == null) {
versionString = NetworkUtils.doGet(NetworkUtils.toURL("http://huangyuhui.duapp.com/hmcl/update.php?version=" + Main.VERSION));
versionString = NetworkUtils.doGet(NetworkUtils.toURL("http://huangyuhui.duapp.com/hmcl/update.php?version=" + Launcher.VERSION));
value = VersionNumber.asVersion(versionString);
}
if (value == null) {
Logging.LOG.warning("Unable to check update...");
if (showMessage)
MessageBox.show(Main.i18n("update.failed"));
MessageBox.show(Launcher.i18n("update.failed"));
} else if (base.compareTo(value) < 0)
outOfDate = true;
if (outOfDate)
@@ -134,7 +134,7 @@ public final class UpdateChecker {
public void checkOutdate() {
if (outOfDate)
if (EventBus.EVENT_BUS.fireEvent(new OutOfDateEvent(this, getNewVersion())) != Event.Result.DENY) {
Main.UPGRADER.download(this, getNewVersion());
Launcher.UPGRADER.download(this, getNewVersion());
}
}
}
@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.util;
import javafx.application.Platform;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.ui.CrashWindow;
import org.jackhuang.hmcl.ui.construct.MessageBox;
@@ -36,15 +36,15 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
private static final HashMap<String, String> SOURCE = new HashMap<String, String>() {
{
put("javafx.fxml.LoadException", Main.i18n("crash.NoClassDefFound"));
put("UnsatisfiedLinkError", Main.i18n("crash.user_fault"));
put("java.lang.NoClassDefFoundError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.VerifyError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.NoSuchMethodError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.IncompatibleClassChangeError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.ClassFormatError", Main.i18n("crash.NoClassDefFound"));
put("javafx.fxml.LoadException", Launcher.i18n("crash.NoClassDefFound"));
put("UnsatisfiedLinkError", Launcher.i18n("crash.user_fault"));
put("java.lang.NoClassDefFoundError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.VerifyError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.NoSuchMethodError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.IncompatibleClassChangeError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.ClassFormatError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.OutOfMemoryError", "FUCKING MEMORY LIMIT!");
put("Trampoline", Main.i18n("launcher.update_java"));
put("Trampoline", Launcher.i18n("launcher.update_java"));
put("NoSuchAlgorithmException", "Has your operating system been installed completely or is a ghost system?");
}
};
@@ -80,7 +80,7 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
try {
StringBuilder builder = new StringBuilder();
builder.append("---- Hello Minecraft! Crash Report ----\n");
builder.append(" Version: " + Main.VERSION + "\n");
builder.append(" Version: " + Launcher.VERSION + "\n");
builder.append(" Time: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("\n");
builder.append(" Thread: ").append(t.toString()).append("\n");
builder.append("\n Content: \n ");
@@ -95,7 +95,7 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
if (checkThrowable(e) && !text.contains("OpenJDK")) {
Platform.runLater(() -> new CrashWindow(text).show());
if (!Main.UPDATE_CHECKER.isOutOfDate())
if (!Launcher.UPDATE_CHECKER.isOutOfDate())
reportToServer(text);
}
} catch (Throwable ex) {
@@ -110,7 +110,8 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
Thread t = new Thread(() -> {
HashMap<String, String> map = new HashMap<>();
map.put("crash_report", text);
map.put("version", Main.VERSION);
map.put("version", Launcher.VERSION);
map.put("log", Logging.getLogs());
try {
String response = NetworkUtils.doPost(NetworkUtils.toURL("http://huangyuhui.duapp.com/hmcl/crash.php"), map);
if (StringUtils.isNotBlank(response))
@@ -35,6 +35,8 @@
<JFXTextField fx:id="txtServerIp" promptText="%account.injector.server_ip" labelFloat="true">
<validators>
<URLValidator message="%input.url">
</URLValidator>
<URLValidator message="%input.url.http">
<protocols>
<String fx:value="http" />
<String fx:value="https" />
@@ -6,14 +6,18 @@
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import com.jfoenix.controls.JFXSpinner?>
<fx:root
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
type="StackPane" pickOnBounds="false"
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane>
</ScrollPane>
<StackPane fx:id="contentPane">
<JFXSpinner fx:id="spinner" styleClass="first-spinner" />
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane>
</ScrollPane>
</StackPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnRefresh" styleClass="jfx-button-raised-round">
<graphic>
@@ -15,7 +15,7 @@
<VBox style="-fx-padding: 8 8 0 8;">
<Label fx:id="lblVersionName" style="-fx-font-size: 15;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblGameVersion" style="-fx-font-size: 11;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblLibraries" style="-fx-font-size: 11; -fx-text-fill: gray;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblLibraries" style="-fx-font-size: 10; -fx-text-fill: gray;" textAlignment="JUSTIFY" wrapText="true" />
</VBox>
</StackPane>
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40" pickOnBounds="false">
@@ -136,6 +136,7 @@ input.email=The username must be an e-mail.
input.number=Must be a number.
input.not_empty=Input Requrired!
input.url=Must be a valid URL.
input.url.http=Only Http or Https accepted.
install=Install New Game
install.failed=Failed to install
@@ -136,6 +136,7 @@ input.email=用户名必须是邮箱
input.number=必须是数字
input.not_empty=必填项
input.url=必须是合法的链接
input.url.http=只支持Http或Https协议
install=添加游戏
install.failed=安装失败
@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.util.StringUtils;
@@ -18,9 +18,12 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
@@ -80,25 +83,31 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
@Override
public Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
switch (libraryId) {
case "forge":
return new ForgeInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
case "liteloader":
return new LiteLoaderInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
case "optifine":
return new OptiFineInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
default:
throw new IllegalArgumentException("Library id " + libraryId + " is unrecognized.");
}
VersionList<?> versionList = getVersionList(libraryId);
return versionList.loadAsync(getDownloadProvider())
.then(variables -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
.orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion))));
}
@Override
public Task installLibraryAsync(Version version, RemoteVersion<?> libraryVersion) {
if (libraryVersion instanceof ForgeRemoteVersion)
return new ForgeInstallTask(this, version, (ForgeRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
else if (libraryVersion instanceof LiteLoaderRemoteVersion)
return new LiteLoaderInstallTask(this, version, (LiteLoaderRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
else if (libraryVersion instanceof OptiFineRemoteVersion)
return new OptiFineInstallTask(this, version, (OptiFineRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
else
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
}
}
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.game.SimpleVersionProvider;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
@@ -52,7 +51,7 @@ public class DefaultGameBuilder extends GameBuilder {
public Task buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> {
Version version = Constants.GSON.fromJson(variables.<String>get(VersionJsonDownloadTask.ID), Version.class);
version = version.setId(name).setJar(null).resolve(new SimpleVersionProvider());
version = version.setId(name).setJar(null);
variables.set("version", version);
Task result = new ParallelTask(
new GameAssetDownloadTask(dependencyManager, version),
@@ -67,6 +66,10 @@ public class DefaultGameBuilder extends GameBuilder {
result = result.then(libraryTaskHelper(gameVersion, "liteloader"));
if (toolVersions.containsKey("optifine"))
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
for (RemoteVersion<?> remoteVersion : remoteVersions)
result = result.then(var -> dependencyManager.installLibraryAsync(var.get("version"), remoteVersion));
return result;
}).finalized((variables, isDependentsSucceeded) -> {
if (!isDependentsSucceeded)
@@ -66,6 +66,16 @@ public interface DependencyManager {
*/
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
/**
* Install a library to a version.
* **Note**: Installing a library may change the version.json.
*
* @param version the version.json.\
* @param libraryVersion the remote version of being installed library.
* @return the task to install the specific library.
*/
Task installLibraryAsync(Version version, RemoteVersion<?> libraryVersion);
/**
* Get registered version list.
*
@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.task.Task;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.*;
/**
* The builder which provide a task to build Minecraft environment.
@@ -33,6 +31,7 @@ public abstract class GameBuilder {
protected String name = "";
protected String gameVersion = "";
protected final Map<String, String> toolVersions = new HashMap<>();
protected final Set<RemoteVersion<?>> remoteVersions = new HashSet<>();
public String getName() {
return name;
@@ -65,6 +64,11 @@ public abstract class GameBuilder {
return this;
}
public GameBuilder version(RemoteVersion<?> remoteVersion) {
remoteVersions.add(remoteVersion);
return this;
}
/**
* @return the task that can build thw whole Minecraft environment
*/
@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult;
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.VersionNumber;
import java.util.Objects;
@@ -74,6 +75,15 @@ public class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
return selfVersion.hashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("selfVersion", selfVersion)
.append("gameVersion", gameVersion)
.append("tag", tag)
.toString();
}
@Override
public int compareTo(RemoteVersion<T> o) {
// newer versions are smaller than older versions
@@ -21,6 +21,7 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SimpleMultimap;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* The remote version list.
@@ -45,15 +46,36 @@ public abstract class VersionList<T> {
return !versions.isEmpty();
}
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* @param downloadProvider DownloadProvider
* @return the task to reload the remote version list.
*/
public abstract Task refreshAsync(DownloadProvider downloadProvider);
public Task loadAsync(DownloadProvider downloadProvider) {
return Task.ofThen(variables -> {
lock.readLock().lock();
boolean loaded;
try {
loaded = isLoaded();
} finally {
lock.readLock().unlock();
}
return loaded ? null : refreshAsync(downloadProvider);
});
}
private Collection<RemoteVersion<T>> getVersionsImpl(String gameVersion) {
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
return ans.isEmpty() ? versions.values() : ans;
lock.readLock().lock();
try {
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
return ans.isEmpty() ? versions.values() : ans;
} finally {
lock.readLock().unlock();
}
}
/**
@@ -74,10 +96,15 @@ public abstract class VersionList<T> {
* @return the specific remote version, null if it is not found.
*/
public final Optional<RemoteVersion<T>> getVersion(String gameVersion, String remoteVersion) {
RemoteVersion<T> result = null;
for (RemoteVersion<T> it : versions.get(gameVersion))
if (remoteVersion.equals(it.getSelfVersion()))
result = it;
return Optional.ofNullable(result);
lock.readLock().lock();
try {
RemoteVersion<T> result = null;
for (RemoteVersion<T> it : versions.get(gameVersion))
if (remoteVersion.equals(it.getSelfVersion()))
result = it;
return Optional.ofNullable(result);
} finally {
lock.readLock().unlock();
}
}
}
@@ -18,8 +18,6 @@
package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.SimpleVersionProvider;
@@ -47,34 +45,22 @@ import java.util.zip.ZipFile;
public final class ForgeInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final VersionList<?> forgeVersionList;
private final File installer = new File("forge-installer.jar").getAbsoluteFile();
private RemoteVersion<?> remote;
private ForgeRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private Task downloadFileTask() {
remote = forgeVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote forge version " + gameVersion + ", " + remoteVersion + " not found"));
return new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), installer);
}
public ForgeInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
this.remote = remoteVersion;
forgeVersionList = dependencyManager.getVersionList("forge");
if (!forgeVersionList.isLoaded())
dependents.add(forgeVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(s -> downloadFileTask()));
else
dependents.add(downloadFileTask());
dependents.add(downloadFileTask());
}
@Override
@@ -0,0 +1,33 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.RemoteVersion;
public class ForgeRemoteVersion extends RemoteVersion<Void> {
/**
* Constructor.
*
* @param gameVersion the Minecraft version that this remote version suits.
* @param selfVersion the version string of the remote version.
* @param url the installer or universal jar URL.
*/
public ForgeRemoteVersion(String gameVersion, String selfVersion, String url) {
super(gameVersion, selfVersion, url, null);
}
}
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
@@ -53,34 +52,40 @@ public final class ForgeVersionList extends VersionList<Void> {
@Override
public void execute() {
ForgeVersionRoot root = Constants.GSON.fromJson(task.getResult(), ForgeVersionRoot.class);
if (root == null)
return;
versions.clear();
lock.writeLock().lock();
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
Optional<String> gameVersion = VersionNumber.parseVersion(entry.getKey());
if (!gameVersion.isPresent())
continue;
for (int v : entry.getValue()) {
ForgeVersion version = root.getNumber().get(v);
if (version == null)
continue;
String jar = null;
for (String[] file : version.getFiles())
if (file.length > 1 && "installer".equals(file[1])) {
String classifier = version.getGameVersion() + "-" + version.getVersion()
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
jar = downloadProvider.injectURL(root.getWebPath() + classifier + "/" + fileName);
}
try {
ForgeVersionRoot root = Constants.GSON.fromJson(task.getResult(), ForgeVersionRoot.class);
if (root == null)
return;
versions.clear();
if (jar == null)
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
Optional<String> gameVersion = VersionNumber.parseVersion(entry.getKey());
if (!gameVersion.isPresent())
continue;
versions.put(gameVersion.get(), new RemoteVersion<>(
version.getGameVersion(), version.getVersion(), jar, null
));
for (int v : entry.getValue()) {
ForgeVersion version = root.getNumber().get(v);
if (version == null)
continue;
String jar = null;
for (String[] file : version.getFiles())
if (file.length > 1 && "installer".equals(file[1])) {
String classifier = version.getGameVersion() + "-" + version.getVersion()
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
jar = downloadProvider.injectURL(root.getWebPath() + classifier + "/" + fileName);
}
if (jar == null)
continue;
versions.put(gameVersion.get(), new ForgeRemoteVersion(
version.getGameVersion(), version.getVersion(), jar
));
}
}
} finally {
lock.writeLock().unlock();
}
}
@@ -55,7 +55,7 @@ public final class GameAssetDownloadTask extends Task {
*/
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
this.refreshTask = new GameAssetRefreshTask(dependencyManager, version);
this.dependents.add(refreshTask);
}
@@ -82,7 +82,7 @@ public final class GameAssetDownloadTask extends Task {
AssetObject assetObject = entry.getValue();
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) {
Logging.LOG.log(Level.SEVERE, "Unable to create new file {0}, because parent directory cannot be created", file);
Logging.LOG.log(Level.SEVERE, "Unable to create new file " + file + ", because parent directory cannot be created");
continue;
}
if (file.isDirectory())
@@ -49,7 +49,7 @@ public final class GameAssetIndexDownloadTask extends Task {
*/
public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -54,7 +54,7 @@ public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File,
*/
public GameAssetRefreshTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
this.assetIndexInfo = version.getAssetIndex();
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
@@ -20,9 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
import java.io.File;
import java.util.LinkedList;
@@ -48,7 +46,7 @@ public final class GameLibrariesTask extends Task {
*/
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -62,9 +60,7 @@ public final class GameLibrariesTask extends Task {
version.getLibraries().stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> {
File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
if (!file.exists())
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl())),
file, dependencyManager.getProxy(), library.getDownload().getSha1()));
dependencies.add(new LibraryDownloadTask(dependencyManager, file, library));
});
}
@@ -49,7 +49,7 @@ public final class GameLoggingDownloadTask extends Task {
*/
public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -50,16 +50,22 @@ public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
@Override
public void execute() {
versions.clear();
lock.writeLock().lock();
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
for (GameRemoteVersion remoteVersion : root.getVersions()) {
versions.put(remoteVersion.getGameVersion(), new RemoteVersionGame(
remoteVersion.getGameVersion(),
remoteVersion.getGameVersion(),
remoteVersion.getUrl(),
new GameRemoteVersionTag(remoteVersion.getType(), remoteVersion.getReleaseTime()))
);
try {
versions.clear();
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
for (GameRemoteVersion remoteVersion : root.getVersions()) {
versions.put(remoteVersion.getGameVersion(), new RemoteVersionGame(
remoteVersion.getGameVersion(),
remoteVersion.getGameVersion(),
remoteVersion.getUrl(),
new GameRemoteVersionTag(remoteVersion.getType(), remoteVersion.getReleaseTime()))
);
}
} finally {
lock.writeLock().unlock();
}
}
};
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.VersionNumber;
@@ -49,6 +50,7 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
for (Library library : libraries) {
String id = library.getGroupId() + ":" + library.getArtifactId();
VersionNumber number = VersionNumber.asVersion(library.getVersion());
String serialized = Constants.GSON.toJson(library);
if (versionMap.containsKey(id)) {
VersionNumber otherNumber = versionMap.get(id);
@@ -56,8 +58,23 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
multimap.removeKey(id);
versionMap.put(id, number);
multimap.put(id, library);
} else if (number.compareTo(otherNumber) == 0) {
multimap.put(id, library);
} else if (number.compareTo(otherNumber) == 0) { // same library id.
boolean flag = false;
// prevent from duplicated libraries
for (Library otherLibrary : multimap.get(id))
if (library.equals(otherLibrary)) {
String otherSerialized = Constants.GSON.toJson(otherLibrary);
// A trick, the library that has more information is better, which can be
// considered whose serialized JSON text will be longer.
if (serialized.length() <= otherSerialized.length()) {
flag = true;
break;
} else {
multimap.removeValue(id, otherLibrary);
}
}
if (!flag)
multimap.put(id, library);
}
} else {
versionMap.put(id, number);
@@ -0,0 +1,166 @@
package org.jackhuang.hmcl.download.game;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.*;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
public final class LibraryDownloadTask extends Task {
private final FileDownloadTask xzTask;
private final FileDownloadTask task;
private final File jar;
private final File xzFile;
private final Library library;
private boolean downloaded = false;
public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) {
if (library.is("net.minecraftforge", "forge"))
library = library.setClassifier("universal");
this.library = library;
String url = dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl());
jar = file;
xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz");
xzTask = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"),
xzFile, dependencyManager.getProxy(), null, 1);
xzTask.setSignificance(TaskSignificance.MINOR);
setSignificance(TaskSignificance.MODERATE);
task = new FileDownloadTask(NetworkUtils.toURL(url),
file, dependencyManager.getProxy(), library.getDownload().getSha1());
}
@Override
public Collection<? extends Task> getDependents() {
return library.getChecksums() != null ? Collections.singleton(xzTask) : Collections.emptySet();
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
@Override
public void execute() throws Exception {
if (isDependentsSucceeded() && library.getChecksums() != null) {
unpackLibrary(jar, FileUtils.readBytes(xzFile));
if (!checksumValid(jar, library.getChecksums()))
throw new IOException("Checksum failed for " + library);
downloaded = true;
}
}
@Override
public Collection<? extends Task> getDependencies() {
return downloaded ? Collections.emptySet() : Collections.singleton(task);
}
private static boolean checksumValid(File libPath, List<String> checksums) {
try {
if ((checksums == null) || (checksums.isEmpty())) {
return true;
}
byte[] fileData = FileUtils.readBytes(libPath);
boolean valid = checksums.contains(DigestUtils.sha1Hex(fileData));
if ((!valid) && (libPath.getName().endsWith(".jar"))) {
}
return validateJar(fileData, checksums);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private static boolean validateJar(byte[] data, List<String> checksums) throws IOException {
HashMap<String, String> files = new HashMap<>();
String[] hashes = null;
JarInputStream jar = new JarInputStream(new ByteArrayInputStream(data));
JarEntry entry = jar.getNextJarEntry();
while (entry != null) {
byte[] eData = IOUtils.readFullyAsByteArray(jar);
if (entry.getName().equals("checksums.sha1")) {
hashes = new String(eData, Charset.forName("UTF-8")).split("\n");
}
if (!entry.isDirectory()) {
files.put(entry.getName(), DigestUtils.sha1Hex(eData));
}
entry = jar.getNextJarEntry();
}
jar.close();
if (hashes != null) {
boolean passed = !checksums.contains(files.get("checksums.sha1"));
if (passed) {
for (String hash : hashes) {
if ((!hash.trim().equals("")) && (hash.contains(" "))) {
String[] e = hash.split(" ");
String validChecksum = e[0];
String target = hash.substring(validChecksum.length() + 1);
String checksum = files.get(target);
if ((!files.containsKey(target)) || (checksum == null)) {
Logging.LOG.warning(" " + target + " : missing");
passed = false;
break;
} else if (!checksum.equals(validChecksum)) {
Logging.LOG.warning(" " + target + " : failed (" + checksum + ", " + validChecksum + ")");
passed = false;
break;
}
}
}
}
return passed;
}
return false;
}
private static void unpackLibrary(File dest, byte[] src) throws IOException {
if (dest.exists())
if (!dest.delete())
throw new IOException("Unable to delete file " + dest);
byte[] decompressed = IOUtils.readFullyAsByteArray(new XZCompressorInputStream(new ByteArrayInputStream(src)));
String end = new String(decompressed, decompressed.length - 4, 4);
if (!end.equals("SIGN"))
throw new IOException("Unpacking failed, signature missing " + end);
int x = decompressed.length;
int len = decompressed[(x - 8)] & 0xFF | (decompressed[(x - 7)] & 0xFF) << 8 | (decompressed[(x - 6)] & 0xFF) << 16 | (decompressed[(x - 5)] & 0xFF) << 24;
File temp = FileUtils.createTempFile("minecraft", ".pack");
byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8);
OutputStream out = new FileOutputStream(temp);
out.write(decompressed, 0, decompressed.length - len - 8);
out.close();
try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) {
Pack200.newUnpacker().unpack(temp, jos);
JarEntry checksumsFile = new JarEntry("checksums.sha1");
checksumsFile.setTime(0L);
jos.putNextEntry(checksumsFile);
jos.write(checksums);
jos.closeEntry();
}
temp.delete();
}
}
@@ -40,11 +40,11 @@ public final class VersionJsonSaveTask extends Task {
* Constructor.
*
* @param repository the game repository
* @param version the **resolved** version
* @param version the game version
*/
public VersionJsonSaveTask(DefaultGameRepository repository, Version version) {
this.repository = repository;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
import org.jackhuang.hmcl.game.Library;
@@ -41,32 +40,15 @@ import java.util.List;
public final class LiteLoaderInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final LiteLoaderVersionList liteLoaderVersionList;
private RemoteVersion<LiteLoaderRemoteVersionTag> remote;
private LiteLoaderRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private void doRemote() {
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote LiteLoader version " + gameVersion + ", " + remoteVersion + " not found"));
}
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
liteLoaderVersionList = (LiteLoaderVersionList) dependencyManager.getVersionList("liteloader");
if (!liteLoaderVersionList.isLoaded())
dependents.add(liteLoaderVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(Task.of(this::doRemote)));
else
doRemote();
this.remote = remoteVersion;
}
@Override
@@ -0,0 +1,36 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.Library;
import java.util.Collection;
public class LiteLoaderRemoteVersion extends RemoteVersion<LiteLoaderRemoteVersionTag> {
/**
* Constructor.
*
* @param gameVersion the Minecraft version that this remote version suits.
* @param selfVersion the version string of the remote version.
* @param url the installer or universal jar URL.
*/
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, String url, String tweakClass, Collection<Library> libraries) {
super(gameVersion, selfVersion, url, new LiteLoaderRemoteVersionTag(tweakClass, libraries));
}
}
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
@@ -58,17 +57,23 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
@Override
public void execute() {
LiteLoaderVersionsRoot root = Constants.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
versions.clear();
lock.writeLock().lock();
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
String gameVersion = entry.getKey();
LiteLoaderGameVersions liteLoader = entry.getValue();
Optional<String> gg = VersionNumber.parseVersion(gameVersion);
if (!gg.isPresent())
continue;
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
try {
LiteLoaderVersionsRoot root = Constants.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
versions.clear();
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
String gameVersion = entry.getKey();
LiteLoaderGameVersions liteLoader = entry.getValue();
Optional<String> gg = VersionNumber.parseVersion(gameVersion);
if (!gg.isPresent())
continue;
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
}
} finally {
lock.writeLock().unlock();
}
}
@@ -92,9 +97,9 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
}
}
versions.put(key, new RemoteVersion<>(gameVersion,
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
version, downloadProvider.injectURL(url),
new LiteLoaderRemoteVersionTag(v.getTweakClass(), v.getLibraries())
v.getTweakClass(), v.getLibraries()
));
}
}
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.download.optifine;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
@@ -67,7 +66,7 @@ public final class OptiFineBMCLVersionList extends VersionList<Void> {
if (StringUtils.isBlank(element.getGameVersion()))
continue;
VersionNumber.parseVersion(element.getGameVersion())
.ifPresent(gameVersion -> versions.put(gameVersion, new RemoteVersion<>(gameVersion, version, mirror, null)));
.ifPresent(gameVersion -> versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, () -> mirror)));
}
}
};
@@ -18,8 +18,6 @@
package org.jackhuang.hmcl.download.optifine;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task;
@@ -39,32 +37,15 @@ import java.util.List;
public final class OptiFineInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final VersionList<?> optiFineVersionList;
private RemoteVersion<?> remote;
private OptiFineRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private void doRemote() {
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote OptiFine version " + gameVersion + ", " + remoteVersion + " not found"));
}
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
optiFineVersionList = dependencyManager.getVersionList("optifine");
if (!optiFineVersionList.isLoaded())
dependents.add(optiFineVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(Task.of(this::doRemote)));
else
doRemote();
this.remote = remoteVersion;
}
@Override
@@ -89,6 +70,8 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
@Override
public void execute() {
String remoteVersion = remote.getSelfVersion();
Library library = new Library(
"net.optifine", "optifine", remoteVersion, null, null,
new LibrariesDownloadInfo(new LibraryDownloadInfo(
@@ -24,8 +24,8 @@ import java.util.function.Supplier;
public class OptiFineRemoteVersion extends RemoteVersion<Void> {
private final Supplier<String> url;
public OptiFineRemoteVersion(String gameVersion, String selfVersion, Supplier<String> url, Void tag) {
super(gameVersion, selfVersion, "", tag);
public OptiFineRemoteVersion(String gameVersion, String selfVersion, Supplier<String> url) {
super(gameVersion, selfVersion, "", null);
this.url = url;
}
@@ -61,48 +61,54 @@ public final class OptiFineVersionList extends VersionList<Void> {
@Override
public void execute() throws Exception {
versions.clear();
lock.writeLock().lock();
String html = task.getResult().replace("&nbsp;", " ").replace("&gt;", ">").replace("&lt;", "<").replace("<br>", "<br />");
try {
versions.clear();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(html.getBytes("UTF-8")));
Element r = doc.getDocumentElement();
NodeList tables = r.getElementsByTagName("table");
for (int i = 0; i < tables.getLength(); i++) {
Element e = (Element) tables.item(i);
if ("downloadTable".equals(e.getAttribute("class"))) {
NodeList tr = e.getElementsByTagName("tr");
for (int k = 0; k < tr.getLength(); k++) {
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
String url = null, version = null, gameVersion = null;
for (int j = 0; j < downloadLine.getLength(); j++) {
Element td = (Element) downloadLine.item(j);
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineMirror"))
url = ((Element) td.getElementsByTagName("a").item(0)).getAttribute("href");
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
version = td.getTextContent();
String html = task.getResult().replace("&nbsp;", " ").replace("&gt;", ">").replace("&lt;", "<").replace("<br>", "<br />");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(html.getBytes("UTF-8")));
Element r = doc.getDocumentElement();
NodeList tables = r.getElementsByTagName("table");
for (int i = 0; i < tables.getLength(); i++) {
Element e = (Element) tables.item(i);
if ("downloadTable".equals(e.getAttribute("class"))) {
NodeList tr = e.getElementsByTagName("tr");
for (int k = 0; k < tr.getLength(); k++) {
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
String url = null, version = null, gameVersion = null;
for (int j = 0; j < downloadLine.getLength(); j++) {
Element td = (Element) downloadLine.item(j);
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineMirror"))
url = ((Element) td.getElementsByTagName("a").item(0)).getAttribute("href");
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
version = td.getTextContent();
}
if (version == null || url == null)
continue;
Matcher matcher = PATTERN.matcher(version);
while (matcher.find())
gameVersion = matcher.group(1);
if (gameVersion == null)
continue;
String finalURL = url;
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Lang.hideException(() -> getLink(finalURL))));
}
if (version == null || url == null)
continue;
Matcher matcher = PATTERN.matcher(version);
while (matcher.find())
gameVersion = matcher.group(1);
if (gameVersion == null)
continue;
String finalURL = url;
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Lang.hideException(() -> getLink(finalURL)), null));
}
}
} finally {
lock.writeLock().unlock();
}
}
};
}
public static String getLink(String url) throws IOException {
private static String getLink(String url) throws IOException {
String result = null;
String content = NetworkUtils.doGet(NetworkUtils.toURL(url));
Matcher m = LINK_PATTERN.matcher(content);
@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.util.Objects;
/**
@@ -28,13 +30,13 @@ public class Event {
/**
* The object on which the Event initially occurred.
*/
protected transient Object source;
protected transient Object source;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws NullPointerException if source is null.
* @throws NullPointerException if source is null.
*/
public Event(Object source) {
Objects.requireNonNull(source);
@@ -54,10 +56,10 @@ public class Event {
/**
* Returns a String representation of this Event.
*
* @return A a String representation of this Event.
* @return A a String representation of this Event.
*/
public String toString() {
return getClass().getName() + "[source=" + source + "]";
return new ToStringBuilder(this).append("source", source).toString();
}
private boolean canceled;
@@ -72,7 +74,6 @@ public class Event {
}
/**
*
* @param canceled new value
* @throws UnsupportedOperationException if trying to cancel a non-cancelable event.
*/
@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.Logging;
import java.util.HashMap;
/**
@@ -36,6 +38,8 @@ public final class EventBus {
@SuppressWarnings("unchecked")
public Event.Result fireEvent(Event obj) {
Logging.LOG.info(obj + " gets fired");
return channel((Class<Event>) obj.getClass()).fireEvent(obj);
}
@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.io.File;
/**
@@ -49,4 +51,13 @@ public final class GameJsonParseFailedEvent extends Event {
public String getVersion() {
return version;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("jsonFile", jsonFile)
.append("version", version)
.toString();
}
}
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ManagedProcess;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when we launch the JVM and it got crashed.
@@ -44,4 +45,12 @@ public class JVMLaunchFailedEvent extends Event {
public ManagedProcess getProcess() {
return process;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("process", process)
.toString();
}
}
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when a minecraft version has been loaded.
@@ -48,4 +49,12 @@ public final class LoadedOneVersionEvent extends Event {
public boolean hasResult() {
return true;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("version", version)
.toString();
}
}
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ManagedProcess;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when a JavaProcess exited abnormally and the exit code is not zero.
@@ -44,4 +45,12 @@ public final class ProcessExitedAbnormallyEvent extends Event {
public ManagedProcess getProcess() {
return process;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("process", process)
.toString();
}
}
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ManagedProcess;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when minecraft process exited successfully and the exit code is 0.
@@ -44,4 +45,12 @@ public class ProcessStoppedEvent extends Event {
public ManagedProcess getProcess() {
return process;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("process", process)
.toString();
}
}
@@ -35,4 +35,8 @@ public final class RefreshingVersionsEvent extends Event {
super(source);
}
@Override
public boolean hasResult() {
return true;
}
}
@@ -0,0 +1,59 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when a minecraft version is being removed.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @author huangyuhui
*/
public class RemoveVersionEvent extends Event {
private final String version;
/**
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the version id.
*/
public RemoveVersionEvent(Object source, String version) {
super(source);
this.version = version;
}
public String getVersion() {
return version;
}
@Override
public boolean hasResult() {
return true;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("version", version)
.toString();
}
}
@@ -0,0 +1,65 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when a minecraft version is being removed.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @author huangyuhui
*/
public class RenameVersionEvent extends Event {
private final String from, to;
/**
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
* @param from the version id.
*/
public RenameVersionEvent(Object source, String from, String to) {
super(source);
this.from = from;
this.to = to;
}
public String getFromVersion() {
return from;
}
public String getToVersion() {
return to;
}
@Override
public boolean hasResult() {
return true;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("from", from)
.append("to", to)
.toString();
}
}
@@ -40,7 +40,7 @@ public class ClassicVersion extends Version {
public ClassicLibrary(String name) {
super("", "", "", null, null,
new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null),
false, null, null, null);
false, null, null, null, null);
}
}
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.io.File;
import java.io.IOException;
@@ -116,6 +117,9 @@ public class DefaultGameRepository implements GameRepository {
@Override
public boolean renameVersion(String from, String to) {
if (EventBus.EVENT_BUS.fireEvent(new RenameVersionEvent(this, from, to)) == Event.Result.DENY)
return false;
try {
Version fromVersion = getVersion(from);
File fromDir = getVersionRoot(from);
@@ -145,6 +149,8 @@ public class DefaultGameRepository implements GameRepository {
public boolean removeVersionFromDisk(String id) {
if (!versions.containsKey(id))
return true;
if (EventBus.EVENT_BUS.fireEvent(new RemoveVersionEvent(this, id)) == Event.Result.DENY)
return false;
File file = getVersionRoot(id);
if (!file.exists())
return true;
@@ -201,7 +207,7 @@ public class DefaultGameRepository implements GameRepository {
try {
FileUtils.writeText(json, Constants.GSON.toJson(version));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Ignoring version {0} because wrong id {1} is set and cannot correct it.", new Object[]{id, version.getId()});
Logging.LOG.log(Level.WARNING, "Ignoring version " + id + " because wrong id " + version.getId() + " is set and cannot correct it.");
continue;
}
}
@@ -217,7 +223,7 @@ public class DefaultGameRepository implements GameRepository {
EventBus.EVENT_BUS.fireEvent(new LoadedOneVersionEvent(this, resolved)) != Event.Result.DENY)
versions.put(version.getId(), version);
} catch (VersionNotFoundException e) {
Logging.LOG.log(Level.WARNING, "Ignoring version {0} because it inherits from a nonexistent version.", version.getId());
Logging.LOG.log(Level.WARNING, "Ignoring version " + version.getId() + " because it inherits from a nonexistent version.");
}
}
@@ -227,7 +233,9 @@ public class DefaultGameRepository implements GameRepository {
@Override
public void refreshVersions() {
EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this));
if (EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this)) == Event.Result.DENY)
return;
Schedulers.newThread().schedule(() -> {
refreshVersionsImpl();
EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this));
@@ -337,4 +345,11 @@ public class DefaultGameRepository implements GameRepository {
return getModpackConfiguration(version).exists();
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("versions", versions == null ? null : versions.keySet())
.append("baseDirectory", baseDirectory)
.toString();
}
}
@@ -116,7 +116,7 @@ public final class GameVersion {
.getEntry("net/minecraft/client/Minecraft.class");
if (minecraft != null)
return getVersionOfOldMinecraft(f, minecraft);
ZipArchiveEntry main = f.getEntry("net/minecraft/client/main/Main.class");
ZipArchiveEntry main = f.getEntry("net/minecraft/client/main/Launcher.class");
ZipArchiveEntry minecraftServer = f.getEntry("net/minecraft/server/MinecraftServer.class");
if ((main != null) && (minecraftServer != null))
return getVersionOfNewMinecraft(f, minecraftServer);
@@ -48,6 +48,7 @@ public class Library implements Comparable<Library> {
private final boolean lateload;
private final Map<OperatingSystem, String> natives;
private final List<CompatibilityRule> rules;
private final List<String> checksums;
private final String path;
@@ -60,10 +61,10 @@ public class Library implements Comparable<Library> {
}
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload) {
this(groupId, artifactId, version, classifier, url, downloads, lateload, null, null, null);
this(groupId, artifactId, version, classifier, url, downloads, lateload, null, null, null, null);
}
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload, List<String> checksums, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
@@ -80,6 +81,7 @@ public class Library implements Comparable<Library> {
this.lateload = lateload;
this.natives = natives;
this.rules = rules;
this.checksums = checksums;
LibraryDownloadInfo temp = null;
if (downloads != null)
@@ -141,6 +143,14 @@ public class Library implements Comparable<Library> {
return lateload;
}
public List<String> getChecksums() {
return checksums;
}
public boolean is(String groupId, String artifactId) {
return this.groupId.equals(groupId) && this.artifactId.equals(artifactId);
}
@Override
public String toString() {
return new ToStringBuilder(this).append("name", getName()).toString();
@@ -168,16 +178,20 @@ public class Library implements Comparable<Library> {
return Objects.hash(getName(), isNative());
}
public static Library fromName(String name) {
return fromName(name, null, null, null, null, null);
public Library setClassifier(String classifier) {
return new Library(groupId, artifactId, version, classifier, url, downloads, lateload, checksums, extract, natives, rules);
}
public static Library fromName(String name, String url, LibrariesDownloadInfo downloads, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
public static Library fromName(String name) {
return fromName(name, null, null, null, null, null, null);
}
public static Library fromName(String name, String url, LibrariesDownloadInfo downloads, List<String> checksums, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
String[] arr = name.split(":", 4);
if (arr.length != 3 && arr.length != 4)
throw new IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version(:classifier).");
return new Library(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, url, downloads, false, extract, natives, rules);
return new Library(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, url, downloads, false, checksums, extract, natives, rules);
}
public static class Serializer implements JsonDeserializer<Library>, JsonSerializer<Library> {
@@ -198,6 +212,8 @@ public class Library implements Comparable<Library> {
jsonObject.get("name").getAsString(),
jsonObject.has("url") ? jsonObject.get("url").getAsString() : null,
context.deserialize(jsonObject.get("downloads"), LibrariesDownloadInfo.class),
context.deserialize(jsonObject.get("checksums"), new TypeToken<List<String>>() {
}.getType()),
context.deserialize(jsonObject.get("extract"), ExtractRules.class),
context.deserialize(jsonObject.get("natives"), new TypeToken<Map<OperatingSystem, String>>() {
}.getType()),
Loaded 100 of 113 files, more files were not shown because too many files have changed in this diff. Show more