Crash and upgrade

This commit is contained in:
huangyuhui
2018-01-27 16:16:38 +08:00
parent 1ff36552e0
commit 2c7510fe5f
20 changed files with 293 additions and 58 deletions

View File

@@ -23,10 +23,14 @@ import javafx.application.Platform;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.upgrade.IUpgrader;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.*;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.logging.Level; import java.util.logging.Level;
@@ -44,8 +48,17 @@ public final class Main extends Application {
} }
public static void main(String[] args) { public static void main(String[] args) {
NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher"); Thread.setDefaultUncaughtExceptionHandler(new CrashReporter());
// NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher");
Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER; Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER;
IUpgrader.NOW_UPGRADER.parseArguments(VersionNumber.asVersion(VERSION), Arrays.asList(args));
Logging.LOG.info("*** " + TITLE + " ***");
UPDATE_CHECKER.process(false)
.then(Task.of(Controllers::showUpdate))
.start();
launch(args); launch(args);
} }
@@ -99,4 +112,8 @@ public final class Main extends Application {
public static final String TITLE = NAME + " " + VERSION; public static final String TITLE = NAME + " " + VERSION;
public static final File APPDATA = getWorkingDirectory("hmcl"); public static final File APPDATA = getWorkingDirectory("hmcl");
public static final ResourceBundle RESOURCE_BUNDLE = Settings.INSTANCE.getLocale().getResourceBundle(); public static final ResourceBundle RESOURCE_BUNDLE = Settings.INSTANCE.getLocale().getResourceBundle();
public static final UpdateChecker UPDATE_CHECKER = new UpdateChecker(VersionNumber.asVersion(VERSION), "hmcl");
public static final String CONTACT = "http://huangyuhui.duapp.com/hmcl.php";
public static final String PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
} }

View File

@@ -61,6 +61,7 @@ public final class Locales {
} }
public static SupportedLocale getLocaleByName(String name) { public static SupportedLocale getLocaleByName(String name) {
if (name == null) return DEFAULT;
switch (name.toLowerCase()) { switch (name.toLowerCase()) {
case "en": return EN; case "en": return EN;
case "zh": return ZH; case "zh": return ZH;

View File

@@ -68,6 +68,7 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
FXUtils.setValidateWhileTextChanged(txtUsername); FXUtils.setValidateWhileTextChanged(txtUsername);
FXUtils.setValidateWhileTextChanged(txtPassword); FXUtils.setValidateWhileTextChanged(txtPassword);
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"));
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> { cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
txtPassword.setVisible(newValue.intValue() != 0); txtPassword.setVisible(newValue.intValue() != 0);
}); });

View File

@@ -139,4 +139,8 @@ public final class Controllers {
public static void navigate(Node node) { public static void navigate(Node node) {
decorator.showPage(node); decorator.showPage(node);
} }
public static void showUpdate() {
}
} }

View File

@@ -0,0 +1,72 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Main;
/**
* @author huangyuhui
*/
public class CrashWindow extends Stage {
private Label lblCrash = new Label();
private Button btnContact = new Button();
private TextArea textArea = new TextArea();
public CrashWindow(String text) {
if (Main.UPDATE_CHECKER.isOutOfDate())
lblCrash.setText(Main.i18n("launcher.crash_out_dated"));
else
lblCrash.setText(Main.i18n("launcher.crash"));
lblCrash.setWrapText(true);
textArea.setText(text);
textArea.setEditable(false);
btnContact.setText(Main.i18n("launcher.contact"));
btnContact.setOnMouseClicked(event -> FXUtils.openLink(Main.CONTACT));
HBox box = new HBox();
box.setStyle("-fx-padding: 8px;");
box.getChildren().add(btnContact);
box.setAlignment(Pos.CENTER_RIGHT);
BorderPane pane = new BorderPane();
StackPane stackPane = new StackPane();
stackPane.setStyle("-fx-padding: 8px;");
stackPane.getChildren().add(lblCrash);
pane.setTop(stackPane);
pane.setCenter(textArea);
pane.setBottom(box);
Scene scene = new Scene(pane, 800, 480);
setScene(scene);
getIcons().add(new Image("/assets/img/icon.png"));
setTitle(Main.i18n("message.error"));
}
}

View File

@@ -118,6 +118,8 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
private JFXButton btnMax; private JFXButton btnMax;
@FXML @FXML
private JFXButton btnClose; private JFXButton btnClose;
@FXML
private HBox updatePane;
public Decorator(Stage primaryStage, Node mainPage, String title) { public Decorator(Stage primaryStage, Node mainPage, String title) {
this(primaryStage, mainPage, title, true, true); this(primaryStage, mainPage, title, true, true);
@@ -131,6 +133,9 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
FXUtils.loadFXML(this, "/assets/fxml/decorator.fxml"); FXUtils.loadFXML(this, "/assets/fxml/decorator.fxml");
updatePane.setCursor(Cursor.HAND);
updatePane.setOnMouseClicked(event -> Main.UPDATE_CHECKER.checkOutdate());
primaryStage.initStyle(StageStyle.UNDECORATED); primaryStage.initStyle(StageStyle.UNDECORATED);
btnClose.setGraphic(close); btnClose.setGraphic(close);
btnMin.setGraphic(minus); btnMin.setGraphic(minus);
@@ -374,6 +379,10 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
} }
} }
public void showUpdate() {
updatePane.setVisible(true);
}
private void setContent(Node content, AnimationProducer animation) { private void setContent(Node content, AnimationProducer animation) {
animationHandler.setContent(content, animation); animationHandler.setContent(content, animation);

View File

@@ -48,6 +48,7 @@ import org.jackhuang.hmcl.util.OperatingSystem;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -203,6 +204,27 @@ public final class FXUtils {
} }
} }
/**
* Open URL by java.awt.Desktop
*
* @param link null is allowed but will be ignored
*/
public static void openLink(String link) {
if (link == null)
return;
try {
java.awt.Desktop.getDesktop().browse(new URI(link));
} catch (Throwable e) {
if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX)
try {
Runtime.getRuntime().exec(new String[] { "/usr/bin/open", link });
} catch (IOException ex) {
Logging.LOG.log(Level.WARNING, "Unable to open link: " + link, ex);
}
Logging.LOG.log(Level.WARNING, "Failed to open link: " + link, e);
}
}
public static void bindInt(JFXTextField textField, Property<?> property) { public static void bindInt(JFXTextField textField, Property<?> property) {
textField.textProperty().unbind(); textField.textProperty().unbind();
textField.textProperty().bindBidirectional((Property<Integer>) property, SafeIntStringConverter.INSTANCE); textField.textProperty().bindBidirectional((Property<Integer>) property, SafeIntStringConverter.INSTANCE);

View File

@@ -40,7 +40,7 @@ import java.util.Objects;
public final class LeftPaneController { public final class LeftPaneController {
private final AdvancedListBox leftPane; private final AdvancedListBox leftPane;
private final VBox profilePane = new VBox(); private final VBox profilePane = new VBox();
private final VersionListItem accountItem = new VersionListItem("No Account", "unknown"); private final VersionListItem accountItem = new VersionListItem("", "");
public LeftPaneController(AdvancedListBox leftPane) { public LeftPaneController(AdvancedListBox leftPane) {
this.leftPane = leftPane; this.leftPane = leftPane;
@@ -67,8 +67,8 @@ public final class LeftPaneController {
FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), it -> { FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), it -> {
if (it == null) { if (it == null) {
accountItem.setVersionName("mojang@mojang.com"); accountItem.setVersionName(Main.i18n("account.missing"));
accountItem.setGameVersion("Yggdrasil"); accountItem.setGameVersion(Main.i18n("message.unknown"));
} else { } else {
accountItem.setVersionName(it.getUsername()); accountItem.setVersionName(it.getUsername());
accountItem.setGameVersion(AccountsPage.accountType(it)); accountItem.setGameVersion(AccountsPage.accountType(it));

View File

@@ -48,7 +48,6 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
@@ -104,13 +103,13 @@ public final class MainPage extends StackPane implements DecoratorPage {
item.setVersionName(version); item.setVersionName(version);
item.setOnLaunchButtonClicked(e -> { item.setOnLaunchButtonClicked(e -> {
if (Settings.INSTANCE.getSelectedAccount() == null) if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.no_Player007")); Controllers.dialog(Main.i18n("login.empty_username"));
else else
LauncherHelper.INSTANCE.launch(version, null); LauncherHelper.INSTANCE.launch(version, null);
}); });
item.setOnScriptButtonClicked(e -> { item.setOnScriptButtonClicked(e -> {
if (Settings.INSTANCE.getSelectedAccount() == null) if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.no_Player007")); Controllers.dialog(Main.i18n("login.empty_username"));
else { else {
FileChooser chooser = new FileChooser(); FileChooser chooser = new FileChooser();
chooser.setInitialDirectory(profile.getRepository().getRunDirectory(version)); chooser.setInitialDirectory(profile.getRepository().getRunDirectory(version));

View File

@@ -106,7 +106,7 @@ public class AppDataUpgrader extends IUpgrader {
String hash = null; String hash = null;
if (map.containsKey("jarsha1")) if (map.containsKey("jarsha1"))
hash = map.get("jarsha1"); hash = map.get("jarsha1");
Controllers.dialog(Main.i18n("ui.message.downloading")); Controllers.dialog(Main.i18n("message.downloading"));
if (new AppDataUpgraderJarTask(NetworkUtils.toURL(map.get("jar")), version.toString(), hash).test()) { if (new AppDataUpgraderJarTask(NetworkUtils.toURL(map.get("jar")), version.toString(), hash).test()) {
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderJarTask.getSelf(version.toString()).getAbsolutePath()) new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderJarTask.getSelf(version.toString()).getAbsolutePath())
.directory(new File("").getAbsoluteFile()).start(); .directory(new File("").getAbsoluteFile()).start();
@@ -121,7 +121,7 @@ public class AppDataUpgrader extends IUpgrader {
String hash = null; String hash = null;
if (map.containsKey("packsha1")) if (map.containsKey("packsha1"))
hash = map.get("packsha1"); hash = map.get("packsha1");
Controllers.dialog(Main.i18n("ui.message.downloading")); Controllers.dialog(Main.i18n("message.downloading"));
if (new AppDataUpgraderPackGzTask(NetworkUtils.toURL(map.get("pack")), version.toString(), hash).test()) { if (new AppDataUpgraderPackGzTask(NetworkUtils.toURL(map.get("pack")), version.toString(), hash).test()) {
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderPackGzTask.getSelf(version.toString()).getAbsolutePath()) new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderPackGzTask.getSelf(version.toString()).getAbsolutePath())
.directory(new File("").getAbsoluteFile()).start(); .directory(new File("").getAbsoluteFile()).start();
@@ -132,14 +132,14 @@ public class AppDataUpgrader extends IUpgrader {
Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex); Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex);
} }
else { else {
String url = URL_PUBLISH; String url = Main.PUBLISH;
if (map != null) if (map != null)
if (map.containsKey(OperatingSystem.CURRENT_OS.getCheckedName())) if (map.containsKey(OperatingSystem.CURRENT_OS.getCheckedName()))
url = map.get(OperatingSystem.CURRENT_OS.getCheckedName()); url = map.get(OperatingSystem.CURRENT_OS.getCheckedName());
else if (map.containsKey(OperatingSystem.UNKNOWN.getCheckedName())) else if (map.containsKey(OperatingSystem.UNKNOWN.getCheckedName()))
url = map.get(OperatingSystem.UNKNOWN.getCheckedName()); url = map.get(OperatingSystem.UNKNOWN.getCheckedName());
if (url == null) if (url == null)
url = URL_PUBLISH; url = Main.PUBLISH;
try { try {
java.awt.Desktop.getDesktop().browse(new URI(url)); java.awt.Desktop.getDesktop().browse(new URI(url));
} catch (URISyntaxException | IOException e) { } catch (URISyntaxException | IOException e) {
@@ -240,7 +240,4 @@ public class AppDataUpgrader extends IUpgrader {
} }
} }
public static final String URL_PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
public static final String URL_CONTACT = "http://huangyuhui.duapp.com/hmcl.php";
} }

View File

@@ -51,7 +51,7 @@ public class NewFileUpgrader extends IUpgrader {
URL url = requestDownloadLink(); URL url = requestDownloadLink();
if (url == null) return; if (url == null) return;
File newf = new File(url.getFile()); File newf = new File(url.getFile());
Controllers.dialog(Main.i18n("ui.message.downloading")); Controllers.dialog(Main.i18n("message.downloading"));
if (new FileDownloadTask(url, newf).test()) { if (new FileDownloadTask(url, newf).test()) {
try { try {
new ProcessBuilder(newf.getCanonicalPath(), "--removeOldLauncher", getRealPath()) new ProcessBuilder(newf.getCanonicalPath(), "--removeOldLauncher", getRealPath())

View File

@@ -75,7 +75,7 @@ public final class UpdateChecker {
} }
if (value == null) { if (value == null) {
Logging.LOG.warning("Failed to check update..."); Logging.LOG.warning("Unable to check update...");
if (showMessage) if (showMessage)
MessageBox.show(Main.i18n("update.failed")); MessageBox.show(Main.i18n("update.failed"));
} else if (base.compareTo(value) < 0) } else if (base.compareTo(value) < 0)

View File

@@ -0,0 +1,121 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
import javafx.application.Platform;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.ui.CrashWindow;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.Level;
/**
* @author huangyuhui
*/
public class CrashReporter implements Thread.UncaughtExceptionHandler {
private static final HashMap<String, String> SOURCE = new HashMap<String, String>() {
{
put("UnsatisfiedLinkError", Main.i18n("crash.user_fault"));
put("java.lang.NoClassDefFoundError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.VerifyError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.NoSuchMethodError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.IncompatibleClassChangeError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.ClassFormatError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.OutOfMemoryError", "FUCKING MEMORY LIMIT!");
put("Trampoline", Main.i18n("launcher.update_java"));
put("NoSuchAlgorithmException", "Has your operating system been installed completely or is a ghost system?");
}
};
private boolean checkThrowable(Throwable e) {
String s = StringUtils.getStackTrace(e);
for (HashMap.Entry<String, String> entry : SOURCE.entrySet())
if (s.contains(entry.getKey())) {
if (StringUtils.isNotBlank(entry.getValue())) {
String info = entry.getValue();
Logging.LOG.severe(info);
try {
MessageBox.show(info);
} catch (Throwable t) {
Logging.LOG.log(Level.SEVERE, "Unable to show message", t);
}
}
return false;
}
return true;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
String s = StringUtils.getStackTrace(e);
if (!s.contains("org.jackhuang"))
return;
try {
StringBuilder builder = new StringBuilder();
builder.append("---- Hello Minecraft! Crash Report ----\n");
builder.append(" Version: " + Main.VERSION + "\n");
builder.append(" Time: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("\n");
builder.append(" Thread: ").append(t.toString()).append("\n");
builder.append("\n Content: \n ");
builder.append(s).append("\n\n");
builder.append("-- System Details --\n");
builder.append(" Operating System: ").append(OperatingSystem.SYSTEM_VERSION).append("\n");
builder.append(" Java Version: ").append(System.getProperty("java.version")).append(", ").append(System.getProperty("java.vendor")).append("\n");
builder.append(" Java VM Version: ").append(System.getProperty("java.vm.name")).append(" (").append(System.getProperty("java.vm.info")).append("), ").append(System.getProperty("java.vm.vendor")).append("\n");
String text = builder.toString();
Logging.LOG.log(Level.SEVERE, text);
if (checkThrowable(e) && !System.getProperty("java.vm.name").contains("OpenJDK")) {
Platform.runLater(() -> new CrashWindow(text).show());
if (!Main.UPDATE_CHECKER.isOutOfDate())
reportToServer(text, s);
}
} catch (Throwable ex) {
Logging.LOG.log(Level.SEVERE, "Unable to caught exception", ex);
Logging.LOG.log(Level.SEVERE, "There is the original exception", e);
}
}
private static final HashSet<String> THROWABLE_SET = new HashSet<>();
private void reportToServer(final String text, String stacktrace) {
if (THROWABLE_SET.contains(stacktrace) || stacktrace.contains("Font") || stacktrace.contains("InternalError"))
return;
THROWABLE_SET.add(stacktrace);
Thread t = new Thread(() -> {
HashMap<String, String> map = new HashMap<>();
map.put("crash_report", text);
map.put("version", Main.VERSION);
try {
NetworkUtils.doPost(NetworkUtils.toURL("http://huangyuhui.duapp.com/hmcl/crash.php"), map);
} catch (IOException ex) {
Logging.LOG.log(Level.SEVERE, "Unable to post HMCL server.", ex);
}
});
t.setDaemon(true);
t.start();
}
}

View File

@@ -26,7 +26,7 @@
<StackPane> <StackPane>
<JFXDialogLayout> <JFXDialogLayout>
<heading> <heading>
<Label>Create a new account</Label> <Label text="%account.create" />
</heading> </heading>
<body> <body>
<GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;"> <GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;">
@@ -34,24 +34,17 @@
<ColumnConstraints maxWidth="100" /> <ColumnConstraints maxWidth="100" />
<ColumnConstraints /> <ColumnConstraints />
</columnConstraints> </columnConstraints>
<Label text="Type" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="0" /> <Label text="%account.methods" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0"> <JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Offline Account"/>
<String fx:value="Online Account" />
</FXCollections>
</items>
</JFXComboBox>
<JFXTextField fx:id="txtUsername" promptText="Username" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2"> <JFXTextField fx:id="txtUsername" promptText="%account.username" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2">
<validators> <validators>
<RequiredFieldValidator message="%input.not_empty"> <RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator> </RequiredFieldValidator>
</validators> </validators>
</JFXTextField> </JFXTextField>
<JFXPasswordField fx:id="txtPassword" promptText="%login.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2"> <JFXPasswordField fx:id="txtPassword" promptText="%account.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2">
<validators> <validators>
<RequiredFieldValidator message="%input.not_empty"> <RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator> </RequiredFieldValidator>

View File

@@ -29,6 +29,11 @@
</center> </center>
<bottom> <bottom>
<BorderPane fx:id="menuBottomBar"> <BorderPane fx:id="menuBottomBar">
<left>
<HBox fx:id="updatePane" visible="false" style="-fx-background-color: red;" alignment="CENTER_LEFT">
<Label text="%update.found" style="-fx-text-fill: white; -fx-font-size: 16px;" />
</HBox>
</left>
<right> <right>
<JFXButton fx:id="addMenuButton" styleClass="toggle-icon4"> <JFXButton fx:id="addMenuButton" styleClass="toggle-icon4">
<graphic> <graphic>

View File

@@ -103,7 +103,7 @@
<BorderPane> <BorderPane>
<left> <left>
<Label text="%mainwindow.show_log"/> <Label text="%settings.show_log"/>
</left> </left>
<right> <right>
<JFXToggleButton fx:id="chkShowLogs" size="7"/> <JFXToggleButton fx:id="chkShowLogs" size="7"/>

View File

@@ -15,7 +15,7 @@
<body> <body>
<VBox spacing="15" style="-fx-padding: 15 0 0 0;"> <VBox spacing="15" style="-fx-padding: 15 0 0 0;">
<Label fx:id="lblUsername" /> <Label fx:id="lblUsername" />
<JFXPasswordField fx:id="txtPassword" promptText="%login.password" labelFloat="true"> <JFXPasswordField fx:id="txtPassword" promptText="%account.password" labelFloat="true">
<validators> <validators>
<RequiredFieldValidator message="Input Required!"> <RequiredFieldValidator message="Input Required!">
</RequiredFieldValidator> </RequiredFieldValidator>

View File

@@ -50,7 +50,7 @@ crash.minecraft=Minecraft has crashed!
login.choose_charactor=Please choose the character you want login.choose_charactor=Please choose the character you want
account.failed.no_charactor=No character in this account. account.failed.no_charactor=No character in this account.
login.failed=Failed to login login.failed=Failed to login
login.no_Player007=You have not set a username! login.empty_username=You have not set a username!
account.failed.wrong_password=Incorrect password or username account.failed.wrong_password=Incorrect password or username
login.invalid_username=Invalid username login.invalid_username=Invalid username
login.invalid_uuid_and_username=Invalid UUID and username login.invalid_uuid_and_username=Invalid UUID and username
@@ -58,9 +58,9 @@ account.failed.invalid_password=Invalid password
account.failed.invalid_access_token=Invalid Access Token account.failed.invalid_access_token=Invalid Access Token
login.changed_client_token=The server response has changed the client token. login.changed_client_token=The server response has changed the client token.
input.email=The username must be an e-mail. input.email=The username must be an e-mail.
account.methods=Login account.methods=Login Type
account.username=Name account.username=Name
login.account=Email account.email=Email
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 log in.
login.no_valid_character=No valid character, please visit skinme.cc and create your own character. login.no_valid_character=No valid character, please visit skinme.cc and create your own character.
@@ -101,9 +101,6 @@ crash.headless=If your OS is Linux, please use Oracle JDK instead of OpenJDK, or
crash.NoClassDefFound=Please check "HMCL" software is complete. crash.NoClassDefFound=Please check "HMCL" software is complete.
crash.error=Minecraft has crashed. crash.error=Minecraft has crashed.
crash.main_class_not_found=Main Class is not found, may be your mc has been broken.
crash.class_path_wrong=Maybe the launch script is malformed.
profile.new_name=New Profile Name: profile.new_name=New Profile Name:
profile.copy_from=Copy From: profile.copy_from=Copy From:
profile.new=New Config profile.new=New Config
@@ -130,12 +127,12 @@ button.ok=OK
button.yes=Yes button.yes=Yes
button.no=No button.no=No
login.password=Password account.password=Password
profile=Profile profile=Profile
login.enter_username=Please enter your name. login.enter_username=Please enter your name.
login.enter_password=Please enter your password. login.enter_password=Please enter your password.
ui.message.downloading=Downloading... message.downloading=Downloading...
profile.remove=Sure to remove profile %s? profile.remove=Sure to remove profile %s?
launcher.update_java=Please upgrade your Java. launcher.update_java=Please upgrade your Java.
launcher.open_jdk=We have found that you started this application using OpenJDK, which will cause so many troubles drawing the UI. We suggest you using Oracle JDK instead. launcher.open_jdk=We have found that you started this application using OpenJDK, which will cause so many troubles drawing the UI. We suggest you using Oracle JDK instead.
@@ -263,13 +260,11 @@ advancedsettings.precall_command=Precalling command(will be executed before game
advancedsettings.server_ip=Server Host advancedsettings.server_ip=Server Host
advancedsettings.dont_check_game_completeness=Don't check game completeness advancedsettings.dont_check_game_completeness=Don't check game completeness
mainwindow.show_log=Show Logs settings.show_log=Show Logs
version.launch_script=Make Launching Script. version.launch_script=Make Launching Script.
version.launch_script.failed=Failed to make script. version.launch_script.failed=Failed to make script.
version.launch_script.save=Save the launch script version.launch_script.save=Save the launch script
version.launch_script.success=Finished script creation, %s. version.launch_script.success=Finished script creation, %s.
mainwindow.no_version=No version found. Switch to Game Downloads Tab?
launcher.about=About Author\nMinecraft Forum ID: klkl6523\nCopyright (c) 2013 huangyuhui\nOpened source under GPL v3 license:http://github.com/huanghongxun/HMCL/\nThis software used project Gson which is under Apache License 2.0, thanks contributors. launcher.about=About Author\nMinecraft Forum ID: klkl6523\nCopyright (c) 2013 huangyuhui\nOpened source under GPL v3 license:http://github.com/huanghongxun/HMCL/\nThis software used project Gson which is under Apache License 2.0, thanks contributors.
launcher_settings.download_source=Download Source launcher_settings.download_source=Download Source
launcher.background_location=Background Location launcher.background_location=Background Location
@@ -285,7 +280,6 @@ launcher.background_tooltip=The laucher uses a default background.\nIf you use c
launcher.update_launcher=Check for update launcher.update_launcher=Check for update
launcher_settings.theme=Theme launcher_settings.theme=Theme
launcher_settings.proxy=Proxy launcher_settings.proxy=Proxy
launcher_settings.decorated=Enable system window border(in order to fix the problem that the ui become all gray in Linux OS)
launcher.modpack=Documentations for modpacks. launcher.modpack=Documentations for modpacks.
launcher_settings.language=Language launcher_settings.language=Language
launcher.restart=Options will be in operations only if restart this app. launcher.restart=Options will be in operations only if restart this app.
@@ -337,11 +331,11 @@ update.no_browser=Cannot open any browser. The link has been copied to the clipb
update.should_open_link=Are you willing to update the launcher? update.should_open_link=Are you willing to update the launcher?
update.newest_version=Newest version: update.newest_version=Newest version:
update.failed=Failed to check for updates. update.failed=Failed to check for updates.
update.found=(Found Update!) update.found=Found Update!
logwindow.terminate_game=Terminate Game logwindow.terminate_game=Terminate Game
logwindow.title=Log logwindow.title=Log
logwindow.contact=Contact Us launcher.contact=Contact Us
logwindow.show_lines=Show Lines logwindow.show_lines=Show Lines
logwindow.search=Search logwindow.search=Search
@@ -373,7 +367,7 @@ wizard.steps=Steps
lang=English lang=English
lang.default=Belong to OS language. lang.default=Belong to OS language.
account.create= account.create=Create a new account
input.not_empty=Input Requrired! input.not_empty=Input Requrired!
account=Accounts account=Accounts
install.new_game=Install a New Game install.new_game=Install a New Game
@@ -412,3 +406,5 @@ modpack.invalid=Invalid modpack file.
modpack.update=Upgrading game modpack.update=Upgrading game
message.confirm=Confirm message.confirm=Confirm
version.forbidden_name=Forbidden name, do not use this. version.forbidden_name=Forbidden name, do not use this.
account.missing=Missing account
message.unknown=Unknown

View File

@@ -15,6 +15,7 @@
# along with this program. If not, see {http://www.gnu.org/licenses/}. # along with this program. If not, see {http://www.gnu.org/licenses/}.
# #
#author: huangyuhui #author: huangyuhui
message.downloading=正在下载...
launch.failed=启动失败 launch.failed=启动失败
launch.failed_creating_process=启动失败在创建新进程时发生错误可能是Java路径错误。 launch.failed_creating_process=启动失败在创建新进程时发生错误可能是Java路径错误。
launch.failed.sh_permission=为启动文件添加权限时发生错误 launch.failed.sh_permission=为启动文件添加权限时发生错误
@@ -50,7 +51,7 @@ crash.minecraft=Minecraft崩溃了请认真阅读建议。
login.choose_charactor=请选择您要使用的角色 login.choose_charactor=请选择您要使用的角色
account.failed.no_charactor=该帐号没有角色 account.failed.no_charactor=该帐号没有角色
login.failed=登录失败: login.failed=登录失败:
login.no_Player007=你还未设置用户名! login.empty_username=你还未设置用户名!
account.failed.wrong_password=可能是您的用户名或密码错误 account.failed.wrong_password=可能是您的用户名或密码错误
login.invalid_username=无效的用户名 login.invalid_username=无效的用户名
login.invalid_uuid_and_username=无效的UUID和用户名 login.invalid_uuid_and_username=无效的UUID和用户名
@@ -58,9 +59,9 @@ account.failed.invalid_password=无效的密码
account.failed.invalid_access_token=无效的访问令牌 account.failed.invalid_access_token=无效的访问令牌
login.changed_client_token=服务器回应已经修改客户端令牌 login.changed_client_token=服务器回应已经修改客户端令牌
input.email=用户名必须是邮箱 input.email=用户名必须是邮箱
account.methods=登录 account.methods=登录方式
account.username=用户名 account.username=用户名
login.account=邮箱 account.email=邮箱
account.failed.invalid_token=请尝试登出并重新输入密码登录 account.failed.invalid_token=请尝试登出并重新输入密码登录
login.no_valid_character=无有效的角色自行到skinme.cc登陆并创建角色 login.no_valid_character=无有效的角色自行到skinme.cc登陆并创建角色
@@ -101,9 +102,6 @@ crash.headless=如果您的操作系统是Linux请注意不要使用OpenJDK
crash.NoClassDefFound=请确认HMCL本体是否完整 crash.NoClassDefFound=请确认HMCL本体是否完整
crash.error=您的Minecraft崩溃了。 crash.error=您的Minecraft崩溃了。
crash.main_class_not_found=找不到主类可能是您的JSON文件填写错误。无法启动游戏。可以通过下载整合包解决问题。
crash.class_path_wrong=解析Class Path时出现错误此错误本不应该发生。可能是启动脚本错误请仔细检查启动脚本。
profile.new_name=新配置名: profile.new_name=新配置名:
profile.copy_from=复制配置: profile.copy_from=复制配置:
profile.new=新建配置 profile.new=新建配置
@@ -130,7 +128,7 @@ button.ok=确定
button.yes= button.yes=
button.no= button.no=
login.password=密码 account.password=密码
profile=配置 profile=配置
login.enter_username=请输入您的账号 login.enter_username=请输入您的账号
@@ -263,13 +261,11 @@ advancedsettings.precall_command=启动前执行命令(不必填写,将在游
advancedsettings.server_ip=直入服务器ip地址(不必填写,启动游戏后直接进入对应服务器) advancedsettings.server_ip=直入服务器ip地址(不必填写,启动游戏后直接进入对应服务器)
advancedsettings.dont_check_game_completeness=不检查游戏完整性 advancedsettings.dont_check_game_completeness=不检查游戏完整性
mainwindow.show_log=查看日志 settings.show_log=查看日志
version.launch_script=生成启动脚本 version.launch_script=生成启动脚本
version.launch_script.failed=生成启动脚本失败 version.launch_script.failed=生成启动脚本失败
version.launch_script.save=保存启动脚本 version.launch_script.save=保存启动脚本
version.launch_script.success=启动脚本已生成完毕: %s. version.launch_script.success=启动脚本已生成完毕: %s.
mainwindow.no_version=未找到任何版本,是否进入游戏下载?
launcher.about=默认背景图感谢gamerteam提供。\n关于作者\n百度IDhuanghongxun20\nmcbbshuanghongxun\nMinecraft Forum ID: klkl6523\n欢迎提交Bug哦\nCopyright (c) 2013-2017 huangyuhui.\n免责声明Minecraft软件版权归Mojang AB所有使用本软件产生的版权问题本软件制作方概不负责。\n本启动器在GPLv3协议下开源:https://github.com/huanghongxun/HMCL/ ,感谢issues和pull requests贡献者\n本软件使用了基于Apache License 2.0的Gson项目感谢贡献者。 launcher.about=默认背景图感谢gamerteam提供。\n关于作者\n百度IDhuanghongxun20\nmcbbshuanghongxun\nMinecraft Forum ID: klkl6523\n欢迎提交Bug哦\nCopyright (c) 2013-2017 huangyuhui.\n免责声明Minecraft软件版权归Mojang AB所有使用本软件产生的版权问题本软件制作方概不负责。\n本启动器在GPLv3协议下开源:https://github.com/huanghongxun/HMCL/ ,感谢issues和pull requests贡献者\n本软件使用了基于Apache License 2.0的Gson项目感谢贡献者。
launcher_settings.download_source=下载源 launcher_settings.download_source=下载源
launcher.background_location=背景地址 launcher.background_location=背景地址
@@ -285,7 +281,6 @@ launcher.background_tooltip=启动器默认使用自带的背景\n如果当前
launcher.update_launcher=检查更新 launcher.update_launcher=检查更新
launcher_settings.theme=主题 launcher_settings.theme=主题
launcher_settings.proxy=代理 launcher_settings.proxy=代理
launcher_settings.decorated=启用窗口边框(Linux下可解决程序界面全灰问题)
launcher.modpack=整合包作者帮助 launcher.modpack=整合包作者帮助
launcher_settings.language=语言 launcher_settings.language=语言
launcher.restart=本界面选项需要重启本启动器生效 launcher.restart=本界面选项需要重启本启动器生效
@@ -337,11 +332,11 @@ update.no_browser=无法打开浏览器,网址已经复制到剪贴板了,
update.should_open_link=是否更新? update.should_open_link=是否更新?
update.newest_version=最新版本为: update.newest_version=最新版本为:
update.failed=检查更新失败 update.failed=检查更新失败
update.found=(发现更新!) update.found=发现更新
logwindow.terminate_game=结束游戏进程 logwindow.terminate_game=结束游戏进程
logwindow.title=日志 logwindow.title=日志
logwindow.contact=联系我们 launcher.contact=联系我们
logwindow.show_lines=显示行数 logwindow.show_lines=显示行数
logwindow.search=查找 logwindow.search=查找
@@ -412,3 +407,5 @@ modpack.invalid=无效的整合包升级文件,可能是下载时出现问题
modpack.update=正在升级整合包 modpack.update=正在升级整合包
message.confirm=提示 message.confirm=提示
version.forbidden_name=此版本名称不受支持,请换一个名字 version.forbidden_name=此版本名称不受支持,请换一个名字
account.missing=没有账户
message.unknown=未知

View File

@@ -283,6 +283,7 @@ public final class Lang {
public static Integer toIntOrNull(Object string) { public static Integer toIntOrNull(Object string) {
try { try {
if (string == null) return null;
return Integer.parseInt(string.toString()); return Integer.parseInt(string.toString());
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return null; return null;