mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2024-12-27 07:10:41 +08:00
upgrader
This commit is contained in:
parent
4b65d4da06
commit
8e6b56b2d6
@ -15,19 +15,31 @@
|
||||
* 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
|
||||
package org.jackhuang.hmcl.event;
|
||||
|
||||
import com.jfoenix.validation.base.ValidatorBase
|
||||
import javafx.scene.control.TextInputControl
|
||||
import org.jackhuang.hmcl.util.VersionNumber;
|
||||
|
||||
/**
|
||||
* @param validator return true if the input string is valid.
|
||||
*
|
||||
* Result: Deny if do not upgrade HMCL.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class Validator(val validator: (String) -> Boolean) : ValidatorBase() {
|
||||
override fun eval() {
|
||||
if (this.srcControl.get() is TextInputControl) {
|
||||
val text = (srcControl.get() as TextInputControl).text
|
||||
hasErrors.set(!validator(text))
|
||||
}
|
||||
public final class OutOfDateEvent extends Event {
|
||||
private final VersionNumber version;
|
||||
|
||||
public OutOfDateEvent(Object source, VersionNumber version) {
|
||||
super(source);
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
public VersionNumber getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasResult() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -15,15 +15,21 @@
|
||||
* 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.game
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.auth.MultiCharacterSelector
|
||||
import org.jackhuang.hmcl.auth.NoSelectedCharacterException
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.MultiCharacterSelector;
|
||||
import org.jackhuang.hmcl.auth.NoSelectedCharacterException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||
|
||||
object HMCLMultiCharacterSelector : MultiCharacterSelector {
|
||||
override fun select(account: Account, names: MutableList<GameProfile>): GameProfile {
|
||||
return names.firstOrNull() ?: throw NoSelectedCharacterException(account)
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class HMCLMultiCharacterSelector implements MultiCharacterSelector {
|
||||
@Override
|
||||
public GameProfile select(Account account, List<GameProfile> names) throws NoSelectedCharacterException {
|
||||
return names.stream().findFirst().orElseThrow(() -> new NoSelectedCharacterException(account));
|
||||
}
|
||||
}
|
||||
}
|
@ -15,10 +15,24 @@
|
||||
* 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.setting
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
enum class EnumGameDirectory {
|
||||
/**
|
||||
* Determines where game runs in and game files such as mods.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public enum EnumGameDirectory {
|
||||
/**
|
||||
* .minecraft
|
||||
*/
|
||||
ROOT_FOLDER,
|
||||
/**
|
||||
* .minecraft/versions/<version name>
|
||||
*/
|
||||
VERSION_FOLDER,
|
||||
/**
|
||||
* user customized directory.
|
||||
*/
|
||||
CUSTOM
|
||||
}
|
@ -15,13 +15,13 @@
|
||||
* 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.setting
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
/**
|
||||
* The visibility of launcher.
|
||||
* @author huangyuhui
|
||||
*/
|
||||
enum class LauncherVisibility {
|
||||
public enum LauncherVisibility {
|
||||
|
||||
/**
|
||||
* Close the launcher anyway when the game process created even if failed to
|
||||
@ -44,4 +44,4 @@ enum class LauncherVisibility {
|
||||
* Hide the launcher and reopen it when game closes.
|
||||
*/
|
||||
HIDE_AND_REOPEN
|
||||
}
|
||||
}
|
@ -15,14 +15,14 @@
|
||||
* 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.animation
|
||||
package org.jackhuang.hmcl.ui.animation;
|
||||
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.util.Duration
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.util.Duration;
|
||||
|
||||
interface AnimationHandler {
|
||||
val snapshot: Node
|
||||
val duration: Duration
|
||||
val view: Pane
|
||||
}
|
||||
public interface AnimationHandler {
|
||||
Node getSnapshot();
|
||||
Duration getDuration();
|
||||
Pane getView();
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.construct;
|
||||
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import org.jackhuang.hmcl.MainKt;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class MessageBox {
|
||||
private MessageBox() {
|
||||
}
|
||||
|
||||
private static final String TITLE = MainKt.i18n("message.info");
|
||||
|
||||
/**
|
||||
* User Operation: Yes
|
||||
*/
|
||||
public static final int YES_OPTION = 0;
|
||||
|
||||
/**
|
||||
* User Operation: No
|
||||
*/
|
||||
public static final int NO_OPTION = 1;
|
||||
|
||||
/**
|
||||
* User Operation: Cancel
|
||||
*/
|
||||
public static final int CANCEL_OPTION = 2;
|
||||
|
||||
/**
|
||||
* User Operation: OK
|
||||
*/
|
||||
public static final int OK_OPTION = 0;
|
||||
|
||||
/**
|
||||
* User Operation: Closed Message Box
|
||||
*/
|
||||
public static final int CLOSED_OPTION = -1;
|
||||
|
||||
/**
|
||||
* Buttons: Yes No
|
||||
*/
|
||||
public static final int YES_NO_OPTION = 10;
|
||||
/**
|
||||
* Buttons: Yes No Cancel
|
||||
*/
|
||||
public static final int YES_NO_CANCEL_OPTION = 11;
|
||||
/**
|
||||
* Buttons: OK Cancel
|
||||
*/
|
||||
public static final int OK_CANCEL_OPTION = 12;
|
||||
|
||||
/**
|
||||
* Message Box Type: Error
|
||||
*/
|
||||
public static final int ERROR_MESSAGE = 0;
|
||||
/**
|
||||
* Message Box Type: Info
|
||||
*/
|
||||
public static final int INFORMATION_MESSAGE = 1;
|
||||
/**
|
||||
* Message Box Type: Warning
|
||||
*/
|
||||
public static final int WARNING_MESSAGE = 2;
|
||||
/**
|
||||
* Message Box Type: Question
|
||||
*/
|
||||
public static final int QUESTION_MESSAGE = 3;
|
||||
/**
|
||||
* Message Box Type: Plain
|
||||
*/
|
||||
public static final int PLAIN_MESSAGE = -1;
|
||||
|
||||
|
||||
public static void show(String message) {
|
||||
show(message, TITLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show MsgBox with title and options
|
||||
*
|
||||
* @param message The Message
|
||||
* @param title The title of MsgBox.
|
||||
* @return user operation.
|
||||
*/
|
||||
public static void show(String message, String title) {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(title);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
public static int confirm(String message, String title) {
|
||||
return confirm(message, title, -1);
|
||||
}
|
||||
|
||||
public static int confirm(String message, int option) {
|
||||
return confirm(message, TITLE, option);
|
||||
}
|
||||
|
||||
public static int confirm(String message, String title, int option) {
|
||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(title);
|
||||
alert.setContentText(message);
|
||||
switch (option) {
|
||||
case YES_NO_OPTION:
|
||||
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO);
|
||||
break;
|
||||
case YES_NO_CANCEL_OPTION:
|
||||
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO, ButtonType.CANCEL);
|
||||
break;
|
||||
case OK_CANCEL_OPTION:
|
||||
alert.getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
|
||||
break;
|
||||
}
|
||||
Optional<ButtonType> buttonType = alert.showAndWait();
|
||||
if (!buttonType.isPresent()) return CLOSED_OPTION;
|
||||
else if (buttonType.get() == ButtonType.OK) return OK_OPTION;
|
||||
else if (buttonType.get() == ButtonType.YES) return YES_OPTION;
|
||||
else if (buttonType.get() == ButtonType.NO) return NO_OPTION;
|
||||
else if (buttonType.get() == ButtonType.CANCEL) return CANCEL_OPTION;
|
||||
else throw new IllegalStateException("Unrecognized button type:" + buttonType.get());
|
||||
}
|
||||
|
||||
public static Optional<String> input(String message) {
|
||||
return input(message, UIManager.getString("OptionPane.inputDialogTitle"));
|
||||
}
|
||||
|
||||
public static Optional<String> input(String message, String title) {
|
||||
return input(message, title, "");
|
||||
}
|
||||
|
||||
public static Optional<String> input(String message, String title, String initialValue) {
|
||||
TextInputDialog dialog = new TextInputDialog(initialValue);
|
||||
dialog.setTitle(title);
|
||||
dialog.setHeaderText(message);
|
||||
dialog.setContentText(message);
|
||||
|
||||
return dialog.showAndWait();
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.construct;
|
||||
|
||||
import kotlin.jvm.Throws;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Locale;
|
||||
import java.util.PropertyResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class UTF8Control extends ResourceBundle.Control {
|
||||
@Override
|
||||
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
|
||||
throws IllegalAccessException, InstantiationException, IOException {
|
||||
// The below is a copy of the default implementation.
|
||||
String bundleName = toBundleName(baseName, locale);
|
||||
String resourceName = toResourceName(bundleName, "properties");
|
||||
ResourceBundle bundle = null;
|
||||
InputStream stream = null;
|
||||
if (reload) {
|
||||
URL url = loader.getResource(resourceName);
|
||||
if (url != null) {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (connection != null) {
|
||||
connection.setUseCaches(false);
|
||||
stream = connection.getInputStream();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stream = loader.getResourceAsStream(resourceName);
|
||||
}
|
||||
if (stream != null) {
|
||||
try {
|
||||
// Only this line is changed to make it to read properties files as UTF-8.
|
||||
bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.construct;
|
||||
|
||||
import com.jfoenix.validation.base.ValidatorBase;
|
||||
import javafx.scene.control.TextInputControl;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class Validator extends ValidatorBase {
|
||||
private final Predicate<String> validator;
|
||||
|
||||
/**
|
||||
* @param validator return true if the input string is valid.
|
||||
*/
|
||||
public Validator(Predicate<String> validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void eval() {
|
||||
if (this.srcControl.get() instanceof TextInputControl) {
|
||||
String text = ((TextInputControl) srcControl.get()).getText();
|
||||
hasErrors.set(!validator.test(text));
|
||||
}
|
||||
}
|
||||
}
|
@ -15,25 +15,36 @@
|
||||
* 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.wizard
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
|
||||
interface Navigation {
|
||||
fun onStart()
|
||||
fun onNext()
|
||||
fun onPrev(cleanUp: Boolean)
|
||||
fun canPrev(): Boolean
|
||||
fun onFinish()
|
||||
fun onEnd()
|
||||
fun onCancel()
|
||||
public interface Navigation {
|
||||
|
||||
enum class NavigationDirection(val animation: ContainerAnimations) {
|
||||
void onStart();
|
||||
void onNext();
|
||||
void onPrev(boolean cleanUp);
|
||||
boolean canPrev();
|
||||
void onFinish();
|
||||
void onEnd();
|
||||
void onCancel();
|
||||
|
||||
enum NavigationDirection {
|
||||
START(ContainerAnimations.NONE),
|
||||
PREVIOUS(ContainerAnimations.SWIPE_RIGHT),
|
||||
NEXT(ContainerAnimations.SWIPE_LEFT),
|
||||
FINISH(ContainerAnimations.SWIPE_LEFT),
|
||||
IN(ContainerAnimations.ZOOM_IN),
|
||||
OUT(ContainerAnimations.ZOOM_OUT)
|
||||
OUT(ContainerAnimations.ZOOM_OUT);
|
||||
|
||||
private final ContainerAnimations animation;
|
||||
|
||||
NavigationDirection(ContainerAnimations animation) {
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
public ContainerAnimations getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,8 +15,8 @@
|
||||
* 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.wizard
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
interface Refreshable {
|
||||
fun refresh()
|
||||
}
|
||||
public interface Refreshable {
|
||||
void refresh();
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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.upgrade;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Pack200;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.MainKt;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class AppDataUpgrader extends IUpgrader {
|
||||
|
||||
private boolean launchNewerVersion(List<String> args, File jar) throws IOException, PrivilegedActionException {
|
||||
try (JarFile jarFile = new JarFile(jar)) {
|
||||
String mainClass = jarFile.getManifest().getMainAttributes().getValue("Main-Class");
|
||||
if (mainClass != null) {
|
||||
ArrayList<String> al = new ArrayList<>(args);
|
||||
al.add("--noupdate");
|
||||
AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
|
||||
new URLClassLoader(new URL[]{jar.toURI().toURL()},
|
||||
URLClassLoader.getSystemClassLoader().getParent()).loadClass(mainClass)
|
||||
.getMethod("main", String[].class).invoke(null, new Object[]{al.toArray(new String[0])});
|
||||
return null;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseArguments(VersionNumber nowVersion, List<String> args) {
|
||||
File f = AppDataUpgraderPackGzTask.HMCL_VER_FILE;
|
||||
if (!args.contains("--noupdate"))
|
||||
try {
|
||||
if (f.exists()) {
|
||||
Map<String, String> m = Constants.GSON.fromJson(FileUtils.readText(f), new TypeToken<Map<String, String>>() {
|
||||
}.getType());
|
||||
String s = m.get("ver");
|
||||
if (s != null && VersionNumber.asVersion(s).compareTo(nowVersion) > 0) {
|
||||
String j = m.get("loc");
|
||||
if (j != null) {
|
||||
File jar = new File(j);
|
||||
if (jar.exists() && launchNewerVersion(args, jar))
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JsonSyntaxException ex) {
|
||||
f.delete();
|
||||
} catch (IOException | PrivilegedActionException t) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to execute newer version application", t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(UpdateChecker checker, VersionNumber ver) {
|
||||
if (!(ver instanceof IntVersionNumber))
|
||||
return;
|
||||
IntVersionNumber version = (IntVersionNumber) ver;
|
||||
checker.requestDownloadLink().then(Task.of(v -> {
|
||||
Map<String, String> map = v.get(UpdateChecker.REQUEST_DOWNLOAD_LINK_ID);
|
||||
|
||||
if (MessageBox.confirm(MainKt.i18n("update.newest_version") + version.get(0) + "." + version.get(1) + "." + version.get(2) + "\n"
|
||||
+ MainKt.i18n("update.should_open_link"),
|
||||
MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
|
||||
if (map != null && map.containsKey("jar") && !StringUtils.isBlank(map.get("jar")))
|
||||
try {
|
||||
String hash = null;
|
||||
if (map.containsKey("jarsha1"))
|
||||
hash = map.get("jarsha1");
|
||||
if (TaskWindow.factory().append(new AppDataUpgraderJarTask(NetworkUtils.toURL(map.get("jar")), version.toString(), hash)).execute()) {
|
||||
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderJarTask.getSelf(version.toString()).getAbsolutePath())
|
||||
.directory(new File("").getAbsoluteFile()).start();
|
||||
System.exit(0);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex);
|
||||
}
|
||||
else if (map != null && map.containsKey("pack") && !StringUtils.isBlank(map.get("pack")))
|
||||
try {
|
||||
String hash = null;
|
||||
if (map.containsKey("packsha1"))
|
||||
hash = map.get("packsha1");
|
||||
if (TaskWindow.factory().append(new AppDataUpgraderPackGzTask(NetworkUtils.toURL(map.get("pack")), version.toString(), hash)).execute()) {
|
||||
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderPackGzTask.getSelf(version.toString()).getAbsolutePath())
|
||||
.directory(new File("").getAbsoluteFile()).start();
|
||||
System.exit(0);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex);
|
||||
}
|
||||
else {
|
||||
String url = URL_PUBLISH;
|
||||
if (map != null)
|
||||
if (map.containsKey(OperatingSystem.CURRENT_OS.getCheckedName()))
|
||||
url = map.get(OperatingSystem.CURRENT_OS.getCheckedName());
|
||||
else if (map.containsKey(OperatingSystem.UNKNOWN.getCheckedName()))
|
||||
url = map.get(OperatingSystem.UNKNOWN.getCheckedName());
|
||||
if (url == null)
|
||||
url = URL_PUBLISH;
|
||||
try {
|
||||
java.awt.Desktop.getDesktop().browse(new URI(url));
|
||||
} catch (URISyntaxException | IOException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e);
|
||||
OperatingSystem.setClipboard(url);
|
||||
MessageBox.show(MainKt.i18n("update.no_browser"));
|
||||
}
|
||||
}
|
||||
})).start();
|
||||
}
|
||||
|
||||
public static class AppDataUpgraderPackGzTask extends Task {
|
||||
|
||||
public static final File BASE_FOLDER = Main.getWorkingDirectory("hmcl");
|
||||
public static final File HMCL_VER_FILE = new File(BASE_FOLDER, "hmclver.json");
|
||||
|
||||
public static File getSelf(String ver) {
|
||||
return new File(BASE_FOLDER, "HMCL-" + ver + ".jar");
|
||||
}
|
||||
|
||||
private final URL downloadLink;
|
||||
private final String newestVersion, hash;
|
||||
File tempFile;
|
||||
|
||||
public AppDataUpgraderPackGzTask(URL downloadLink, String newestVersion, String hash) throws IOException {
|
||||
this.downloadLink = downloadLink;
|
||||
this.newestVersion = newestVersion;
|
||||
this.hash = hash;
|
||||
tempFile = File.createTempFile("hmcl", ".pack.gz");
|
||||
|
||||
setName("Upgrade");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task> getDependents() {
|
||||
return Arrays.asList(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
HashMap<String, String> json = new HashMap<>();
|
||||
File f = getSelf(newestVersion);
|
||||
if (!FileUtils.makeDirectory(f.getParentFile()))
|
||||
throw new IOException("Failed to make directories: " + f.getParent());
|
||||
|
||||
for (int i = 0; f.exists(); i++)
|
||||
f = new File(BASE_FOLDER, "HMCL-" + newestVersion + (i > 0 ? "-" + i : "") + ".jar");
|
||||
if (!f.createNewFile())
|
||||
throw new IOException("Failed to create new file: " + f);
|
||||
|
||||
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(f))) {
|
||||
Pack200.newUnpacker().unpack(new GZIPInputStream(new FileInputStream(tempFile)), jos);
|
||||
}
|
||||
json.put("ver", newestVersion);
|
||||
json.put("loc", f.getAbsolutePath());
|
||||
String result = Constants.GSON.toJson(json);
|
||||
FileUtils.writeText(HMCL_VER_FILE, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class AppDataUpgraderJarTask extends Task {
|
||||
|
||||
public static final File BASE_FOLDER = Main.getWorkingDirectory("hmcl");
|
||||
public static final File HMCL_VER_FILE = new File(BASE_FOLDER, "hmclver.json");
|
||||
|
||||
public static File getSelf(String ver) {
|
||||
return new File(BASE_FOLDER, "HMCL-" + ver + ".jar");
|
||||
}
|
||||
|
||||
private final URL downloadLink;
|
||||
private final String newestVersion, hash;
|
||||
File tempFile;
|
||||
|
||||
public AppDataUpgraderJarTask(URL downloadLink, String newestVersion, String hash) throws IOException {
|
||||
this.downloadLink = downloadLink;
|
||||
this.newestVersion = newestVersion;
|
||||
this.hash = hash;
|
||||
tempFile = File.createTempFile("hmcl", ".jar");
|
||||
|
||||
setName("Upgrade");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task> getDependents() {
|
||||
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
HashMap<String, String> json = new HashMap<>();
|
||||
File f = getSelf(newestVersion);
|
||||
FileUtils.copyFile(tempFile, f);
|
||||
json.put("ver", newestVersion);
|
||||
json.put("loc", f.getAbsolutePath());
|
||||
String result = Constants.GSON.toJson(json);
|
||||
FileUtils.writeText(HMCL_VER_FILE, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
@ -15,15 +15,19 @@
|
||||
* 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.upgrade
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import org.jackhuang.hmcl.util.VersionNumber
|
||||
import org.jackhuang.hmcl.util.VersionNumber;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
interface IUpgrader {
|
||||
public abstract class IUpgrader {
|
||||
|
||||
public static final IUpgrader NOW_UPGRADER = new AppDataUpgrader();
|
||||
|
||||
/**
|
||||
* Paring arguments to decide on whether the upgrade is needed.
|
||||
@ -31,19 +35,15 @@ interface IUpgrader {
|
||||
* @param nowVersion now launcher version
|
||||
* @param args Application CommandLine Arguments
|
||||
*/
|
||||
fun parseArguments(nowVersion: VersionNumber, args: Array<String>)
|
||||
public abstract void parseArguments(VersionNumber nowVersion, List<String> args);
|
||||
|
||||
/**
|
||||
* Just download the new app.
|
||||
*
|
||||
* @param checker Should be VersionChecker
|
||||
* @param versionNumber the newest version
|
||||
* @param version the newest version
|
||||
*
|
||||
* @return should return true
|
||||
*/
|
||||
fun download(checker: UpdateChecker, versionNumber: VersionNumber)
|
||||
|
||||
companion object {
|
||||
val NOW_UPGRADER: IUpgrader = AppDataUpgrader()
|
||||
}
|
||||
public abstract void download(UpdateChecker checker, VersionNumber version);
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.upgrade;
|
||||
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.util.Charsets;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.VersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class NewFileUpgrader extends IUpgrader {
|
||||
|
||||
@Override
|
||||
public void parseArguments(VersionNumber nowVersion, List<String> args) {
|
||||
int i = args.indexOf("--removeOldLauncher");
|
||||
if (i != -1 && i < args.size() - 1) {
|
||||
File f = new File(args.get(i + 1));
|
||||
if (f.exists())
|
||||
f.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(UpdateChecker checker, VersionNumber version) {
|
||||
URL url = requestDownloadLink();
|
||||
if (url == null) return;
|
||||
File newf = new File(url.getFile());
|
||||
if (TaskWindow.factory().append(new FileDownloadTask(url, newf)).execute()) {
|
||||
try {
|
||||
new ProcessBuilder(newf.getCanonicalPath(), "--removeOldLauncher", getRealPath())
|
||||
.directory(new File("").getAbsoluteFile())
|
||||
.start();
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to start new app", ex);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRealPath() {
|
||||
String realPath = NewFileUpgrader.class.getClassLoader().getResource("").getFile();
|
||||
File file = new File(realPath);
|
||||
realPath = file.getAbsolutePath();
|
||||
try {
|
||||
realPath = java.net.URLDecoder.decode(realPath, Charsets.DEFAULT_CHARSET.name());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return realPath;
|
||||
}
|
||||
|
||||
private URL requestDownloadLink() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
139
HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java
Normal file
139
HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.upgrade;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import org.jackhuang.hmcl.MainKt;
|
||||
import org.jackhuang.hmcl.event.Event;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.OutOfDateEvent;
|
||||
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 java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class UpdateChecker {
|
||||
|
||||
private volatile boolean outOfDate = false;
|
||||
private VersionNumber base;
|
||||
private String versionString;
|
||||
public String type;
|
||||
private Map<String, String> download_link = null;
|
||||
|
||||
public UpdateChecker(VersionNumber base, String type) {
|
||||
this.base = base;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private VersionNumber value;
|
||||
|
||||
public boolean isOutOfDate() {
|
||||
return outOfDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the version number synchronously. When you execute this method
|
||||
* first, should leave "showMessage" false.
|
||||
*
|
||||
* @param showMessage If it is requested to warn the user that there is a
|
||||
* new version.
|
||||
*
|
||||
* @return the process observable.
|
||||
*/
|
||||
public TaskResult<VersionNumber> process(final boolean showMessage) {
|
||||
return new TaskResult<VersionNumber>() {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (value == null) {
|
||||
versionString = NetworkUtils.doGet(NetworkUtils.toURL("http://huangyuhui.duapp.com/info.php?type=" + type));
|
||||
value = VersionNumber.asVersion(versionString);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
Logging.LOG.warning("Failed to check update...");
|
||||
if (showMessage)
|
||||
MessageBox.show(MainKt.i18n("update.failed"));
|
||||
} else if (base.compareTo(value) < 0)
|
||||
outOfDate = true;
|
||||
if (outOfDate)
|
||||
setResult(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "update_checker.process";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <b>cached</b> newest version number, use "process" method to
|
||||
* download!
|
||||
*
|
||||
* @return the newest version number
|
||||
*
|
||||
* @see #process(boolean)
|
||||
*/
|
||||
public VersionNumber getNewVersion() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the download links.
|
||||
*
|
||||
* @return a JSON, which contains the server response.
|
||||
*/
|
||||
public synchronized TaskResult<Map<String, String>> requestDownloadLink() {
|
||||
return new TaskResult<Map<String, String>>() {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (download_link == null)
|
||||
try {
|
||||
download_link = Constants.GSON.<Map<String, String>>fromJson(NetworkUtils.doGet(NetworkUtils.toURL("http://huangyuhui.duapp.com/update_link.php?type=" + type)), Map.class);
|
||||
} catch (JsonSyntaxException | IOException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to get update link.", e);
|
||||
}
|
||||
setResult(download_link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "update_checker.request_download_link";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final String REQUEST_DOWNLOAD_LINK_ID = "update_checker.request_download_link";
|
||||
|
||||
public void checkOutdate() {
|
||||
if (outOfDate)
|
||||
if (EventBus.EVENT_BUS.fireEvent(new OutOfDateEvent(this, getNewVersion())) != Event.Result.DENY) {
|
||||
IUpgrader.NOW_UPGRADER.download(this, getNewVersion());
|
||||
}
|
||||
}
|
||||
}
|
@ -70,6 +70,7 @@ class Main : Application() {
|
||||
launch(Main::class.java, *args)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getWorkingDirectory(folder: String): File {
|
||||
val userhome = System.getProperty("user.home", ".")
|
||||
return when (OperatingSystem.CURRENT_OS) {
|
||||
@ -83,6 +84,7 @@ class Main : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getMinecraftDirectory(): File = getWorkingDirectory("minecraft")
|
||||
|
||||
fun stop() = runOnUiThread {
|
||||
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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.construct
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.util.*
|
||||
|
||||
object UTF8Control : ResourceBundle.Control() {
|
||||
@Throws(IllegalAccessException::class, InstantiationException::class, IOException::class)
|
||||
override fun newBundle(baseName: String, locale: Locale, format: String, loader: ClassLoader, reload: Boolean): ResourceBundle? {
|
||||
// The below is a copy of the default implementation.
|
||||
val bundleName = toBundleName(baseName, locale)
|
||||
val resourceName = toResourceName(bundleName, "properties")
|
||||
var bundle: ResourceBundle? = null
|
||||
var stream: InputStream? = null
|
||||
if (reload) {
|
||||
val url = loader.getResource(resourceName)
|
||||
if (url != null) {
|
||||
val connection = url.openConnection()
|
||||
if (connection != null) {
|
||||
connection.useCaches = false
|
||||
stream = connection.getInputStream()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stream = loader.getResourceAsStream(resourceName)
|
||||
}
|
||||
if (stream != null) {
|
||||
try {
|
||||
// Only this line is changed to make it to read properties files as UTF-8.
|
||||
bundle = PropertyResourceBundle(InputStreamReader(stream, "UTF-8"))
|
||||
} finally {
|
||||
stream.close()
|
||||
}
|
||||
}
|
||||
return bundle
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
/*
|
||||
* 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.upgrade
|
||||
|
||||
import java.security.PrivilegedActionException
|
||||
import java.io.IOException
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import javafx.scene.control.Alert
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.security.PrivilegedExceptionAction
|
||||
import java.security.AccessController
|
||||
import java.util.Arrays
|
||||
import java.util.ArrayList
|
||||
import java.util.jar.JarFile
|
||||
import org.jackhuang.hmcl.Main
|
||||
import java.util.HashMap
|
||||
import org.jackhuang.hmcl.task.*
|
||||
import java.util.logging.Level
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.jar.Pack200
|
||||
import java.util.jar.JarOutputStream
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.net.URISyntaxException
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.ui.alert
|
||||
import org.jackhuang.hmcl.util.Constants.GSON
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import org.jackhuang.hmcl.util.VersionNumber
|
||||
import java.net.Proxy
|
||||
import java.net.URI
|
||||
|
||||
class AppDataUpgrader : IUpgrader {
|
||||
|
||||
@Throws(IOException::class, PrivilegedActionException::class)
|
||||
private fun launchNewerVersion(args: Array<String>, jar: File): Boolean {
|
||||
JarFile(jar).use { jarFile ->
|
||||
val mainClass = jarFile.manifest.mainAttributes.getValue("Main-Class")
|
||||
if (mainClass != null) {
|
||||
val al = ArrayList(Arrays.asList(*args))
|
||||
al.add("--noupdate")
|
||||
AccessController.doPrivileged(PrivilegedExceptionAction<Void> {
|
||||
URLClassLoader(arrayOf(jar.toURI().toURL()),
|
||||
URLClassLoader.getSystemClassLoader().parent).loadClass(mainClass)
|
||||
.getMethod("main", Array<String>::class.java).invoke(null, *arrayOf<Any>(al.toTypedArray()))
|
||||
null
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun parseArguments(nowVersion: VersionNumber, args: Array<String>) {
|
||||
val f = AppDataUpgraderPackGzTask.HMCL_VER_FILE
|
||||
if (!args.contains("--noupdate"))
|
||||
try {
|
||||
if (f.exists()) {
|
||||
val m = GSON.fromJson(f.readText(), Map::class.java)
|
||||
val s = m["ver"] as? String?
|
||||
if (s != null && VersionNumber.asVersion(s.toString()) > nowVersion) {
|
||||
val j = m["loc"] as? String?
|
||||
if (j != null) {
|
||||
val jar = File(j)
|
||||
if (jar.exists() && launchNewerVersion(args, jar))
|
||||
System.exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex: JsonSyntaxException) {
|
||||
f.delete()
|
||||
} catch (t: IOException) {
|
||||
LOG.log(Level.SEVERE, "Failed to execute newer version application", t)
|
||||
} catch (t: PrivilegedActionException) {
|
||||
LOG.log(Level.SEVERE, "Failed to execute newer version application", t)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun download(checker: UpdateChecker, versionNumber: VersionNumber) {
|
||||
val version = versionNumber as IntVersionNumber
|
||||
checker.requestDownloadLink().then {
|
||||
val map: Map<String, String>? = it["update_checker.request_download_link"]
|
||||
if (alert(Alert.AlertType.CONFIRMATION, "Alert", i18n("update.newest_version") + version[0] + "." + version[1] + "." + version[2] + "\n"
|
||||
+ i18n("update.should_open_link")))
|
||||
if (map != null && map.containsKey("jar") && map["jar"]!!.isNotBlank())
|
||||
try {
|
||||
var hash: String? = null
|
||||
if (map.containsKey("jarsha1"))
|
||||
hash = map.get("jarsha1")
|
||||
if (AppDataUpgraderJarTask(map["jar"]!!, version.toString(), hash!!).test()) {
|
||||
ProcessBuilder(JavaVersion.fromCurrentEnvironment().binary.absolutePath, "-jar", AppDataUpgraderJarTask.getSelf(version.toString()).absolutePath).directory(File("").absoluteFile).start()
|
||||
System.exit(0)
|
||||
}
|
||||
} catch (ex: IOException) {
|
||||
LOG.log(Level.SEVERE, "Failed to create upgrader", ex)
|
||||
}
|
||||
else if (map != null && map.containsKey("pack") && map["pack"]!!.isNotBlank())
|
||||
try {
|
||||
var hash: String? = null
|
||||
if (map.containsKey("packsha1"))
|
||||
hash = map["packsha1"]
|
||||
if (AppDataUpgraderPackGzTask(map["pack"]!!, version.toString(), hash!!).test()) {
|
||||
ProcessBuilder(JavaVersion.fromCurrentEnvironment().binary.absolutePath, "-jar", AppDataUpgraderPackGzTask.getSelf(version.toString()).absolutePath).directory(File("").absoluteFile).start()
|
||||
System.exit(0)
|
||||
}
|
||||
} catch (ex: IOException) {
|
||||
LOG.log(Level.SEVERE, "Failed to create upgrader", ex)
|
||||
}
|
||||
else {
|
||||
var url = URL_PUBLISH
|
||||
if (map != null)
|
||||
if (map.containsKey(OperatingSystem.CURRENT_OS.checkedName))
|
||||
url = map.get(OperatingSystem.CURRENT_OS.checkedName)!!
|
||||
else if (map.containsKey(OperatingSystem.UNKNOWN.checkedName))
|
||||
url = map.get(OperatingSystem.UNKNOWN.checkedName)!!
|
||||
try {
|
||||
java.awt.Desktop.getDesktop().browse(URI(url))
|
||||
} catch (e: URISyntaxException) {
|
||||
LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e)
|
||||
OperatingSystem.setClipboard(url)
|
||||
} catch (e: IOException) {
|
||||
LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e)
|
||||
OperatingSystem.setClipboard(url)
|
||||
}
|
||||
|
||||
}
|
||||
null
|
||||
}.execute()
|
||||
}
|
||||
|
||||
class AppDataUpgraderPackGzTask(downloadLink: String, private val newestVersion: String, private val expectedHash: String) : Task() {
|
||||
private val tempFile: File = File.createTempFile("hmcl", ".pack.gz")
|
||||
private val dependents = listOf(FileDownloadTask(downloadLink.toURL(), tempFile, Proxy.NO_PROXY, expectedHash))
|
||||
override fun getDependents() = dependents
|
||||
|
||||
init {
|
||||
onDone() += { event -> if (event.isFailed) tempFile.delete() }
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val json = HashMap<String, String>()
|
||||
var f = getSelf(newestVersion)
|
||||
if (!FileUtils.makeDirectory(f.parentFile))
|
||||
throw IOException("Failed to make directories: " + f.parent)
|
||||
|
||||
var i = 0
|
||||
while (f.exists()) {
|
||||
f = File(BASE_FOLDER, "HMCL-" + newestVersion + (if (i > 0) "-" + i else "") + ".jar")
|
||||
i++
|
||||
}
|
||||
if (!f.createNewFile())
|
||||
throw IOException("Failed to create new file: " + f)
|
||||
|
||||
JarOutputStream(f.outputStream()).use { jos -> Pack200.newUnpacker().unpack(GZIPInputStream(tempFile.inputStream()), jos) }
|
||||
json.put("ver", newestVersion)
|
||||
json.put("loc", f.absolutePath)
|
||||
val result = GSON.toJson(json)
|
||||
HMCL_VER_FILE.writeText(result)
|
||||
}
|
||||
|
||||
val info: String
|
||||
get() = "Upgrade"
|
||||
|
||||
companion object {
|
||||
|
||||
val BASE_FOLDER = Main.getWorkingDirectory("hmcl")
|
||||
val HMCL_VER_FILE = File(BASE_FOLDER, "hmclver.json")
|
||||
|
||||
fun getSelf(ver: String): File {
|
||||
return File(BASE_FOLDER, "HMCL-$ver.jar")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AppDataUpgraderJarTask(downloadLink: String, private val newestVersion: String, expectedHash: String) : Task() {
|
||||
private val tempFile = File.createTempFile("hmcl", ".jar")
|
||||
|
||||
init {
|
||||
name = "Upgrade"
|
||||
onDone() += { event -> if (event.isFailed) tempFile.delete() }
|
||||
}
|
||||
|
||||
private val dependents = listOf(FileDownloadTask(downloadLink.toURL(), tempFile, Proxy.NO_PROXY, expectedHash))
|
||||
override fun getDependents() = dependents
|
||||
|
||||
override fun execute() {
|
||||
val json = HashMap<String, String>()
|
||||
val f = getSelf(newestVersion)
|
||||
tempFile.copyTo(f)
|
||||
json.put("ver", newestVersion)
|
||||
json.put("loc", f.absolutePath)
|
||||
val result = GSON.toJson(json)
|
||||
HMCL_VER_FILE.writeText(result)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val BASE_FOLDER = Main.getWorkingDirectory("hmcl")
|
||||
val HMCL_VER_FILE = File(BASE_FOLDER, "hmclver.json")
|
||||
|
||||
fun getSelf(ver: String): File {
|
||||
return File(BASE_FOLDER, "HMCL-$ver.jar")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val URL_PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html"
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.upgrade
|
||||
|
||||
import java.io.IOException
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import org.jackhuang.hmcl.task.TaskResult
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.Constants.GSON
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import java.util.logging.Level
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class UpdateChecker(var base: VersionNumber, var type: String) {
|
||||
|
||||
@Volatile
|
||||
var isOutOfDate = false
|
||||
private set
|
||||
var versionString: String? = null
|
||||
private var download_link: Map<String, String>? = null
|
||||
|
||||
/**
|
||||
* Get the <b>cached</b> newest version number, use "process" method to
|
||||
* download!
|
||||
*
|
||||
* @return the newest version number
|
||||
*
|
||||
* @see process
|
||||
*/
|
||||
var newVersion: VersionNumber? = null
|
||||
internal set
|
||||
|
||||
/**
|
||||
* Download the version number synchronously. When you execute this method
|
||||
* first, should leave "showMessage" false.
|
||||
*
|
||||
* @param showMessage If it is requested to warn the user that there is a
|
||||
* new version.
|
||||
*
|
||||
* @return the process observable.
|
||||
*/
|
||||
fun process(showMessage: Boolean): TaskResult<VersionNumber> {
|
||||
return object : TaskResult<VersionNumber>() {
|
||||
override fun getId() = "update_checker.process"
|
||||
override fun execute() {
|
||||
if (newVersion == null) {
|
||||
versionString = NetworkUtils.doGet("http://huangyuhui.duapp.com/info.php?type=$type".toURL())
|
||||
newVersion = VersionNumber.asVersion(versionString!!)
|
||||
}
|
||||
|
||||
if (newVersion == null) {
|
||||
LOG.warning("Failed to check update...")
|
||||
} else if (base < newVersion!!)
|
||||
isOutOfDate = true
|
||||
if (isOutOfDate)
|
||||
result = newVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the download links.
|
||||
*
|
||||
* @return a JSON, which contains the server response.
|
||||
*/
|
||||
@Synchronized
|
||||
fun requestDownloadLink(): TaskResult<Map<String, String>> {
|
||||
return object : TaskResult<Map<String, String>>() {
|
||||
override fun getId() = "update_checker.request_download_link"
|
||||
override fun execute() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (download_link == null)
|
||||
try {
|
||||
download_link = GSON.fromJson(NetworkUtils.doGet("http://huangyuhui.duapp.com/update_link.php?type=$type".toURL()), Map::class.java) as Map<String, String>
|
||||
} catch (e: JsonSyntaxException) {
|
||||
LOG.log(Level.WARNING, "Failed to get update link.", e)
|
||||
} catch (e: IOException) {
|
||||
LOG.log(Level.WARNING, "Failed to get update link.", e)
|
||||
}
|
||||
|
||||
result = download_link
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
val upgrade: EventHandler<SimpleEvent<VersionNumber>> = EventHandler()
|
||||
|
||||
fun checkOutdate() {
|
||||
if (isOutOfDate)
|
||||
if (EVENT_BUS.fireChannelResulted(OutOfDateEvent(this, newVersion)))
|
||||
upgrade.fire(SimpleEvent(this, newVersion))
|
||||
}*/
|
||||
}
|
@ -108,14 +108,14 @@ public abstract class Task {
|
||||
* The collection of sub-tasks that should execute **before** this task running.
|
||||
*/
|
||||
public Collection<Task> getDependents() {
|
||||
return Collections.EMPTY_SET;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection of sub-tasks that should execute **after** this task running.
|
||||
*/
|
||||
public Collection<Task> getDependencies() {
|
||||
return Collections.EMPTY_SET;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public EventManager<TaskEvent> onDone() {
|
||||
@ -180,8 +180,6 @@ public abstract class Task {
|
||||
progressProperty.unbind();
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
public final TaskExecutor executor() {
|
||||
return new TaskExecutor(this);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user