Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db29f97397 | ||
|
|
573f41611d | ||
|
|
28ed103ec0 | ||
|
|
5e37750f00 | ||
|
|
5fe3cd9406 | ||
|
|
b00cc99408 | ||
|
|
9a66c3bba9 | ||
|
|
546921b445 | ||
|
|
8b698d3a4c | ||
|
|
c4608a949b | ||
|
|
bba1c559a3 | ||
|
|
bdee9ea72f | ||
|
|
520923ba08 | ||
|
|
936a74597f | ||
|
|
97132745cc | ||
|
|
2bb30f65f3 | ||
|
|
ec04d55793 | ||
|
|
88671db566 | ||
|
|
538f501bc0 | ||
|
|
3b75d41a2f | ||
|
|
110149d0e6 | ||
|
|
89b68781fa | ||
|
|
4cdc54b9c1 | ||
|
|
7673595a43 | ||
|
|
76a259c107 | ||
|
|
5f40d01fb4 | ||
|
|
df29d47c0a | ||
|
|
10d4084dde | ||
|
|
ba8cdb1b42 | ||
|
|
22732fee6a | ||
|
|
bfbc4613b9 | ||
|
|
92e5eecaf2 | ||
|
|
593159f4a6 | ||
|
|
53cad0e56a | ||
|
|
d61006dd67 | ||
|
|
eb565285b2 |
No files matched your search
@@ -152,7 +152,7 @@ public final class Launcher extends Application {
|
||||
public static final File HMCL_DIRECTORY = OperatingSystem.getWorkingDirectory("hmcl");
|
||||
public static final File LOG_DIRECTORY = new File(Launcher.HMCL_DIRECTORY, "logs");
|
||||
|
||||
public static final String VERSION = "@HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING@";
|
||||
public static final String VERSION = System.getProperty("hmcl.version.override", "@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();
|
||||
@@ -160,7 +160,7 @@ public final class Launcher extends Application {
|
||||
public static final IUpgrader UPGRADER = new AppDataUpgrader();
|
||||
public static final CrashReporter CRASH_REPORTER = new CrashReporter();
|
||||
|
||||
public static final String UPDATE_SERVER = "http://47.101.47.110";
|
||||
public static final String UPDATE_SERVER = "https://hmcl.huangyuhui.net";
|
||||
public static final String CONTACT = UPDATE_SERVER + "/hmcl.php";
|
||||
public static final String PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
|
||||
}
|
||||
@@ -27,6 +27,7 @@ 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.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
@@ -40,6 +41,7 @@ import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -76,7 +78,7 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
private void launch0(Profile profile, Account account, String selectedVersion, File scriptFile) {
|
||||
GameRepository repository = profile.getRepository();
|
||||
HMCLGameRepository repository = profile.getRepository();
|
||||
DefaultDependencyManager dependencyManager = profile.getDependency();
|
||||
Version version = MaintainTask.maintain(repository.getResolvedVersion(selectedVersion));
|
||||
VersionSetting setting = profile.getVersionSetting(selectedVersion);
|
||||
@@ -90,7 +92,17 @@ public final class LauncherHelper {
|
||||
return dependencyManager.checkGameCompletionAsync(version);
|
||||
})
|
||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
|
||||
.then(new CurseCompletionTask(dependencyManager, selectedVersion))
|
||||
.then(var -> {
|
||||
try {
|
||||
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
||||
if ("Curse".equals(configuration.getType()))
|
||||
return new CurseCompletionTask(dependencyManager, selectedVersion);
|
||||
else
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
|
||||
.then(Task.of(Launcher.i18n("account.methods"), variables -> {
|
||||
try {
|
||||
@@ -179,10 +191,12 @@ public final class LauncherHelper {
|
||||
private static void checkGameState(Profile profile, VersionSetting setting, Version version, Runnable onAccept) throws InterruptedException {
|
||||
boolean flag = false;
|
||||
|
||||
// Without onAccept called, the launching operation will be terminated.
|
||||
|
||||
VersionNumber gameVersion = VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse("Unknown"));
|
||||
JavaVersion java = setting.getJavaVersion();
|
||||
if (java == null) {
|
||||
Controllers.dialog(Launcher.i18n("launch.wrong_javadir"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
|
||||
Controllers.dialog(Launcher.i18n("launch.wrong_javadir"), Launcher.i18n("message.error"), MessageBox.WARNING_MESSAGE, onAccept);
|
||||
setting.setJava(null);
|
||||
setting.setDefaultJavaPath(null);
|
||||
java = JavaVersion.fromCurrentEnvironment();
|
||||
@@ -190,10 +204,19 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
if (!flag && java.getParsedVersion() < JavaVersion.JAVA_8) {
|
||||
Controllers.dialog(Launcher.i18n("launch.advice.newer_java"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
|
||||
if (gameVersion.compareTo(VersionNumber.asVersion("1.13")) >= 0) {
|
||||
// Minecraft 1.13 and later versions only support Java 8 or later.
|
||||
// Terminate launching operation.
|
||||
Controllers.dialog(Launcher.i18n("launch.advice.java8_1_13"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
|
||||
} else {
|
||||
// Most mods require Java 8 or later version.
|
||||
Controllers.dialog(Launcher.i18n("launch.advice.newer_java"), Launcher.i18n("message.error"), MessageBox.WARNING_MESSAGE, onAccept);
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
|
||||
// LaunchWrapper will crash because of assuming the system class loader is an instance of URLClassLoader.
|
||||
// cpw has claimed that he will make MinecraftForge of 1.13 and later versions able to run on Java 9.
|
||||
if (!flag && java.getParsedVersion() >= JavaVersion.JAVA_9 && gameVersion.compareTo(VersionNumber.asVersion("1.12.5")) < 0 && version.getMainClass().contains("launchwrapper")) {
|
||||
Controllers.dialog(Launcher.i18n("launch.advice.java9"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
|
||||
flag = true;
|
||||
@@ -206,6 +229,8 @@ public final class LauncherHelper {
|
||||
}
|
||||
if (!flag && java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
|
||||
setting.getMaxMemory() > 1.5 * 1024) {
|
||||
// 1.5 * 1024 is an inaccurate number.
|
||||
// Actual memory limit depends on operating system and memory.
|
||||
Controllers.dialog(Launcher.i18n("launch.advice.too_large_memory_for_32bit"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
|
||||
flag = true;
|
||||
}
|
||||
|
||||
@@ -59,12 +59,12 @@ public final class ModpackHelper {
|
||||
throw new UnsupportedModpackException(file.toString());
|
||||
}
|
||||
|
||||
public static <T> ModpackConfiguration<T> readModpackConfiguration(File file) throws IOException {
|
||||
public static ModpackConfiguration<?> readModpackConfiguration(File file) throws IOException {
|
||||
if (!file.exists())
|
||||
throw new FileNotFoundException(file.getPath());
|
||||
else
|
||||
try {
|
||||
return Constants.GSON.fromJson(FileUtils.readText(file), new TypeToken<ModpackConfiguration<T>>() {
|
||||
return Constants.GSON.fromJson(FileUtils.readText(file), new TypeToken<ModpackConfiguration<?>>() {
|
||||
}.getType());
|
||||
} catch (JsonParseException e) {
|
||||
throw new IOException("Malformed modpack configuration");
|
||||
|
||||
@@ -23,26 +23,23 @@ import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorBuildInfo;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerResponse;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.MojangYggdrasilProvider;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskResult;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
/**
|
||||
@@ -58,11 +55,9 @@ public final class Accounts {
|
||||
public static final Map<String, AccountFactory<?>> ACCOUNT_FACTORY = mapOf(
|
||||
pair(OFFLINE_ACCOUNT_KEY, OfflineAccountFactory.INSTANCE),
|
||||
pair(YGGDRASIL_ACCOUNT_KEY, new YggdrasilAccountFactory(MojangYggdrasilProvider.INSTANCE)),
|
||||
pair(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector))
|
||||
pair(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector, Accounts::getOrCreateAuthlibInjectorServer))
|
||||
);
|
||||
|
||||
private static final Map<String, String> AUTHLIB_INJECTOR_SERVER_NAMES = new HashMap<>();
|
||||
|
||||
public static String getAccountType(Account account) {
|
||||
if (account instanceof OfflineAccount) return OFFLINE_ACCOUNT_KEY;
|
||||
else if (account instanceof AuthlibInjectorAccount) return AUTHLIB_INJECTOR_ACCOUNT_KEY;
|
||||
@@ -94,17 +89,23 @@ public final class Accounts {
|
||||
return jar.getAbsolutePath();
|
||||
}
|
||||
|
||||
public static String getAuthlibInjectorServerName(String serverIp) throws Exception {
|
||||
if (AUTHLIB_INJECTOR_SERVER_NAMES.containsKey(serverIp))
|
||||
return AUTHLIB_INJECTOR_SERVER_NAMES.get(serverIp);
|
||||
else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
private static AuthlibInjectorServer getOrCreateAuthlibInjectorServer(String url) {
|
||||
return Settings.SETTINGS.authlibInjectorServers.stream()
|
||||
.filter(server -> url.equals(server.getUrl()))
|
||||
.findFirst()
|
||||
.orElseGet(() -> {
|
||||
// this usually happens when migrating data from an older version
|
||||
AuthlibInjectorServer server;
|
||||
try {
|
||||
server = AuthlibInjectorServer.fetchServerInfo(url);
|
||||
LOG.info("Migrated authlib injector server " + server);
|
||||
} catch (IOException e) {
|
||||
server = new AuthlibInjectorServer(url, url);
|
||||
LOG.log(Level.WARNING, "Failed to migrate authlib injector server " + url, e);
|
||||
}
|
||||
|
||||
public static TaskResult<String> getAuthlibInjectorServerNameAsync(AuthlibInjectorAccount account) {
|
||||
return Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(account.getServerBaseURL()));
|
||||
Settings.SETTINGS.authlibInjectorServers.add(server);
|
||||
return server;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,12 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
@@ -35,7 +36,6 @@ import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableMap;
|
||||
import javafx.collections.ObservableSet;
|
||||
|
||||
public final class Config implements Cloneable {
|
||||
|
||||
@@ -99,8 +99,7 @@ public final class Config implements Cloneable {
|
||||
@SerializedName("logLines")
|
||||
public final IntegerProperty logLines = new SimpleIntegerProperty(100);
|
||||
|
||||
@SerializedName("authlibInjectorServerURLs")
|
||||
public final ObservableSet<String> authlibInjectorServerURLs = FXCollections.observableSet(new HashSet<>());
|
||||
public final ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList();
|
||||
|
||||
@Override
|
||||
public Config clone() {
|
||||
|
||||
@@ -20,14 +20,18 @@ package org.jackhuang.hmcl.setting;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
|
||||
public final class Profiles {
|
||||
|
||||
public static final String DEFAULT_PROFILE = "Default";
|
||||
public static final String HOME_PROFILE = "Home";
|
||||
|
||||
private Profiles() {
|
||||
}
|
||||
|
||||
public static String getProfileDisplayName(Profile profile) {
|
||||
switch (profile.getName()) {
|
||||
case Settings.DEFAULT_PROFILE:
|
||||
case Profiles.DEFAULT_PROFILE:
|
||||
return Launcher.i18n("profile.default");
|
||||
case Settings.HOME_PROFILE:
|
||||
case Profiles.HOME_PROFILE:
|
||||
return Launcher.i18n("profile.home");
|
||||
default:
|
||||
return profile.getName();
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
@@ -35,6 +36,7 @@ import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.event.*;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
@@ -51,7 +53,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
public class Settings {
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
@@ -65,15 +70,12 @@ public class Settings {
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
public static final String DEFAULT_PROFILE = "Default";
|
||||
public static final String HOME_PROFILE = "Home";
|
||||
|
||||
public static final String SETTINGS_FILE_NAME = "hmcl.json";
|
||||
public static final File SETTINGS_FILE = new File(SETTINGS_FILE_NAME).getAbsoluteFile();
|
||||
|
||||
public static final Settings INSTANCE = new Settings();
|
||||
public static final Config SETTINGS = initSettings();
|
||||
|
||||
private final Config SETTINGS = initSettings();
|
||||
public static final Settings INSTANCE = new Settings();
|
||||
|
||||
private final Map<String, Account> accounts = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -84,7 +86,7 @@ public class Settings {
|
||||
Map<Object, Object> settings = iterator.next();
|
||||
AccountFactory<?> factory = Accounts.ACCOUNT_FACTORY.get(tryCast(settings.get("type"), String.class).orElse(""));
|
||||
if (factory == null) {
|
||||
// unrecognized account type, so remove it.
|
||||
LOG.warning("Unrecognized account type, removing: " + settings);
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
@@ -93,7 +95,7 @@ public class Settings {
|
||||
try {
|
||||
account = factory.fromStorage(settings, getProxy());
|
||||
} catch (Exception e) {
|
||||
// storage is malformed, delete.
|
||||
LOG.log(Level.WARNING, "Malformed account storage, removing: " + settings, e);
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
@@ -101,7 +103,8 @@ public class Settings {
|
||||
accounts.put(Accounts.getAccountId(account), account);
|
||||
}
|
||||
|
||||
checkAuthlibInjectorAccounts();
|
||||
SETTINGS.authlibInjectorServers.addListener(onInvalidating(this::removeDanglingAuthlibInjectorAccounts));
|
||||
|
||||
checkProfileMap();
|
||||
|
||||
save();
|
||||
@@ -115,7 +118,7 @@ public class Settings {
|
||||
Lang.ignoringException(() -> Runtime.getRuntime().addShutdownHook(new Thread(this::save)));
|
||||
}
|
||||
|
||||
private Config initSettings() {
|
||||
private static Config initSettings() {
|
||||
Config c = new Config();
|
||||
if (SETTINGS_FILE.exists())
|
||||
try {
|
||||
@@ -305,30 +308,17 @@ public class Settings {
|
||||
* AUTHLIB INJECTORS *
|
||||
****************************************/
|
||||
|
||||
public Set<String> getAuthlibInjectorServerURLs() {
|
||||
return SETTINGS.authlibInjectorServerURLs;
|
||||
}
|
||||
|
||||
public void removeAuthlibInjectorServerURL(String serverURL) {
|
||||
SETTINGS.authlibInjectorServerURLs.remove(serverURL);
|
||||
|
||||
checkAuthlibInjectorAccounts();
|
||||
save();
|
||||
}
|
||||
|
||||
public void addAuthlibInjectorServerURL(String serverURL) {
|
||||
SETTINGS.authlibInjectorServerURLs.add(serverURL);
|
||||
save();
|
||||
}
|
||||
|
||||
private void checkAuthlibInjectorAccounts() {
|
||||
for (Account account : getAccounts()) {
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
AuthlibInjectorAccount injectorAccount = (AuthlibInjectorAccount) account;
|
||||
if (!SETTINGS.authlibInjectorServerURLs.contains(injectorAccount.getServerBaseURL()))
|
||||
deleteAccount(account);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* After an {@link AuthlibInjectorServer} is removed, the associated accounts should also be removed.
|
||||
* This method performs a check and removes the dangling accounts.
|
||||
* Don't call this before {@link #migrateAuthlibInjectorServers()} is called, otherwise old data would be lost.
|
||||
*/
|
||||
private void removeDanglingAuthlibInjectorAccounts() {
|
||||
accounts.values().stream()
|
||||
.filter(AuthlibInjectorAccount.class::isInstance)
|
||||
.filter(it -> !SETTINGS.authlibInjectorServers.contains(((AuthlibInjectorAccount) it).getServer()))
|
||||
.collect(toList())
|
||||
.forEach(this::deleteAccount);
|
||||
}
|
||||
|
||||
/****************************************
|
||||
@@ -561,8 +551,8 @@ 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, Launcher.MINECRAFT_DIRECTORY));
|
||||
getProfileMap().put(Profiles.DEFAULT_PROFILE, new Profile(Profiles.DEFAULT_PROFILE));
|
||||
getProfileMap().put(Profiles.HOME_PROFILE, new Profile(Profiles.HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.game.AccountHelper;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
@@ -80,8 +79,7 @@ public class AccountPage extends StackPane implements DecoratorPage {
|
||||
|
||||
FXUtils.setLimitWidth(this, 300);
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
Accounts.getAuthlibInjectorServerNameAsync((AuthlibInjectorAccount) account)
|
||||
.subscribe(Schedulers.javafx(), variables -> lblServer.setText(variables.get("serverName")));
|
||||
lblServer.setText(((AuthlibInjectorAccount) account).getServer().getName());
|
||||
FXUtils.setLimitHeight(this, 182);
|
||||
} else {
|
||||
componentList.removeChildren(paneServer);
|
||||
|
||||
@@ -33,6 +33,7 @@ import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.*;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
@@ -45,17 +46,19 @@ import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jackhuang.hmcl.ui.construct.IconedItem;
|
||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
import java.util.Collection;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class AddAccountPane extends StackPane {
|
||||
|
||||
@@ -64,25 +67,26 @@ public class AddAccountPane extends StackPane {
|
||||
@FXML private Label lblCreationWarning;
|
||||
@FXML private Label lblPassword;
|
||||
@FXML private JFXComboBox<String> cboType;
|
||||
@FXML private JFXComboBox<TwoLineListItem> cboServers;
|
||||
@FXML private JFXComboBox<AuthlibInjectorServer> cboServers;
|
||||
@FXML private Label lblInjectorServer;
|
||||
@FXML private Hyperlink linkManageInjectorServers;
|
||||
@FXML private JFXDialogLayout layout;
|
||||
@FXML private JFXButton btnAccept;
|
||||
@FXML private StackPane acceptPane;
|
||||
@FXML private JFXSpinner spinnerAccept;
|
||||
@FXML private SpinnerPane acceptPane;
|
||||
private final Consumer<Region> finalization;
|
||||
private final TransitionHandler transitionHandler;
|
||||
|
||||
public AddAccountPane(Consumer<Region> finalization) {
|
||||
this.finalization = finalization;
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/account-add.fxml");
|
||||
|
||||
transitionHandler = new TransitionHandler(acceptPane);
|
||||
acceptPane.getChildren().setAll(btnAccept);
|
||||
cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl())));
|
||||
cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName));
|
||||
cboServers.setItems(Settings.SETTINGS.authlibInjectorServers);
|
||||
|
||||
loadServers();
|
||||
// workaround: otherwise the combox will be black
|
||||
if (!cboServers.getItems().isEmpty())
|
||||
cboServers.getSelectionModel().select(0);
|
||||
|
||||
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) -> {
|
||||
@@ -95,10 +99,6 @@ public class AddAccountPane extends StackPane {
|
||||
});
|
||||
cboType.getSelectionModel().select(0);
|
||||
|
||||
// These two lines can eliminate black, don't know why.
|
||||
cboServers.getItems().setAll(new TwoLineListItem("", ""));
|
||||
cboServers.getSelectionModel().select(0);
|
||||
|
||||
txtPassword.setOnAction(e -> onCreationAccept());
|
||||
txtUsername.setOnAction(e -> onCreationAccept());
|
||||
txtUsername.getValidators().add(new Validator(Launcher.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
|
||||
@@ -111,62 +111,53 @@ public class AddAccountPane extends StackPane {
|
||||
btnAccept.setDisable(!txtUsername.validate() || (cboType.getSelectionModel().getSelectedIndex() != 0 && !txtPassword.validate()));
|
||||
}
|
||||
|
||||
private void loadServers() {
|
||||
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
|
||||
.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"));
|
||||
if (!cboServers.getItems().isEmpty())
|
||||
cboServers.getSelectionModel().select(0);
|
||||
}));
|
||||
}
|
||||
|
||||
private void showSpinner() {
|
||||
transitionHandler.setContent(spinnerAccept, ContainerAnimations.FADE.getAnimationProducer());
|
||||
}
|
||||
|
||||
private void hideSpinner() {
|
||||
transitionHandler.setContent(btnAccept, ContainerAnimations.FADE.getAnimationProducer());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onCreationAccept() {
|
||||
int type = cboType.getSelectionModel().getSelectedIndex();
|
||||
String username = txtUsername.getText();
|
||||
String password = txtPassword.getText();
|
||||
String apiRoot = Optional.ofNullable(cboServers.getSelectionModel().getSelectedItem()).map(TwoLineListItem::getSubtitle).orElse(null);
|
||||
showSpinner();
|
||||
lblCreationWarning.setText("");
|
||||
Task.ofResult("create_account", () -> {
|
||||
AccountFactory<?> factory;
|
||||
switch (type) {
|
||||
case 0: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY); break;
|
||||
case 1: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY); break;
|
||||
case 2: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY); break;
|
||||
default: throw new Error();
|
||||
}
|
||||
Object addtionalData;
|
||||
|
||||
return factory.create(new Selector(), username, password, apiRoot, Settings.INSTANCE.getProxy());
|
||||
}).finalized(Schedulers.javafx(), variables -> {
|
||||
Settings.INSTANCE.addAccount(variables.get("create_account"));
|
||||
hideSpinner();
|
||||
finalization.accept(this);
|
||||
}, exception -> {
|
||||
if (exception instanceof NoSelectedCharacterException) {
|
||||
finalization.accept(this);
|
||||
} else {
|
||||
lblCreationWarning.setText(accountException(exception));
|
||||
}
|
||||
hideSpinner();
|
||||
}).start();
|
||||
int type = cboType.getSelectionModel().getSelectedIndex();
|
||||
AccountFactory<?> factory;
|
||||
switch (type) {
|
||||
case 0:
|
||||
factory = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY);
|
||||
addtionalData = null;
|
||||
break;
|
||||
case 1:
|
||||
factory = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY);
|
||||
addtionalData = null;
|
||||
break;
|
||||
case 2:
|
||||
factory = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY);
|
||||
Optional<AuthlibInjectorServer> server = Optional.ofNullable(cboServers.getSelectionModel().getSelectedItem());
|
||||
if (server.isPresent()) {
|
||||
addtionalData = server.get();
|
||||
} else {
|
||||
lblCreationWarning.setText(Launcher.i18n("account.failed.no_selected_server"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
acceptPane.showSpinner();
|
||||
lblCreationWarning.setText("");
|
||||
|
||||
Task.ofResult("create_account", () -> factory.create(new Selector(), username, password, addtionalData, Settings.INSTANCE.getProxy()))
|
||||
.finalized(Schedulers.javafx(), variables -> {
|
||||
Settings.INSTANCE.addAccount(variables.get("create_account"));
|
||||
acceptPane.hideSpinner();
|
||||
finalization.accept(this);
|
||||
}, exception -> {
|
||||
if (exception instanceof NoSelectedCharacterException) {
|
||||
finalization.accept(this);
|
||||
} else {
|
||||
lblCreationWarning.setText(accountException(exception));
|
||||
}
|
||||
acceptPane.hideSpinner();
|
||||
}).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -224,13 +215,13 @@ public class AddAccountPane extends StackPane {
|
||||
image = AccountHelper.getSkinImmediately(yggdrasilAccount, profile, 4, Settings.INSTANCE.getProxy());
|
||||
} catch (Exception e) {
|
||||
Logging.LOG.log(Level.WARNING, "Failed to get skin for " + profile.getName(), e);
|
||||
image = FXUtils.DEFAULT_ICON;
|
||||
image = null;
|
||||
}
|
||||
ImageView portraitView = new ImageView();
|
||||
portraitView.setSmooth(false);
|
||||
if (image == FXUtils.DEFAULT_ICON)
|
||||
portraitView.setImage(FXUtils.DEFAULT_ICON);
|
||||
else {
|
||||
if (image == null) {
|
||||
portraitView.setImage(Constants.DEFAULT_ICON.get());
|
||||
} else {
|
||||
portraitView.setImage(image);
|
||||
portraitView.setViewport(AccountHelper.getViewport(4));
|
||||
}
|
||||
|
||||
@@ -23,19 +23,19 @@ import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class AuthlibInjectorServerItem extends BorderPane {
|
||||
private final AuthlibInjectorServerInfo info;
|
||||
private final AuthlibInjectorServer server;
|
||||
|
||||
private final Label lblServerName = new Label();
|
||||
private final Label lblServerIp = new Label();
|
||||
|
||||
public AuthlibInjectorServerItem(AuthlibInjectorServerInfo info, Consumer<AuthlibInjectorServerItem> deleteCallback) {
|
||||
this.info = info;
|
||||
public AuthlibInjectorServerItem(AuthlibInjectorServer server, Consumer<AuthlibInjectorServerItem> deleteCallback) {
|
||||
this.server = server;
|
||||
|
||||
lblServerName.setStyle("-fx-font-size: 15;");
|
||||
lblServerIp.setStyle("-fx-font-size: 10;");
|
||||
@@ -54,11 +54,11 @@ public final class AuthlibInjectorServerItem extends BorderPane {
|
||||
|
||||
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
|
||||
JFXDepthManager.setDepth(this, 1);
|
||||
lblServerName.setText(info.getServerName());
|
||||
lblServerIp.setText(info.getServerIp());
|
||||
lblServerName.setText(server.getName());
|
||||
lblServerIp.setText(server.getUrl());
|
||||
}
|
||||
|
||||
public AuthlibInjectorServerInfo getInfo() {
|
||||
return info;
|
||||
public AuthlibInjectorServer getServer() {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
@@ -4,49 +4,47 @@ import com.jfoenix.controls.*;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
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.construct.SpinnerPane;
|
||||
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.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("account.injector.server"));
|
||||
|
||||
@FXML private ScrollPane scrollPane;
|
||||
@FXML private StackPane addServerContainer;
|
||||
@FXML private Label lblServerIp;
|
||||
@FXML private Label lblServerUrl;
|
||||
@FXML private Label lblServerName;
|
||||
@FXML private Label lblCreationWarning;
|
||||
@FXML private Label lblServerWarning;
|
||||
@FXML private VBox listPane;
|
||||
@FXML private JFXTextField txtServerIp;
|
||||
@FXML private JFXTextField txtServerUrl;
|
||||
@FXML private JFXDialogLayout addServerPane;
|
||||
@FXML private JFXDialogLayout confirmServerPane;
|
||||
@FXML private JFXDialog dialog;
|
||||
@FXML private StackPane contentPane;
|
||||
@FXML private JFXSpinner spinner;
|
||||
@FXML private JFXProgressBar progressBar;
|
||||
@FXML private SpinnerPane nextPane;
|
||||
@FXML private JFXButton btnAddNext;
|
||||
|
||||
private final TransitionHandler transitionHandler;
|
||||
|
||||
private AuthlibInjectorServer serverBeingAdded;
|
||||
|
||||
{
|
||||
FXUtils.loadFXML(this, "/assets/fxml/authlib-injector-servers.fxml");
|
||||
FXUtils.smoothScrolling(scrollPane);
|
||||
@@ -55,51 +53,35 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
|
||||
getChildren().remove(dialog);
|
||||
dialog.setDialogContainer(this);
|
||||
|
||||
txtServerIp.textProperty().addListener((a, b, newValue) ->
|
||||
btnAddNext.setDisable(!txtServerIp.validate()));
|
||||
txtServerUrl.textProperty().addListener((a, b, newValue) ->
|
||||
btnAddNext.setDisable(!txtServerUrl.validate()));
|
||||
|
||||
loading();
|
||||
reload();
|
||||
}
|
||||
|
||||
private void removeServer(AuthlibInjectorServerItem item) {
|
||||
Settings.INSTANCE.removeAuthlibInjectorServerURL(item.getInfo().getServerIp());
|
||||
loading();
|
||||
Settings.SETTINGS.authlibInjectorServers.remove(item.getServer());
|
||||
reload();
|
||||
}
|
||||
|
||||
private void loading() {
|
||||
getChildren().remove(contentPane);
|
||||
spinner.setVisible(true);
|
||||
|
||||
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
|
||||
.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"));
|
||||
loadingCompleted();
|
||||
}));
|
||||
}
|
||||
|
||||
private void loadingCompleted() {
|
||||
getChildren().add(contentPane);
|
||||
spinner.setVisible(false);
|
||||
|
||||
if (Settings.INSTANCE.getAuthlibInjectorServerURLs().isEmpty())
|
||||
private void reload() {
|
||||
listPane.getChildren().setAll(
|
||||
Settings.SETTINGS.authlibInjectorServers.stream()
|
||||
.map(server -> new AuthlibInjectorServerItem(server, this::removeServer))
|
||||
.collect(toList()));
|
||||
if (Settings.SETTINGS.authlibInjectorServers.isEmpty()) {
|
||||
onAdd();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAdd() {
|
||||
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
||||
txtServerIp.setText("");
|
||||
txtServerUrl.setText("");
|
||||
txtServerUrl.resetValidation();
|
||||
lblCreationWarning.setText("");
|
||||
addServerPane.setDisable(false);
|
||||
progressBar.setVisible(false);
|
||||
nextPane.hideSpinner();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@@ -110,26 +92,28 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
|
||||
|
||||
@FXML
|
||||
private void onAddNext() {
|
||||
String serverIp = txtServerIp.getText();
|
||||
progressBar.setVisible(true);
|
||||
String url = fixInputUrl(txtServerUrl.getText());
|
||||
|
||||
nextPane.showSpinner();
|
||||
addServerPane.setDisable(true);
|
||||
|
||||
Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(serverIp))
|
||||
.finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||
progressBar.setVisible(false);
|
||||
addServerPane.setDisable(false);
|
||||
Task.of(() -> {
|
||||
serverBeingAdded = AuthlibInjectorServer.fetchServerInfo(url);
|
||||
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||
nextPane.hideSpinner();
|
||||
addServerPane.setDisable(false);
|
||||
|
||||
if (isDependentsSucceeded) {
|
||||
lblServerName.setText(variables.get("serverName"));
|
||||
lblServerIp.setText(txtServerIp.getText());
|
||||
if (isDependentsSucceeded) {
|
||||
lblServerName.setText(serverBeingAdded.getName());
|
||||
lblServerUrl.setText(serverBeingAdded.getUrl());
|
||||
|
||||
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverIp).getProtocol()));
|
||||
|
||||
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
|
||||
} else
|
||||
lblCreationWarning.setText(variables.<Exception>get("lastException").getLocalizedMessage());
|
||||
}).start();
|
||||
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverBeingAdded.getUrl()).getProtocol()));
|
||||
|
||||
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
|
||||
} else {
|
||||
lblCreationWarning.setText(resolveFetchExceptionMessage(variables.<Exception>get("lastException")));
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
@@ -140,11 +124,10 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
|
||||
|
||||
@FXML
|
||||
private void onAddFinish() {
|
||||
String ip = txtServerIp.getText();
|
||||
if (!ip.endsWith("/"))
|
||||
ip += "/";
|
||||
Settings.INSTANCE.addAuthlibInjectorServerURL(ip);
|
||||
loading();
|
||||
if (!Settings.INSTANCE.SETTINGS.authlibInjectorServers.contains(serverBeingAdded)) {
|
||||
Settings.INSTANCE.SETTINGS.authlibInjectorServers.add(serverBeingAdded);
|
||||
}
|
||||
reload();
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
@@ -160,4 +143,19 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
private String fixInputUrl(String url) {
|
||||
if (!url.endsWith("/")) {
|
||||
url += "/";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private String resolveFetchExceptionMessage(Throwable exception) {
|
||||
if (exception instanceof IOException) {
|
||||
return Launcher.i18n("account.failed.connect_injector_server");
|
||||
} else {
|
||||
return exception.getClass() + ": " + exception.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
@@ -30,18 +31,22 @@ import javafx.beans.value.WeakChangeListener;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.ScrollBar;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.input.ScrollEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.util.Callback;
|
||||
import javafx.util.Duration;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
@@ -52,6 +57,7 @@ import java.net.URI;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@@ -68,6 +74,10 @@ public final class FXUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static InvalidationListener onInvalidating(Runnable action) {
|
||||
return arg -> action.run();
|
||||
}
|
||||
|
||||
public static <T> void onChange(ObservableValue<T> value, Consumer<T> consumer) {
|
||||
value.addListener((a, b, c) -> consumer.accept(c));
|
||||
}
|
||||
@@ -402,7 +412,32 @@ public final class FXUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static final Image DEFAULT_ICON = new Image("/assets/img/icon.png");
|
||||
public static <T> StringConverter<T> stringConverter(Function<T, String> func) {
|
||||
return new StringConverter<T>() {
|
||||
|
||||
@Override
|
||||
public String toString(T object) {
|
||||
return object == null ? "" : func.apply(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromString(String string) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Callback<ListView<T>, ListCell<T>> jfxListCellFactory(Function<T, Node> graphicBuilder) {
|
||||
return view -> new JFXListCell<T>() {
|
||||
@Override
|
||||
public void updateItem(T item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (!empty) {
|
||||
setGraphic(graphicBuilder.apply(item));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final Interpolator SINE = new Interpolator() {
|
||||
@Override
|
||||
|
||||
@@ -186,9 +186,9 @@ public final class LeftPaneController {
|
||||
} else
|
||||
item.setImage(AccountHelper.getDefaultSkin(account.getUUID(), 4), AccountHelper.getViewport(4));
|
||||
|
||||
if (account instanceof AuthlibInjectorAccount)
|
||||
Accounts.getAuthlibInjectorServerNameAsync((AuthlibInjectorAccount) account)
|
||||
.subscribe(Schedulers.javafx(), variables -> FXUtils.installTooltip(ripplerContainer, 500, 5000, 0, new Tooltip(variables.get("serverName"))));
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
FXUtils.installTooltip(ripplerContainer, 500, 5000, 0, new Tooltip(((AuthlibInjectorAccount) account).getServer().getName()));
|
||||
}
|
||||
|
||||
if (selectedAccount == account)
|
||||
ripplerContainer.setSelected(true);
|
||||
|
||||
@@ -264,7 +264,7 @@ public final class VersionSettingsController {
|
||||
if (iconFile.exists())
|
||||
iconPickerItem.setImage(new Image("file:" + iconFile.getAbsolutePath()));
|
||||
else
|
||||
iconPickerItem.setImage(FXUtils.DEFAULT_ICON);
|
||||
iconPickerItem.setImage(Constants.DEFAULT_ICON.get());
|
||||
FXUtils.limitSize(iconPickerItem.getImageView(), 32, 32);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ public final class TransitionHandler implements AnimationHandler {
|
||||
} else
|
||||
previousNode = NULL;
|
||||
|
||||
if (previousNode == currentNode)
|
||||
if (previousNode == newView)
|
||||
previousNode = NULL;
|
||||
|
||||
currentNode = newView;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXSpinner;
|
||||
import javafx.beans.DefaultProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||
|
||||
@DefaultProperty("content")
|
||||
public class SpinnerPane extends StackPane {
|
||||
private final TransitionHandler transitionHandler = new TransitionHandler(this);
|
||||
private final JFXSpinner spinner = new JFXSpinner();
|
||||
private final StackPane contentPane = new StackPane();
|
||||
private final ObjectProperty<Node> content = new SimpleObjectProperty<>(this, "content");
|
||||
|
||||
public SpinnerPane() {
|
||||
getChildren().setAll(contentPane);
|
||||
|
||||
content.addListener((a, b, newValue) -> contentPane.getChildren().setAll(newValue));
|
||||
}
|
||||
|
||||
public void showSpinner() {
|
||||
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
|
||||
}
|
||||
|
||||
public void hideSpinner() {
|
||||
transitionHandler.setContent(contentPane, ContainerAnimations.FADE.getAnimationProducer());
|
||||
}
|
||||
|
||||
public Node getContent() {
|
||||
return content.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Node> contentProperty() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(Node content) {
|
||||
this.content.set(content);
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public final class DownloadWizardProvider implements WizardProvider {
|
||||
return null;
|
||||
|
||||
File selected = tryCast(settings.get(ModpackPage.MODPACK_FILE), File.class).orElse(null);
|
||||
Modpack modpack = tryCast(settings.get(ModpackPage.MODPACK_CURSEFORGE_MANIFEST), Modpack.class).orElse(null);
|
||||
Modpack modpack = tryCast(settings.get(ModpackPage.MODPACK_MANIFEST), Modpack.class).orElse(null);
|
||||
String name = tryCast(settings.get(ModpackPage.MODPACK_NAME), String.class).orElse(null);
|
||||
if (selected == null || modpack == null || name == null) return null;
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
|
||||
|
||||
try {
|
||||
manifest = ModpackHelper.readModpackManifest(selectedFile);
|
||||
controller.getSettings().put(MODPACK_CURSEFORGE_MANIFEST, manifest);
|
||||
controller.getSettings().put(MODPACK_MANIFEST, manifest);
|
||||
lblName.setText(manifest.getName());
|
||||
lblVersion.setText(manifest.getVersion());
|
||||
lblAuthor.setText(manifest.getAuthor());
|
||||
@@ -130,5 +130,5 @@ public final class ModpackPage extends StackPane implements WizardPage {
|
||||
|
||||
public static final String MODPACK_FILE = "MODPACK_FILE";
|
||||
public static final String MODPACK_NAME = "MODPACK_NAME";
|
||||
public static final String MODPACK_CURSEFORGE_MANIFEST = "CURSEFORGE_MANIFEST";
|
||||
public static final String MODPACK_MANIFEST = "MODPACK_MANIFEST";
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -49,7 +48,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class AppDataUpgrader extends IUpgrader {
|
||||
|
||||
private void launchNewerVersion(List<String> args, File jar) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InvocationTargetException, IllegalAccessException {
|
||||
private void launchNewerVersion(List<String> args, File jar) throws IOException, ReflectiveOperationException {
|
||||
try (JarFile jarFile = new JarFile(jar)) {
|
||||
String mainClass = jarFile.getManifest().getMainAttributes().getValue("Main-Class");
|
||||
if (mainClass == null)
|
||||
@@ -91,7 +90,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
}
|
||||
} catch (JsonParseException ex) {
|
||||
f.delete();
|
||||
} catch (IOException | NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException | ClassNotFoundException t) {
|
||||
} catch (IOException | ReflectiveOperationException t) {
|
||||
Logging.LOG.log(Level.SEVERE, "Unable to execute newer version application", t);
|
||||
AppDataUpgraderPackGzTask.HMCL_VER_FILE.delete(); // delete version json, let HMCL re-download the newer version.
|
||||
}
|
||||
|
||||
@@ -27,10 +27,11 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskResult;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.VersionNumber;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -78,8 +79,10 @@ public final class UpdateChecker {
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (Launcher.VERSION.contains("@"))
|
||||
if (isDevelopmentVersion(Launcher.VERSION)) {
|
||||
LOG.info("Current version is a development version, skip updating");
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
versionString = http.getResult();
|
||||
@@ -87,7 +90,7 @@ public final class UpdateChecker {
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
Logging.LOG.warning("Unable to check update...");
|
||||
LOG.warning("Unable to check update...");
|
||||
if (showMessage)
|
||||
MessageBox.show(Launcher.i18n("update.failed"));
|
||||
} else if (base.compareTo(value) < 0)
|
||||
@@ -103,6 +106,11 @@ public final class UpdateChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isDevelopmentVersion(String version) {
|
||||
return version.contains("@") || // eg. @HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING@
|
||||
version.contains("SNAPSHOT"); // eg. 3.1.SNAPSHOT
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <b>cached</b> newest version number, use "process" method to
|
||||
* download!
|
||||
@@ -124,12 +132,13 @@ public final class UpdateChecker {
|
||||
return new TaskResult<Map<String, String>>() {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (download_link == null)
|
||||
if (download_link == null) {
|
||||
try {
|
||||
download_link = Constants.GSON.<Map<String, String>>fromJson(NetworkUtils.doGet(NetworkUtils.toURL(Launcher.UPDATE_SERVER + "/hmcl/update_link.php")), Map.class);
|
||||
} catch (JsonSyntaxException | IOException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to get update link.", e);
|
||||
LOG.log(Level.SEVERE, "Failed to get update link.", e);
|
||||
}
|
||||
}
|
||||
setResult(download_link);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,16 @@ import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.ui.CrashWindow;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
|
||||
import static java.util.Collections.newSetFromMap;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
@@ -34,7 +39,7 @@ import java.util.logging.Level;
|
||||
*/
|
||||
public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private static final HashMap<String, String> SOURCE = new HashMap<String, String>() {
|
||||
private static final Map<String, String> SOURCE = new HashMap<String, String>() {
|
||||
{
|
||||
put("javafx.fxml.LoadException", Launcher.i18n("crash.NoClassDefFound"));
|
||||
put("Location is not set", Launcher.i18n("crash.NoClassDefFound"));
|
||||
@@ -59,11 +64,11 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
if (s.contains(entry.getKey())) {
|
||||
if (StringUtils.isNotBlank(entry.getValue())) {
|
||||
String info = entry.getValue();
|
||||
Logging.LOG.severe(info);
|
||||
LOG.severe(info);
|
||||
try {
|
||||
MessageBox.show(info);
|
||||
} catch (Throwable t) {
|
||||
Logging.LOG.log(Level.SEVERE, "Unable to show message", t);
|
||||
LOG.log(Level.SEVERE, "Unable to show message", t);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -71,17 +76,21 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Set<String> CAUGHT_EXCEPTIONS = newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
String stackTrace = StringUtils.getStackTrace(e);
|
||||
if (!stackTrace.contains("org.jackhuang"))
|
||||
return;
|
||||
|
||||
if (THROWABLE_SET.contains(stackTrace))
|
||||
return;
|
||||
THROWABLE_SET.add(stackTrace);
|
||||
LOG.log(Level.SEVERE, "Uncaught exception in thread " + t.getName(), e);
|
||||
|
||||
try {
|
||||
String stackTrace = StringUtils.getStackTrace(e);
|
||||
if (!stackTrace.contains("org.jackhuang"))
|
||||
return;
|
||||
|
||||
if (CAUGHT_EXCEPTIONS.contains(stackTrace))
|
||||
return;
|
||||
CAUGHT_EXCEPTIONS.add(stackTrace);
|
||||
|
||||
String text = "---- Hello Minecraft! Crash Report ----\n" +
|
||||
" Version: " + Launcher.VERSION + "\n" +
|
||||
" Time: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\n" +
|
||||
@@ -93,21 +102,18 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
" Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor") + "\n" +
|
||||
" Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor") + "\n";
|
||||
|
||||
Logging.LOG.log(Level.SEVERE, text);
|
||||
LOG.log(Level.SEVERE, text);
|
||||
|
||||
if (checkThrowable(e)) {
|
||||
Platform.runLater(() -> new CrashWindow(text).show());
|
||||
if (!Launcher.UPDATE_CHECKER.isOutOfDate())
|
||||
reportToServer(text);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Unable to caught exception", ex);
|
||||
Logging.LOG.log(Level.SEVERE, "There is the original exception", e);
|
||||
} catch (Throwable handlingException) {
|
||||
LOG.log(Level.SEVERE, "Unable to handle uncaught exception", handlingException);
|
||||
}
|
||||
}
|
||||
|
||||
private static final HashSet<String> THROWABLE_SET = new HashSet<>();
|
||||
|
||||
private void reportToServer(final String text) {
|
||||
Thread t = new Thread(() -> {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
@@ -115,11 +121,11 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
map.put("version", Launcher.VERSION);
|
||||
map.put("log", Logging.getLogs());
|
||||
try {
|
||||
String response = NetworkUtils.doPost(NetworkUtils.toURL("https://huangyuhui.duapp.com/hmcl/crash.php"), map);
|
||||
String response = NetworkUtils.doPost(NetworkUtils.toURL("https://hmcl.huangyuhui.net/hmcl/crash.php"), map);
|
||||
if (StringUtils.isNotBlank(response))
|
||||
Logging.LOG.log(Level.SEVERE, "Crash server response: " + response);
|
||||
LOG.log(Level.SEVERE, "Crash server response: " + response);
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Unable to post HMCL server.", ex);
|
||||
LOG.log(Level.SEVERE, "Unable to post HMCL server.", ex);
|
||||
}
|
||||
});
|
||||
t.setDaemon(true);
|
||||
|
||||
@@ -919,6 +919,14 @@
|
||||
-fx-stroke-width: 3.0;
|
||||
}
|
||||
|
||||
.small-spinner-pane .jfx-spinner {
|
||||
-jfx-radius: 10;
|
||||
}
|
||||
|
||||
.small-spinner-pane .jfx-spinner > .arc {
|
||||
-fx-stroke-width: 3.0;
|
||||
}
|
||||
|
||||
.second-spinner {
|
||||
-jfx-radius: 30;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.FXUtils?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
||||
<fx:root xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
type="StackPane">
|
||||
@@ -56,10 +57,9 @@
|
||||
</body>
|
||||
<actions>
|
||||
<Label fx:id="lblCreationWarning"/>
|
||||
<StackPane fx:id="acceptPane">
|
||||
<SpinnerPane fx:id="acceptPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAccept" onMouseClicked="#onCreationAccept" text="%button.ok" styleClass="dialog-accept"/>
|
||||
<JFXSpinner fx:id="spinnerAccept" styleClass="small-spinner" />
|
||||
</StackPane>
|
||||
</SpinnerPane>
|
||||
<JFXButton onMouseClicked="#onCreationCancel" text="%button.cancel" styleClass="dialog-cancel"/>
|
||||
</actions>
|
||||
</JFXDialogLayout>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.URLValidator?>
|
||||
<?import java.lang.String?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
<JFXSpinner fx:id="spinner" style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
|
||||
<StackPane fx:id="contentPane">
|
||||
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
|
||||
<VBox fx:id="listPane" spacing="10" style="-fx-padding: 20 20 70 20;">
|
||||
@@ -32,11 +32,9 @@
|
||||
<Label text="%account.injector.add" />
|
||||
</heading>
|
||||
<body>
|
||||
<JFXTextField fx:id="txtServerIp" promptText="%account.injector.server_ip">
|
||||
<JFXTextField fx:id="txtServerUrl" promptText="%account.injector.server_url">
|
||||
<validators>
|
||||
<URLValidator message="%input.url">
|
||||
</URLValidator>
|
||||
<URLValidator message="%input.url.http">
|
||||
<protocols>
|
||||
<String fx:value="http" />
|
||||
<String fx:value="https" />
|
||||
@@ -48,7 +46,9 @@
|
||||
<actions>
|
||||
<Label fx:id="lblCreationWarning" />
|
||||
<JFXButton onMouseClicked="#onAddCancel" text="%button.cancel" styleClass="dialog-cancel" />
|
||||
<JFXButton fx:id="btnAddNext" onMouseClicked="#onAddNext" text="%wizard.next" styleClass="dialog-accept" />
|
||||
<SpinnerPane fx:id="nextPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAddNext" onMouseClicked="#onAddNext" text="%wizard.next" styleClass="dialog-accept" />
|
||||
</SpinnerPane>
|
||||
</actions>
|
||||
</JFXDialogLayout>
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
<ColumnConstraints maxWidth="100" />
|
||||
<ColumnConstraints />
|
||||
</columnConstraints>
|
||||
<Label text="%account.injector.server_ip" GridPane.columnIndex="0" GridPane.rowIndex="0" />
|
||||
<Label text="%account.injector.server_url" GridPane.columnIndex="0" GridPane.rowIndex="0" />
|
||||
<Label text="%account.injector.server_name" GridPane.columnIndex="0" GridPane.rowIndex="1" />
|
||||
|
||||
<Label fx:id="lblServerIp" GridPane.columnIndex="1" GridPane.rowIndex="0" />
|
||||
<Label fx:id="lblServerUrl" GridPane.columnIndex="1" GridPane.rowIndex="0" />
|
||||
<Label fx:id="lblServerName" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||
|
||||
<Label fx:id="lblServerWarning" text="%account.injector.http" style="-fx-text-fill: red;" GridPane.rowIndex="2" GridPane.columnSpan="2" GridPane.columnIndex="0" />
|
||||
@@ -78,7 +78,6 @@
|
||||
</actions>
|
||||
</JFXDialogLayout>
|
||||
</StackPane>
|
||||
<JFXProgressBar fx:id="progressBar" visible="false" StackPane.alignment="TOP_CENTER" />
|
||||
</StackPane>
|
||||
</JFXDialog>
|
||||
</fx:root>
|
||||
@@ -20,9 +20,10 @@ about.copyright.statement=Copyright (c) 2018 huangyuhui.
|
||||
about.author=Author
|
||||
about.author.statement=huanghongxun ([email protected])
|
||||
about.thanks_to=Thanks to
|
||||
about.thanks_to.statement=bangbang93 (BMCLAPI, http://bmclapi2.bangbang93.com/)\ngamerteam (Default background image)\nAll contributors.
|
||||
about.dependency=Dependency
|
||||
about.dependency.statement=Gson (Google, Apache License 2.0)\nCommons Compress (Apache, Apache License 2.0)
|
||||
about.thanks_to.statement=bangbang93 (BMCLAPI, https://bmclapi2.bangbang93.com/)\ngamerteam (Default background image)\nAll contributors who participated in this project via issues, pull requests, etc.
|
||||
about.dependency=Dependencies
|
||||
# Due to space limitations, only first authors are listed
|
||||
about.dependency.statement=JFoenix (Shadi Shaheen, Apache 2.0)\nGson (Google, Apache 2.0)\nApache Commons Compress (ASF, Apache 2.0)\nXZ for Java (Lasse Collin, Public Domain)\nfx-gson (Joffrey Bion, MIT)
|
||||
about.claim=Statement
|
||||
about.claim.statement=Minecraft is copyrighted by Mojang AB. We are not responsible for any copyright infringement arising from the use of this software.
|
||||
about.open_source=Open Source
|
||||
@@ -31,19 +32,20 @@ about.open_source.statement=GPL v3 (https://github.com/huanghongxun/HMCL/)
|
||||
account=Accounts
|
||||
account.character=character
|
||||
account.choose=Choose a character
|
||||
account.current=Current
|
||||
account.create=Create a new account
|
||||
account.email=Email
|
||||
account.failed.connect_authentication_server=Cannot connect the authentication server. Check your network.
|
||||
account.failed.invalid_credentials=Incorrect password. Or forbidden to log in temporarily.
|
||||
account.failed.connect_authentication_server=Cannot connect to the authentication server. Check your network.
|
||||
account.failed.invalid_credentials=Incorrect password, or you are forbidden to login temporarily.
|
||||
account.failed.invalid_password=Invalid password
|
||||
account.failed.invalid_token=Please log out and re-input your password to log in.
|
||||
account.failed.invalid_token=Please log out and re-input your password to login.
|
||||
account.failed.no_charactor=No character in this account.
|
||||
account.failed.no_selected_server=No authentication server is selected.
|
||||
account.failed.connect_injector_server=Cannot connect to the authentication server. Check your network and ensure the URL is correct.
|
||||
account.injector.add=Add authentication server
|
||||
account.injector.manage=Manage authentication servers
|
||||
account.injector.http=Warning: This server is using HTTP, which will cause your password be transmitted in clear text.
|
||||
account.injector.server=Auth Server
|
||||
account.injector.server_ip=Server URL
|
||||
account.injector.server_url=Server URL
|
||||
account.injector.server_name=Server Name
|
||||
account.methods=Login Type
|
||||
account.methods.authlib_injector=authlib-injector
|
||||
@@ -59,66 +61,30 @@ archive.game_version=Game
|
||||
archive.version=Version
|
||||
|
||||
assets.download=Download assets
|
||||
assets.download_all=Check for the completion of assets
|
||||
assets.failed=Failed to get the list, try again.
|
||||
assets.failed_download=Failed to download assets, may cause no sounds and language files.
|
||||
assets.download_all=Check the integrity of assets
|
||||
|
||||
button.about=About
|
||||
button.cancel=Cancel
|
||||
button.clear=Clear
|
||||
button.close=Close
|
||||
button.copy=Copy
|
||||
button.delete=Delete
|
||||
button.download=Download
|
||||
button.edit=Edit
|
||||
button.explore=Explore
|
||||
button.info=Info
|
||||
button.install=Install
|
||||
button.more=More
|
||||
button.no=No
|
||||
button.ok=OK
|
||||
button.others=Others
|
||||
button.preview=Preview
|
||||
button.refresh=Refresh
|
||||
button.retry=Retry
|
||||
button.save=Save
|
||||
button.test=Test
|
||||
button.yes=Yes
|
||||
|
||||
color.blue=Blue
|
||||
color.dark_blue=Dark Blue
|
||||
color.green=Green
|
||||
color.orange=Orange
|
||||
color.purple=Purple
|
||||
color.red=Red
|
||||
color.recent=Recommended
|
||||
color.custom=Custom Color
|
||||
|
||||
crash.NoClassDefFound=Please check "HMCL" software is complete.
|
||||
crash.advice.ClassNotFoundException=Minecraft or mods are incomplete. Retry if there are some libraries that have not downloaded or update your game and mods! Or you can try Game Settings -> Manage (Version) -> Delete library files to solve the problem.
|
||||
crash.advice.ConcurrentModificationException=Maybe your Java is newer than 1.8.0_11, you could downgrade to Java 7.
|
||||
crash.advice.LWJGLException=Maybe your video driver does not work well, please update your video driver.
|
||||
crash.advice.NoSuchFieldError=Minecraft or mods are incomplete. Retry if there are some libraries that have not downloaded or update your game and mods!
|
||||
crash.advice.OpenGL=Maybe drivers caused problems.
|
||||
crash.advice.OutOfMemoryError=The maximum memory of JVM is too small, please modify it.
|
||||
crash.advice.SecurityException=Maybe you have modified minecraft.jar but have not removed the META-INF.
|
||||
crash.advice.UnsupportedClassVersionError=Maybe your java is too old, try to update the java.
|
||||
crash.advice.no=No advice.
|
||||
crash.advice.no_lwjgl=Maybe drivers caused problems.
|
||||
crash.advice.otherwise=Maybe mods caused problems.
|
||||
crash.error=Minecraft has crashed.
|
||||
crash.launcher=Launcher has crashed!
|
||||
crash.minecraft=Minecraft has crashed!
|
||||
crash.user_fault=Your OS or Java environment may not be properly installed resulting in crashing of this software, please check your Java Environment or your computer!
|
||||
|
||||
download=Download
|
||||
download.BMCL=BMCLAPI (bangbang93, http://bmclapi2.bangbang93.com/)
|
||||
download.BMCL=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)
|
||||
download.failed=Failed to download
|
||||
download.failed.refresh=Unable to load version list. Click here to retry.
|
||||
download.mojang=Mojang
|
||||
download.not_200=Failed to download, the response code is %s.
|
||||
download.source=Download Source
|
||||
download.successfully=Downloaded successfully
|
||||
|
||||
extension.bat=Windows Bat file
|
||||
extension.mod=Mod file
|
||||
@@ -135,14 +101,12 @@ folder.screenshots=Screenshots
|
||||
|
||||
input.email=The username must be an e-mail.
|
||||
input.number=Must be a number.
|
||||
input.not_empty=Input Requrired!
|
||||
input.not_empty=Required field
|
||||
input.url=Must be a valid URL.
|
||||
input.url.http=Only Http or Https accepted.
|
||||
|
||||
install=Install New Game
|
||||
install.failed=Failed to install
|
||||
install.installer.choose=Choose a %s version
|
||||
install.installer.failed=Unable to install %s.
|
||||
install.installer.forge=Forge
|
||||
install.installer.game=Game
|
||||
install.installer.install=
|
||||
@@ -154,37 +118,29 @@ install.modpack=Install a modpack (CurseForge supported)
|
||||
install.new_game=Install a New Game
|
||||
install.new_game.already_exists=This version has already been existing.
|
||||
install.new_game.current_game_version=Current Game Version
|
||||
install.no_version=The version is not found.
|
||||
install.no_version_if_intall=The required version is not found, do you wish to install the version automatically?
|
||||
install.release_time=Release Time
|
||||
install.select=Select an operation
|
||||
install.success=Installed successfully
|
||||
install.time=Time
|
||||
install.type=Type
|
||||
|
||||
lang=English
|
||||
lang.default=Belong to OS language.
|
||||
|
||||
launch.advice.different_platform=Your OS is 64-Bit but your Java is 32-Bit. The 64-Bit Java is recommended.
|
||||
launch.advice.java9=You cannot launch Minecraft until game version is higher than 1.13 with Java 9 or newer Java version.
|
||||
launch.advice.java8_1_13=Minecraft 1.13 and later versions can only run on Java 8 or later.
|
||||
launch.advice.java9=You cannot launch Minecraft until game version is higher than 1.13 with Java 9 or later Java versions.
|
||||
launch.advice.newer_java=Java 8 is suggested, which can make game run more fluently. And many mods and Minecraft 1.12 and newer versions requires Java 8.
|
||||
launch.advice.not_enough_space=You have allocated too much memory, because the physical memory size is %dMB, your game probably crash. The launcher will try to launch it.
|
||||
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game probably crash. The maximum memory is 1024MB. The launcher will try to launch it.
|
||||
launch.circular_dependency_versions=Found circular dependency versions, please check if your client has been modified.
|
||||
launch.failed=Unable to launch
|
||||
launch.failed.cannot_create_jvm=Java virtual machine cannot be created. Java arguments may have problems. You can enable the no args mode in the settings.
|
||||
launch.failed.creating_process=Failed to create process, maybe your java path is wrong, please modify your java path.
|
||||
launch.failed.decompressing_natives=Unable to decompress native libraries.
|
||||
launch.failed.downloading_libraries=Did not finish downloading libraries, continue launching game?
|
||||
launch.failed.executable_permission=Unable to add permission to the launch script
|
||||
launch.failed.exited_abnormally=Game exited abnormally, please visit the log, or ask someone for help.
|
||||
launch.failed.prelaunch_command=Prelaunch command fail.
|
||||
launch.state.dependencies=Dependencies
|
||||
launch.state.done=Done
|
||||
launch.state.logging_in=Logging In
|
||||
launch.state.modpack=Preparing for modpack
|
||||
launch.state.waiting_launching=Waiting for game launching
|
||||
launch.unsupported_launcher_version=Sorry, the launcher cannot launch minecraft, but will retry launching it.
|
||||
launch.wrong_javadir=Incorrect Java directory, will reset to default Java directory.
|
||||
|
||||
launcher=Launcher
|
||||
@@ -196,61 +152,31 @@ launcher.common_directory.choose=Choose common directory.
|
||||
launcher.contact=Contact Us
|
||||
launcher.crash=Hello Minecraft! Launcher has crashed!
|
||||
launcher.crash_out_dated=Hello Minecraft! Launcher has crashed! Your launcher is outdated. Update it!
|
||||
launcher.exit_failed=Failed to shutdown.
|
||||
launcher.modpack=Documentations for modpacks.
|
||||
launcher.restart=Options will be in operations only if restart this app.
|
||||
launcher.tab.about=About
|
||||
launcher.tab.general=General
|
||||
launcher.tab.ui=UI
|
||||
launcher.update_java=Please upgrade your Java.
|
||||
launcher.update_launcher=Check for update
|
||||
launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it?
|
||||
launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem?
|
||||
launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it?
|
||||
|
||||
login.changed_client_token=The server response has changed the client token.
|
||||
login.choose_charactor=Please choose the character you want
|
||||
login.empty_username=You have not set a username!
|
||||
login.enter_password=Please enter your password.
|
||||
login.enter_username=Please enter your name.
|
||||
login.failed=Failed to login
|
||||
login.invalid_username=Invalid username
|
||||
login.invalid_uuid_and_username=Invalid UUID and username
|
||||
login.no_valid_character=No valid character, please visit skinme.cc and create your own character.
|
||||
|
||||
logwindow.search=Search
|
||||
logwindow.show_lines=Show Lines
|
||||
logwindow.terminate_game=Terminate Game
|
||||
logwindow.title=Log
|
||||
|
||||
main_page=Home
|
||||
|
||||
message.cancelled=Cancelled
|
||||
message.cannot_open_explorer=Cannot open explorer:
|
||||
message.confirm=Confirm
|
||||
message.doing=Please wait
|
||||
message.downloading=Downloading...
|
||||
message.error=Error
|
||||
message.info=Info
|
||||
message.loading=Loading...
|
||||
message.success=Tasks succeeded
|
||||
message.unknown=Unknown
|
||||
|
||||
minecraft.invalid=Invalid
|
||||
minecraft.invalid_jar=Invalid Jar
|
||||
minecraft.modified=(Modified!)
|
||||
minecraft.not_a_file=Not a file
|
||||
minecraft.not_found=Not found
|
||||
minecraft.not_readable=Not readable
|
||||
minecraft.wrong_path=Wrong Minecraft path, the launcher could not find the path.
|
||||
|
||||
modpack=Mod pack
|
||||
modpack.choose=Choose a modpack zip file which you want to import. If you want to update the modpack, please enter the version you want to update.
|
||||
modpack.desc=Describe your modpack, including precautions, changlog, supporting Markdown(also supporting online pictures).
|
||||
modpack.enter_name=Enter your desired name for this game.
|
||||
modpack.export=Export Modpack
|
||||
modpack.export_error=Failed to export the modpack, maybe the format of your game directory is incorrect or failed to manage files.
|
||||
modpack.export_finished=Exporting the modpack finished. See
|
||||
modpack.files.blueprints=BuildCraft blueprints
|
||||
modpack.files.config=Mod configs
|
||||
modpack.files.dumps=NEI debug output
|
||||
@@ -263,16 +189,13 @@ modpack.files.resourcepacks=Resource(Texture) packs
|
||||
modpack.files.saves=Saved games
|
||||
modpack.files.scripts=MineTweaker configuration
|
||||
modpack.files.servers_dat=Multiplayer servers list
|
||||
modpack.included_launcher=The modpack is included the launcher, you can publish it directly.
|
||||
modpack.install=Install %s modpack
|
||||
modpack.installing=Installing modpack
|
||||
modpack.introduction=Curse, MultiMC, HMCL, MCBBS modpacks supprted.
|
||||
modpack.invalid=Invalid modpack file.
|
||||
modpack.mismatched_type=Mismatched modpack type, your current game is %s modpack, but your update file is %s modpack.
|
||||
modpack.name=Modpack Name
|
||||
modpack.not_a_valid_location=Not a valid modpack path
|
||||
modpack.not_a_valid_name=Not a valid modpack name
|
||||
modpack.not_included_launcher=You can use the modpack by clicking the "Import Modpack" button.
|
||||
modpack.scan=Scanning this modpack
|
||||
modpack.task.install=Import Modpack
|
||||
modpack.task.install.error=Failed to install the modpack. Maybe the files is incorrect, or a management issue occurred.
|
||||
@@ -289,7 +212,6 @@ modpack.wizard.step.1.title=Set the basic options to the modpack.
|
||||
modpack.wizard.step.2=Files selection
|
||||
modpack.wizard.step.2.title=Choose the files you want to put into the modpack.
|
||||
modpack.wizard.step.3=Description
|
||||
modpack.wizard.step.3.title=Describe your modpack.
|
||||
modpack.wizard.step.initialization.exported_version=The exported game version
|
||||
modpack.wizard.step.initialization.include_launcher=Include the launcher
|
||||
modpack.wizard.step.initialization.save=Choose a path to export the game files to
|
||||
@@ -300,11 +222,8 @@ mods.add=Add mods
|
||||
mods.add.failed=Failed to add mods %s.
|
||||
mods.add.success=Successfully added mods %s.
|
||||
mods.choose_mod=Choose your mods
|
||||
mods.default_information=Please ensure that you have installed Forge or LiteLoader before installing mods!\nYou can drop your mod files from explorer/finder, and delete mods by the delete button.\nDisable a mod by leaving the check box unchecked; Choose an item to get the information.
|
||||
mods.remove=Remove
|
||||
|
||||
operation.confirm_stop=Terminate the operations?
|
||||
operation.stopped=The operation was aborted.
|
||||
|
||||
profile=Profile
|
||||
profile.default=Current directory
|
||||
@@ -312,8 +231,6 @@ profile.home=User home
|
||||
profile.instance_directory=Game Directory
|
||||
profile.instance_directory.choose=Choose Game Directory
|
||||
profile.new=New Config
|
||||
profile.new_name=New Profile Name:
|
||||
profile.remove=Sure to remove profile %s?
|
||||
profile.title=Game Directories
|
||||
profile.selected=Selected
|
||||
|
||||
@@ -342,10 +259,7 @@ settings.advanced.precall_command=Pre-Launch command(will be executed before gam
|
||||
settings.advanced.server_ip=Server Host
|
||||
settings.advanced.wrapper_launcher=Wrapper Launcher(i.e. optirun...)
|
||||
|
||||
settings.cannot_remove_default_config=Cannot remove the default configution.
|
||||
settings.custom=Custom
|
||||
settings.default=Default
|
||||
settings.failed_load=Failed to load settings file. Remove it?
|
||||
|
||||
settings.game=Games
|
||||
settings.game.dimension=Game Window Dimension
|
||||
@@ -378,16 +292,11 @@ settings.launcher.theme=Theme
|
||||
|
||||
settings.max_memory=Max Memory/MB
|
||||
settings.physical_memory=Physical Memory Size
|
||||
settings.run_directory=Run Directory(Version Isolation)
|
||||
settings.show_log=Show Logs
|
||||
settings.tabs.assets_downloads=Assets
|
||||
settings.tabs.game_download=Games
|
||||
settings.tabs.installers=Installers
|
||||
settings.test_game=Test game
|
||||
settings.type.global=Global version settings(all shared)
|
||||
settings.type=Version setting type
|
||||
settings.type.special=Specialized version settings(will not affect other versions)
|
||||
settings.update_version=Update version json.
|
||||
|
||||
update=Update
|
||||
update.failed=Failed to check for updates.
|
||||
@@ -395,7 +304,6 @@ update.found=Found Update!
|
||||
update.newest_version=Newest version: %s
|
||||
update.latest=This is latest Version.
|
||||
update.no_browser=Cannot open any browser. The link has been copied to the clipboard. Paste it to a browser address bar to update.
|
||||
update.should_open_link=Are you willing to update the launcher?
|
||||
update.tooltip=Update
|
||||
|
||||
version.cannot_read=Unable to gather the game version. Cannot continue auto-installing.
|
||||
@@ -409,7 +317,6 @@ version.launch_script.failed=Unable to make launch script.
|
||||
version.launch_script.save=Save the launch script
|
||||
version.launch_script.success=Finished script creation, %s.
|
||||
version.manage.redownload_assets_index=Redownload Assets Index
|
||||
version.manage.redownload_json=Redownload Minecraft Configuration(minecraft.json)
|
||||
version.manage.remove=Delete this version
|
||||
version.manage.remove.confirm=Sure to remove version %s?
|
||||
version.manage.remove_libraries=Delete library files
|
||||
@@ -419,10 +326,6 @@ version.settings=Settings
|
||||
version.update=Update
|
||||
|
||||
wizard.prev=< Prev
|
||||
wizard.close=Close
|
||||
wizard.failed=Failed
|
||||
wizard.finish=Finish
|
||||
wizard.help=Help
|
||||
wizard.next=Next >
|
||||
wizard.steps=Steps
|
||||
wizard.summary=Summary
|
||||
@@ -20,9 +20,10 @@ about.copyright.statement=Copyright (c) 2018 huangyuhui.
|
||||
about.author=作者
|
||||
about.author.statement=huanghongxun ([email protected])
|
||||
about.thanks_to=鸣谢
|
||||
about.thanks_to.statement=bangbang93 (BMCLAPI, http://bmclapi2.bangbang93.com/)\ngamerteam (默认背景图)\n所有参与本项目,issue,pull requests的贡献者
|
||||
about.thanks_to.statement=bangbang93 (BMCLAPI, https://bmclapi2.bangbang93.com/)\ngamerteam (默认背景图)\n所有通过 Issues、Pull Requests 等方式参与本项目的贡献者
|
||||
about.dependency=依赖
|
||||
about.dependency.statement=Gson (Google, Apache License 2.0)\nCommons Compress (Apache, Apache License 2.0)
|
||||
# 由于篇幅限制,仅列出第一作者
|
||||
about.dependency.statement=JFoenix (Shadi Shaheen, Apache 2.0)\nGson (Google, Apache 2.0)\nApache Commons Compress (ASF, Apache 2.0)\nXZ for Java (Lasse Collin, Public Domain)\nfx-gson (Joffrey Bion, MIT)
|
||||
about.claim=免责声明
|
||||
about.claim.statement=Minecraft 版权归 Mojang AB 所有,使用本软件产生的版权问题,软件制作方概不负责。请支持正版。
|
||||
about.open_source=开源
|
||||
@@ -31,7 +32,6 @@ about.open_source.statement=GPL v3 (https://github.com/huanghongxun/HMCL/)
|
||||
account=账户
|
||||
account.character=角色
|
||||
account.choose=选择一个角色
|
||||
account.current=当前账户
|
||||
account.create=新建账户
|
||||
account.email=邮箱
|
||||
account.failed.connect_authentication_server=无法连接认证服务器,可能是网络问题
|
||||
@@ -39,11 +39,13 @@ account.failed.invalid_credentials=您的用户名或密码错误,或者登录
|
||||
account.failed.invalid_password=无效的密码
|
||||
account.failed.invalid_token=请尝试登出并重新输入密码登录
|
||||
account.failed.no_charactor=该帐号没有角色
|
||||
account.failed.no_selected_server=未选择认证服务器
|
||||
account.failed.connect_injector_server=无法连接认证服务器,可能是网络故障或 URL 输入错误
|
||||
account.injector.add=添加认证服务器
|
||||
account.injector.manage=管理认证服务器
|
||||
account.injector.http=警告:此服务器使用不安全的http协议,您的密码在登录时会被明文传输。
|
||||
account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您的密码在登录时会被明文传输。
|
||||
account.injector.server=认证服务器
|
||||
account.injector.server_ip=服务器地址
|
||||
account.injector.server_url=服务器地址
|
||||
account.injector.server_name=服务器名称
|
||||
account.methods=登录方式
|
||||
account.methods.authlib_injector=authlib-injector 登录
|
||||
@@ -60,65 +62,29 @@ archive.version=版本
|
||||
|
||||
assets.download=下载资源
|
||||
assets.download_all=检查资源文件完整性
|
||||
assets.failed=获取列表失败,请刷新重试。
|
||||
assets.failed_download=下载资源文件失败,可能导致没有中文和声音。
|
||||
|
||||
button.about=关于
|
||||
button.cancel=取消
|
||||
button.clear=清除
|
||||
button.close=关闭
|
||||
button.copy=复制
|
||||
button.delete=删除
|
||||
button.download=下载
|
||||
button.edit=修改
|
||||
button.explore=浏览
|
||||
button.info=信息
|
||||
button.install=安装
|
||||
button.more=更多
|
||||
button.no=否
|
||||
button.ok=确定
|
||||
button.others=其他
|
||||
button.preview=预览
|
||||
button.refresh=刷新
|
||||
button.retry=重试
|
||||
button.save=保存
|
||||
button.test=测试
|
||||
button.yes=是
|
||||
|
||||
color.blue=蓝色
|
||||
color.dark_blue=深蓝色
|
||||
color.green=绿色
|
||||
color.orange=橙色
|
||||
color.purple=紫色
|
||||
color.red=红色
|
||||
color.recent=推荐
|
||||
color.custom=自定义颜色
|
||||
|
||||
crash.NoClassDefFound=请确认HMCL本体是否完整,或更新您的Java。
|
||||
crash.advice.ClassNotFoundException=Minecraft不完整或Mod冲突,如果有未能下载的文件请下载成功后重试或是客户端损坏请重试请重新制作客户端或下载整合包解决问题,另可尝试游戏设置->(版本)管理->删除库文件解决问题
|
||||
crash.advice.ConcurrentModificationException=这可能是因为您的Java版本高于Java 1.8.0_11导致的,可以尝试卸载Java8安装Java7。
|
||||
crash.advice.LWJGLException=您的电脑不正常,可能需要使用驱动精灵或其他安装器更新显卡驱动。
|
||||
crash.advice.NoSuchFieldError=Minecraft不完整或Mod冲突,如果有未能下载的文件请下载成功后重试或是客户端损坏请重试请重新制作客户端或下载整合包解决问题。
|
||||
crash.advice.OpenGL=可能是显卡/声卡驱动问题,也可能是Mod导致的问题。
|
||||
crash.advice.OutOfMemoryError=内存溢出,您设置的Minecraft最大内存过小,请调大!
|
||||
crash.advice.SecurityException=可能是您修改了minecraft.jar但未删除META-INF文件夹的原因。请通过压缩软件删除jar中的META-INF文件夹。
|
||||
crash.advice.UnsupportedClassVersionError=这可能是因为您的Java版本过于老旧,可以尝试更换最新Java并在版本设置的Java路径中设置.
|
||||
crash.advice.no=无建议。
|
||||
crash.advice.no_lwjgl=可能是游戏依赖库不完整或解压依赖库时出错。可以通过下载整合包解决问题。
|
||||
crash.advice.otherwise=可能是Mod或其他问题。
|
||||
crash.error=您的Minecraft崩溃了。
|
||||
crash.launcher=启动器崩溃了!
|
||||
crash.minecraft=Minecraft崩溃了!请认真阅读建议。
|
||||
crash.user_fault=您的系统或Java环境可能安装不当导致本软件崩溃,请检查您的Java环境或您的电脑!可以尝试重新安装Java。
|
||||
crash.NoClassDefFound=请确认 HMCL 本体是否完整,或更新您的 Java。
|
||||
crash.user_fault=您的系统或 Java 环境可能安装不当导致本软件崩溃,请检查您的 Java 环境或您的电脑!可以尝试重新安装 Java。
|
||||
|
||||
download=下载
|
||||
download.BMCL=BMCLAPI (bangbang93, http://bmclapi2.bangbang93.com/)
|
||||
download.BMCL=BMCLAPI(bangbang93,https://bmclapi2.bangbang93.com/)
|
||||
download.failed=下载失败
|
||||
download.failed.refresh=加载版本列表失败,点击此处重试。
|
||||
download.mojang=官方
|
||||
download.not_200=下载失败,HTTP状态码:%s.
|
||||
download.source=下载源
|
||||
download.successfully=下载完成
|
||||
|
||||
extension.bat=Windows 脚本
|
||||
extension.mod=模组文件
|
||||
@@ -137,174 +103,127 @@ input.email=用户名必须是邮箱
|
||||
input.number=必须是数字
|
||||
input.not_empty=必填项
|
||||
input.url=必须是合法的链接
|
||||
input.url.http=只支持Http或Https协议
|
||||
|
||||
install=添加游戏
|
||||
install.failed=安装失败
|
||||
install.installer.choose=选择%s版本
|
||||
install.installer.failed=未能成功安装%s.
|
||||
install.installer.choose=选择 %s 版本
|
||||
install.installer.forge=Forge
|
||||
install.installer.game=游戏
|
||||
install.installer.install=安装%s
|
||||
install.installer.install=安装 %s
|
||||
install.installer.liteloader=LiteLoader
|
||||
install.installer.not_installed=暂不安装%s,可以点击此处安装
|
||||
install.installer.not_installed=暂不安装 %s,可以点击此处安装
|
||||
install.installer.optifine=OptiFine
|
||||
install.installer.version=%s版本
|
||||
install.installer.version=%s 版本
|
||||
install.modpack=安装整合包
|
||||
install.new_game=安装新游戏版本
|
||||
install.new_game.already_exists=此版本已经存在,请换一个名字
|
||||
install.new_game.current_game_version=当前游戏版本
|
||||
install.no_version=未找到要安装的对应MC版本
|
||||
install.no_version_if_intall=未找到要安装的对应MC版本,是否自动安装需要的MC版本?
|
||||
install.release_time=发布时间
|
||||
install.select=请选择安装方式
|
||||
install.success=安装成功
|
||||
install.time=时间
|
||||
install.type=类型
|
||||
|
||||
lang=简体中文
|
||||
lang.default=跟随系统语言
|
||||
|
||||
launch.advice.different_platform=您的系统是64位的但是Java是32位的,推荐您安装64位Java.
|
||||
launch.advice.java9=不高于1.13的Minecraft不支持Java 9或更高版本,请使用Java 8.
|
||||
launch.advice.newer_java=检测到您未使用Java 8及更新版本,Java 8能使游戏更流畅而且Minecraft 1.12及更新版本和很多Mod强制需要需要Java 8。
|
||||
launch.advice.not_enough_space=您设置的内存大小过大,由于超过了系统内存大小%dMB,所以可能影响游戏体验或无法启动游戏,启动器仍会尝试启动。
|
||||
launch.advice.too_large_memory_for_32bit=您设置的内存大小过大,由于可能超过了32位Java的内存分配限制,所以可能无法启动游戏,请将内存调至1024MB或更小,启动器仍会尝试启动。
|
||||
launch.circular_dependency_versions=发现游戏版本循环引用,请确认您的客户端未被修改或修改导致出现此问题。
|
||||
launch.advice.different_platform=您的系统是 64 位的但是 Java 是 32 位的,推荐您安装 64 位 Java。
|
||||
launch.advice.java8_1_13=Minecraft 1.13 只支持 Java 8 或更高版本,请使用 Java 8 或最新版本。
|
||||
launch.advice.java9=不高于 1.13 的安装了 Mod 的 Minecraft 不支持 Java 9 或更高版本,请使用 Java 8。
|
||||
launch.advice.newer_java=检测到您未使用 Java 8 及更新版本,Java 8 能使游戏更流畅而且 Minecraft 1.12 及更新版本和很多 Mod 强制需要需要 Java 8。
|
||||
launch.advice.not_enough_space=您设置的内存大小过大,由于超过了系统内存大小 %dMB,所以可能影响游戏体验或无法启动游戏,启动器仍会尝试启动。
|
||||
launch.advice.too_large_memory_for_32bit=您设置的内存大小过大,由于可能超过了 32 位 Java 的内存分配限制,所以可能无法启动游戏,请将内存调至 1024MB 或更小,启动器仍会尝试启动。
|
||||
launch.failed=启动失败
|
||||
launch.failed.cannot_create_jvm=截获到无法创建Java虚拟机,可能是Java参数有问题,可以在设置中开启无参数模式启动
|
||||
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是Java路径错误。
|
||||
launch.failed.cannot_create_jvm=截获到无法创建 Java 虚拟机,可能是 Java 参数有问题,可以在设置中开启无参数模式启动。
|
||||
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是 Java 路径错误。
|
||||
launch.failed.decompressing_natives=未能解压游戏本地库。
|
||||
launch.failed.downloading_libraries=未完成游戏依赖库的下载,还要继续启动游戏吗?
|
||||
launch.failed.executable_permission=未能为启动文件添加执行权限
|
||||
launch.failed.executable_permission=未能为启动文件添加执行权限。
|
||||
launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
|
||||
launch.failed.prelaunch_command=启动前执行命令执行失败。
|
||||
launch.state.dependencies=正在处理游戏依赖
|
||||
launch.state.done=启动完成
|
||||
launch.state.logging_in=登录中
|
||||
launch.state.modpack=正在下载必要文件
|
||||
launch.state.waiting_launching=等待游戏启动
|
||||
launch.unsupported_launcher_version=对不起,本启动器现在可能不能启动这个版本的Minecraft,但启动器还是会尝试启动,请尽快将此问题报告给作者。
|
||||
launch.wrong_javadir=错误的Java路径,将自动重置为默认Java路径。
|
||||
launch.wrong_javadir=错误的 Java 路径,将自动重置为默认 Java 路径。
|
||||
|
||||
launcher=启动器
|
||||
launcher.background=背景地址
|
||||
launcher.background.choose=选择背景路径
|
||||
launcher.background.default=默认(自动检索启动器同目录下的background.png/jpg及bg文件夹内的图片)
|
||||
launcher.background.default=默认(自动检索启动器同目录下的 background.png/jpg 及 bg 文件夹内的图片)
|
||||
launcher.common_directory=公共文件夹
|
||||
launcher.common_directory.choose=选择公共文件夹
|
||||
launcher.contact=联系我们
|
||||
launcher.crash=Hello Minecraft!遇到了无法处理的错误,请复制下列内容并通过mcbbs、贴吧、Github或Minecraft Forum反馈bug。
|
||||
launcher.crash_out_dated=Hello Minecraft! Launcher遇到了无法处理的错误,已检测到您的启动器不是最新版本,请更新后再试!
|
||||
launcher.exit_failed=强制退出失败,可能是Forge 1.7.10及更高版本导致的,无法解决。
|
||||
launcher.modpack=整合包作者帮助
|
||||
launcher.restart=本界面选项需要重启本启动器生效
|
||||
launcher.tab.about=关于
|
||||
launcher.tab.general=通用
|
||||
launcher.tab.ui=界面
|
||||
launcher.update_java=请更新您的Java
|
||||
launcher.update_launcher=检查更新
|
||||
launcher.versions_json_not_formatted=版本%s信息文件格式错误,是否重新下载?
|
||||
launcher.versions_json_not_matched=版本%s格式不规范!该版本文件夹下有json:%s,是否更名这个文件来规范格式?
|
||||
launcher.versions_json_not_matched_cannot_auto_completion=版本%s缺失必要的版本信息文件,是否删除该版本?
|
||||
launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误,请复制下列内容并通过 MCBBS、贴吧、GitHub 或 Minecraft Forum 反馈 bug。
|
||||
launcher.crash_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误,已检测到您的启动器不是最新版本,请更新后再试!
|
||||
launcher.update_java=请更新您的 Java
|
||||
|
||||
login.changed_client_token=服务器回应已经修改客户端令牌
|
||||
login.choose_charactor=请选择您要使用的角色
|
||||
login.empty_username=你还未设置用户名!
|
||||
login.enter_password=请输入您的密码
|
||||
login.enter_username=请输入您的账号
|
||||
login.failed=登录失败:
|
||||
login.invalid_username=无效的用户名
|
||||
login.invalid_uuid_and_username=无效的UUID和用户名
|
||||
login.no_valid_character=无有效的角色,自行到skinme.cc登陆并创建角色
|
||||
|
||||
logwindow.search=查找
|
||||
logwindow.show_lines=显示行数
|
||||
logwindow.terminate_game=结束游戏进程
|
||||
logwindow.title=日志
|
||||
|
||||
main_page=主页
|
||||
|
||||
message.cancelled=已取消
|
||||
message.cannot_open_explorer=无法打开文件管理器:
|
||||
message.confirm=提示
|
||||
message.doing=请耐心等待
|
||||
message.downloading=正在下载...
|
||||
message.error=错误
|
||||
message.info=提示
|
||||
message.loading=加载中...
|
||||
message.success=已完成
|
||||
message.unknown=未知
|
||||
|
||||
minecraft.invalid=无效的
|
||||
minecraft.invalid_jar=无效的jar包
|
||||
minecraft.modified=(修改的!)
|
||||
minecraft.not_a_file=不是文件
|
||||
minecraft.not_found=找不到minecraft.jar
|
||||
minecraft.not_readable=minecraft.jar不可读
|
||||
minecraft.wrong_path=错误的Minecraft路径,启动器未找到设定的Minecraft路径,请检查。
|
||||
|
||||
modpack=整合包
|
||||
modpack.choose=选择要导入的游戏整合包文件,如果您希望更新整合包,请输入要更新的版本名
|
||||
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录,支持Markdown(图片请用网络图片)。
|
||||
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录,支持 Markdown(图片请用网络图)。
|
||||
modpack.enter_name=给游戏起个你喜欢的名字
|
||||
modpack.export=导出整合包
|
||||
modpack.export_error=导出失败,可能是您的游戏文件夹格式不正确或操作文件失败
|
||||
modpack.export_finished=整合包导出完成,参见
|
||||
modpack.files.blueprints=BuildCraft蓝图
|
||||
modpack.files.config=Mod配置文件
|
||||
modpack.files.dumps=NEI调试输出
|
||||
modpack.files.liteconfig=Mod配置文件
|
||||
modpack.files.blueprints=BuildCraft 蓝图
|
||||
modpack.files.config=Mod 配置文件
|
||||
modpack.files.dumps=NEI 调试输出
|
||||
modpack.files.liteconfig=Mod 配置文件
|
||||
modpack.files.mods=Mod
|
||||
modpack.files.mods.voxelmods=VoxelMods配置,如小地图
|
||||
modpack.files.mods.voxelmods=VoxelMods 配置,如小地图
|
||||
modpack.files.options_txt=游戏设定
|
||||
modpack.files.optionsshaders_txt=光影设定
|
||||
modpack.files.resourcepacks=资源包(材质包)
|
||||
modpack.files.saves=游戏存档
|
||||
modpack.files.scripts=MineTweaker配置
|
||||
modpack.files.scripts=MineTweaker 配置
|
||||
modpack.files.servers_dat=多人游戏服务器列表
|
||||
modpack.included_launcher=整合包已包含启动器,可直接发布
|
||||
modpack.install=安装%s整合包
|
||||
modpack.installing=正在安装整合包
|
||||
modpack.introduction=支持 Curse, MultiMC, HMCL, MCBBS 整合包。
|
||||
modpack.introduction=支持 Curse、MultiMC、HMCL、MCBBS 整合包。
|
||||
modpack.invalid=无效的整合包升级文件,可能是下载时出现问题。
|
||||
modpack.mismatched_type=整合包类型不匹配,当前游戏是 %s 整合包,但是提供的整合包更新文件是 %s 整合包。
|
||||
modpack.name=整合包名称
|
||||
modpack.not_a_valid_location=不是一个有效整合包位置
|
||||
modpack.not_a_valid_name=不是一个有效的整合包名称
|
||||
modpack.not_included_launcher=整合包未包含启动器,可使用本软件的导入整合包功能导入整合包
|
||||
modpack.scan=解析整合包
|
||||
modpack.task.install=导入整合包
|
||||
modpack.task.install.error=安装失败,可能是整合包格式不正确或操作文件失败
|
||||
modpack.task.install.error=安装失败,可能是整合包格式不正确或操作文件失败。
|
||||
modpack.task.install.will=将会安装整合包:
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.curse.completion=下载Curse整合包相关文件
|
||||
modpack.type.curse.completion=下载 Curse 整合包相关文件
|
||||
modpack.type.hmcl=HMCL
|
||||
modpack.type.multimc=MultiMC
|
||||
modpack.unsupported=该整合包不被支持。仅HMCL、MultiMC、Curse整合包受支持。
|
||||
modpack.unsupported=该整合包不被支持。仅 HMCL、MultiMC、Curse 整合包受支持。
|
||||
modpack.update=正在升级整合包
|
||||
modpack.wizard=导出整合包向导
|
||||
modpack.wizard.step.1=基本设置
|
||||
modpack.wizard.step.1.title=设置整合包的主要信息
|
||||
modpack.wizard.step.2=文件选择
|
||||
modpack.wizard.step.2.title=选中你想加到整合包中的文件(夹)
|
||||
modpack.wizard.step.2.title=选中你想加到整合包中的文件或文件夹
|
||||
modpack.wizard.step.3=整合包描述
|
||||
modpack.wizard.step.3.title=描述你要制作的整合包
|
||||
modpack.wizard.step.initialization.exported_version=要导出的游戏版本
|
||||
modpack.wizard.step.initialization.include_launcher=包含启动器
|
||||
modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位置
|
||||
modpack.wizard.step.initialization.warning=在制作整合包前,请您确认您选择的版本可以正常启动,\n并保证您的Minecraft是正式版而非快照版,\n而且不应当将不允许非官方途径传播的Mod、材质包等纳入整合包。\n整合包会保存您目前的下载源设置
|
||||
modpack.wizard.step.initialization.warning=在制作整合包前,请您确认您选择的版本可以正常启动,\n并保证您的 Minecraft 是正式版而非快照版,\n而且不应当将不允许非官方途径传播的 Mod、材质包等纳入整合包。\n整合包会保存您目前的下载源设置
|
||||
|
||||
mods=模组管理
|
||||
mods.add=添加模组
|
||||
mods.add.failed=添加模组%s失败。
|
||||
mods.add.success=成功添加模组%s。
|
||||
mods.add.failed=添加模组 %s 失败。
|
||||
mods.add.success=成功添加模组 %s。
|
||||
mods.choose_mod=选择模组
|
||||
mods.default_information=安装Mod前你需要确保已安装Forge或LiteLoader!\n您可以从资源管理器拖动mod文件到列表中来添加mod,同时使用删除键可快速删除选中mod\n点掉mod前面的勾可禁用mod,不会加载;选择mod可以获取mod信息
|
||||
mods.remove=删除
|
||||
|
||||
operation.confirm_stop=真的要终止操作吗?
|
||||
operation.stopped=操作被强行终止
|
||||
|
||||
profile=配置
|
||||
profile.default=当前目录
|
||||
@@ -312,8 +231,6 @@ profile.home=主文件夹
|
||||
profile.instance_directory=游戏路径
|
||||
profile.instance_directory.choose=选择游戏路径
|
||||
profile.new=新建配置
|
||||
profile.new_name=新配置名:
|
||||
profile.remove=真的要删除配置%s吗?
|
||||
profile.title=游戏目录
|
||||
profile.selected=已选中
|
||||
|
||||
@@ -325,27 +242,24 @@ settings=普通设置
|
||||
|
||||
settings.advanced=高级设置
|
||||
settings.advanced.dont_check_game_completeness=不检查游戏完整性
|
||||
settings.advanced.game_dir.default=默认(.minecraft/)
|
||||
settings.advanced.game_dir.independent=各版本独立(.minecraft/versions/<版本名>/,除assets,libraries)
|
||||
settings.advanced.game_dir.default=默认(.minecraft/)
|
||||
settings.advanced.game_dir.independent=各版本独立(.minecraft/versions/<版本名>/,除 assets、libraries)
|
||||
settings.advanced.java_args_default=启动器默认添加的参数(请不要重复添加):-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:-UseAdaptiveSizePolicy -XX:MaxPermSize=???m -Xmx???m -Dfml.ignoreInvalidMinecraftCertificates=true -Dfml.ignorePatchDiscrepancies=true
|
||||
settings.advanced.java_permanent_generation_space=内存永久保存区域(不必填写,MB)
|
||||
settings.advanced.jvm_args=Java虚拟机参数(不必填写)
|
||||
settings.advanced.java_permanent_generation_space=内存永久保存区域(不必填写,MB)
|
||||
settings.advanced.jvm_args=Java 虚拟机参数(不必填写)
|
||||
settings.advanced.launcher_visibility.close=游戏启动后结束启动器
|
||||
settings.advanced.launcher_visibility.hide=游戏启动后隐藏启动器
|
||||
settings.advanced.launcher_visibility.hide_reopen=隐藏启动器并在游戏结束后重新打开
|
||||
settings.advanced.launcher_visibility.keep=保持启动器可见
|
||||
settings.advanced.launcher_visible=启动器可见性
|
||||
settings.advanced.minecraft_arguments=Minecraft额外参数(不必填写)
|
||||
settings.advanced.minecraft_arguments=Minecraft 额外参数(不必填写)
|
||||
settings.advanced.no_common=不使用公共路径
|
||||
settings.advanced.no_jvm_args=不添加默认的JVM参数(使用Java9时必勾)
|
||||
settings.advanced.precall_command=启动前执行命令(不必填写,将在游戏启动前调用)
|
||||
settings.advanced.server_ip=直入服务器ip地址(不必填写,启动游戏后直接进入对应服务器)
|
||||
settings.advanced.wrapper_launcher=前置指令(不必填写,如optirun)
|
||||
settings.advanced.no_jvm_args=不添加默认的 JVM 参数(使用 Java 9 时必勾)
|
||||
settings.advanced.precall_command=启动前执行命令(不必填写,将在游戏启动前调用)
|
||||
settings.advanced.server_ip=直入服务器 IP 地址(不必填写,启动游戏后直接进入对应服务器)
|
||||
settings.advanced.wrapper_launcher=前置指令(不必填写,如 optirun)
|
||||
|
||||
settings.cannot_remove_default_config=不能删除默认配置
|
||||
settings.custom=自定义
|
||||
settings.default=默认
|
||||
settings.failed_load=设置文件加载失败,可能是升级了启动器或被人工修改造成错误,是否清除?
|
||||
|
||||
settings.game=游戏设置
|
||||
settings.game.dimension=游戏窗口分辨率
|
||||
@@ -353,8 +267,8 @@ settings.game.exploration=浏览
|
||||
settings.game.fullscreen=全屏
|
||||
settings.game.game_directory=游戏路径(版本隔离)
|
||||
settings.game.game_directory.choose=选择游戏路径
|
||||
settings.game.java_directory=Java路径
|
||||
settings.game.java_directory.choose=选择Java路径
|
||||
settings.game.java_directory=Java 路径
|
||||
settings.game.java_directory.choose=选择 Java 路径
|
||||
settings.game.management=管理
|
||||
|
||||
settings.icon=游戏图标
|
||||
@@ -376,18 +290,13 @@ settings.launcher.proxy.socks=Socks
|
||||
settings.launcher.proxy.username=账户
|
||||
settings.launcher.theme=主题
|
||||
|
||||
settings.max_memory=最大内存/MB
|
||||
settings.max_memory=最大内存(MB)
|
||||
settings.physical_memory=物理内存大小
|
||||
settings.run_directory=运行路径(版本隔离)
|
||||
settings.show_log=查看日志
|
||||
settings.tabs.assets_downloads=资源下载
|
||||
settings.tabs.game_download=游戏下载
|
||||
settings.tabs.installers=自动安装
|
||||
settings.test_game=测试游戏
|
||||
settings.type.global=全局版本设置(使用该设置的版本共用一套设定)
|
||||
settings.type=版本设置类型
|
||||
settings.type.special=单独版本设置(不会影响到其他版本的设定)
|
||||
settings.update_version=更新版本文件
|
||||
|
||||
update=启动器更新
|
||||
update.failed=检查更新失败
|
||||
@@ -395,7 +304,6 @@ update.found=发现更新
|
||||
update.newest_version=最新版本为:%s
|
||||
update.latest=当前版本为最新版本
|
||||
update.no_browser=无法打开浏览器,网址已经复制到剪贴板了,您可以手动粘贴网址打开页面
|
||||
update.should_open_link=是否更新?
|
||||
update.tooltip=更新
|
||||
|
||||
version.cannot_read=读取游戏版本失败,无法进行自动安装
|
||||
@@ -407,9 +315,8 @@ version.launch=启动游戏
|
||||
version.launch_script=生成启动脚本
|
||||
version.launch_script.failed=生成启动脚本失败
|
||||
version.launch_script.save=保存启动脚本
|
||||
version.launch_script.success=启动脚本已生成完毕: %s.
|
||||
version.manage.redownload_assets_index=重新下载资源配置(assets_index.json)
|
||||
version.manage.redownload_json=重新下载版本配置(minecraft.json)
|
||||
version.launch_script.success=启动脚本已生成完毕:%s
|
||||
version.manage.redownload_assets_index=重新下载资源配置(assets_index.json)
|
||||
version.manage.remove=删除该版本
|
||||
version.manage.remove.confirm=真的要删除版本 %s 吗?
|
||||
version.manage.remove_libraries=删除所有库文件
|
||||
@@ -419,10 +326,6 @@ version.settings=游戏设置
|
||||
version.update=更新
|
||||
|
||||
wizard.prev=< 上一步
|
||||
wizard.close=关闭
|
||||
wizard.failed=失败
|
||||
wizard.finish=完成
|
||||
wizard.help=帮助
|
||||
wizard.next=下一步 >
|
||||
wizard.steps=步骤
|
||||
wizard.summary=概要
|
||||
+8
-8
@@ -37,14 +37,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class AuthlibInjectorAccount extends YggdrasilAccount {
|
||||
private final String serverBaseURL;
|
||||
private final AuthlibInjectorServer server;
|
||||
private final ExceptionalSupplier<String, ?> injectorJarPath;
|
||||
|
||||
protected AuthlibInjectorAccount(YggdrasilService service, String serverBaseURL, ExceptionalSupplier<String, ?> injectorJarPath, String username, UUID characterUUID, YggdrasilSession session) {
|
||||
protected AuthlibInjectorAccount(YggdrasilService service, AuthlibInjectorServer server, ExceptionalSupplier<String, ?> injectorJarPath, String username, UUID characterUUID, YggdrasilSession session) {
|
||||
super(service, username, characterUUID, session);
|
||||
|
||||
this.injectorJarPath = injectorJarPath;
|
||||
this.serverBaseURL = serverBaseURL;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +59,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
||||
|
||||
private AuthInfo inject(ExceptionalSupplier<AuthInfo, AuthenticationException> supplier) throws AuthenticationException {
|
||||
// Authlib Injector recommends launchers to pre-fetch the server basic information before launched the game to save time.
|
||||
GetTask getTask = new GetTask(NetworkUtils.toURL(serverBaseURL));
|
||||
GetTask getTask = new GetTask(NetworkUtils.toURL(server.getUrl()));
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
Thread thread = Lang.thread(() -> flag.set(getTask.test()));
|
||||
|
||||
@@ -67,7 +67,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
||||
try {
|
||||
thread.join();
|
||||
|
||||
Arguments arguments = new Arguments().addJVMArguments("-javaagent:" + injectorJarPath.get() + "=" + serverBaseURL);
|
||||
Arguments arguments = new Arguments().addJVMArguments("-javaagent:" + injectorJarPath.get() + "=" + server.getUrl());
|
||||
|
||||
if (flag.get())
|
||||
arguments = arguments.addJVMArguments("-Dorg.to2mbn.authlibinjector.config.prefetched=" + new String(Base64.getEncoder().encode(getTask.getResult().getBytes()), UTF_8));
|
||||
@@ -81,12 +81,12 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
||||
@Override
|
||||
public Map<Object, Object> toStorage() {
|
||||
Map<Object, Object> map = super.toStorage();
|
||||
map.put("serverBaseURL", serverBaseURL);
|
||||
map.put("serverBaseURL", server.getUrl());
|
||||
return map;
|
||||
}
|
||||
|
||||
public String getServerBaseURL() {
|
||||
return serverBaseURL;
|
||||
public AuthlibInjectorServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
}
|
||||
+15
-9
@@ -6,33 +6,37 @@ import org.jackhuang.hmcl.auth.CharacterSelector;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
|
||||
import org.jackhuang.hmcl.util.ExceptionalSupplier;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
|
||||
public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjectorAccount> {
|
||||
private final ExceptionalSupplier<String, ?> injectorJarPathSupplier;
|
||||
private Function<String, AuthlibInjectorServer> serverLookup;
|
||||
|
||||
public AuthlibInjectorAccountFactory(ExceptionalSupplier<String, ?> injectorJarPathSupplier) {
|
||||
/**
|
||||
* @param serverLookup a function that looks up {@link AuthlibInjectorServer} by url
|
||||
*/
|
||||
public AuthlibInjectorAccountFactory(ExceptionalSupplier<String, ?> injectorJarPathSupplier, Function<String, AuthlibInjectorServer> serverLookup) {
|
||||
this.injectorJarPathSupplier = injectorJarPathSupplier;
|
||||
this.serverLookup = serverLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthlibInjectorAccount create(CharacterSelector selector, String username, String password, Object serverBaseURL, Proxy proxy) throws AuthenticationException {
|
||||
public AuthlibInjectorAccount create(CharacterSelector selector, String username, String password, Object additionalData, Proxy proxy) throws AuthenticationException {
|
||||
Objects.requireNonNull(selector);
|
||||
Objects.requireNonNull(username);
|
||||
Objects.requireNonNull(password);
|
||||
Objects.requireNonNull(proxy);
|
||||
|
||||
if (!(serverBaseURL instanceof String) || !NetworkUtils.isURL((String) serverBaseURL))
|
||||
throw new IllegalArgumentException("Additional data should be server base url string for authlib injector accounts.");
|
||||
AuthlibInjectorServer server = (AuthlibInjectorServer) additionalData;
|
||||
|
||||
AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider((String) serverBaseURL), proxy),
|
||||
(String) serverBaseURL, injectorJarPathSupplier, username, null, null);
|
||||
AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl()), proxy),
|
||||
server, injectorJarPathSupplier, username, null, null);
|
||||
account.logInWithPassword(password, selector);
|
||||
return account;
|
||||
}
|
||||
@@ -49,7 +53,9 @@ public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjecto
|
||||
String apiRoot = tryCast(storage.get("serverBaseURL"), String.class)
|
||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have API root."));
|
||||
|
||||
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(apiRoot), proxy),
|
||||
apiRoot, injectorJarPathSupplier, username, session.getSelectedProfile().getId(), session);
|
||||
AuthlibInjectorServer server = serverLookup.apply(apiRoot);
|
||||
|
||||
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl()), proxy),
|
||||
server, injectorJarPathSupplier, username, session.getSelectedProfile().getId(), session);
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.auth.authlibinjector;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jackhuang.hmcl.util.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
public class AuthlibInjectorServer {
|
||||
|
||||
public static AuthlibInjectorServer fetchServerInfo(String url) throws IOException {
|
||||
try {
|
||||
JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(NetworkUtils.toURL(url)), JsonObject.class);
|
||||
String name = extractServerName(response).orElse(url);
|
||||
return new AuthlibInjectorServer(url, name);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new IOException("Malformed response", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<String> extractServerName(JsonObject response){
|
||||
return tryCast(response.get("meta"), JsonObject.class)
|
||||
.flatMap(meta -> tryCast(meta.get("serverName"), JsonPrimitive.class).map(JsonPrimitive::getAsString));
|
||||
}
|
||||
|
||||
private String url;
|
||||
private String name;
|
||||
|
||||
public AuthlibInjectorServer(String url, String name) {
|
||||
this.url = url;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return url + " (" + name + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return url.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (!(obj instanceof AuthlibInjectorServer))
|
||||
return false;
|
||||
AuthlibInjectorServer another = (AuthlibInjectorServer) obj;
|
||||
return this.url.equals(another.url);
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.auth.authlibinjector;
|
||||
|
||||
public class AuthlibInjectorServerInfo {
|
||||
private final String serverIp;
|
||||
private final String serverName;
|
||||
|
||||
public AuthlibInjectorServerInfo(String serverIp, String serverName) {
|
||||
this.serverIp = serverIp;
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return serverIp;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.auth.authlibinjector;
|
||||
|
||||
public class AuthlibInjectorServerResponse {
|
||||
|
||||
private final Meta meta;
|
||||
|
||||
public AuthlibInjectorServerResponse() {
|
||||
this(new Meta());
|
||||
}
|
||||
|
||||
public AuthlibInjectorServerResponse(Meta meta) {
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
public Meta getMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
public static class Meta {
|
||||
private final String serverName;
|
||||
|
||||
public Meta() {
|
||||
this("");
|
||||
}
|
||||
|
||||
public Meta(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,7 @@ public class DefaultLauncher extends Launcher {
|
||||
if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS)
|
||||
res.add("-Duser.home=" + options.getGameDir().getParent());
|
||||
|
||||
// Force using G1GC with its settings
|
||||
if (options.getJava().getParsedVersion() >= JavaVersion.JAVA_7) {
|
||||
res.add("-XX:+UnlockExperimentalVMOptions");
|
||||
res.add("-XX:+UseG1GC");
|
||||
@@ -103,6 +104,12 @@ public class DefaultLauncher extends Launcher {
|
||||
res.add("-XX:-OmitStackTraceInFastThrow");
|
||||
res.add("-Xmn128m");
|
||||
|
||||
// As 32-bit JVM allocate 320KB for stack by default rather than 64-bit version allocating 1MB,
|
||||
// causing Minecraft 1.13 crashed accounting for java.lang.StackOverflowError.
|
||||
if (options.getJava().getPlatform() == Platform.BIT_32) {
|
||||
res.add("-Xss1M");
|
||||
}
|
||||
|
||||
if (options.getMaxMemory() != null && options.getMaxMemory() > 0)
|
||||
res.add("-Xmx" + options.getMaxMemory() + "m");
|
||||
|
||||
|
||||
@@ -113,7 +113,10 @@ public final class CurseInstallTask extends Task {
|
||||
throw new IOException("Unable to delete mod file " + oldFile);
|
||||
}
|
||||
|
||||
dependencies.add(new CurseCompletionTask(dependencyManager, name));
|
||||
File root = repository.getVersionRoot(name);
|
||||
FileUtils.writeText(new File(root, "manifest.json"), Constants.GSON.toJson(manifest));
|
||||
|
||||
dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest));
|
||||
dependencies.add(new MinecraftInstanceTask<>(zipFile, manifest.getOverrides(), manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ package org.jackhuang.hmcl.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
import org.jackhuang.hmcl.game.Argument;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.RuledArgument;
|
||||
@@ -65,6 +70,9 @@ public final class Constants {
|
||||
javafx.application.Platform.runLater(s);
|
||||
};
|
||||
|
||||
// lazy loading
|
||||
public static final ObjectBinding<Image> DEFAULT_ICON = Bindings.createObjectBinding(() -> new Image("/assets/img/icon.png"));
|
||||
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
.enableComplexMapKeySerialization()
|
||||
.setPrettyPrinting()
|
||||
|
||||
Reference in new issue
Block a user