Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9d825b6c1 | ||
|
|
7dcd0eb616 | ||
|
|
e79a95b885 | ||
|
|
5d232c3f5e | ||
|
|
ee1c0d362d | ||
|
|
aafcd57c82 | ||
|
|
66a4566dc5 | ||
|
|
4c317f5b03 | ||
|
|
12d706a812 | ||
|
|
c006068580 | ||
|
|
f8a838506d | ||
|
|
270844bbb1 | ||
|
|
c9466cf8ef | ||
|
|
7d6f1f98f1 |
No files matched your search
@@ -26,7 +26,6 @@ import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import java.io.File;
|
||||
@@ -74,19 +73,14 @@ public final class Launcher extends Application {
|
||||
public static void main(String[] args) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
|
||||
|
||||
if (!FileUtils.makeDirectory(LOG_DIRECTORY))
|
||||
System.out.println("Unable to create log directory " + LOG_DIRECTORY + ", log files cannot be generated.");
|
||||
|
||||
try {
|
||||
Logging.start(LOG_DIRECTORY);
|
||||
|
||||
LOG.info("*** " + Metadata.TITLE + " ***");
|
||||
LOG.info("Operating System: " + System.getProperty("os.name") + ' ' + OperatingSystem.SYSTEM_VERSION);
|
||||
LOG.info("Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
|
||||
LOG.info("Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
|
||||
LOG.info("Java Home: " + System.getProperty("java.home"));
|
||||
LOG.info("Current Directory: " + Paths.get("").toAbsolutePath());
|
||||
LOG.info("HMCL Directory: " + HMCL_DIRECTORY);
|
||||
LOG.info("HMCL Directory: " + Metadata.HMCL_DIRECTORY);
|
||||
LOG.info("Memory: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
|
||||
ManagementFactory.getMemoryPoolMXBeans().stream().filter(bean -> bean.getName().equals("Metaspace")).findAny()
|
||||
.ifPresent(bean -> LOG.info("Metaspace: " + bean.getUsage().getUsed() / 1024 / 1024 + "MB"));
|
||||
@@ -143,9 +137,5 @@ public final class Launcher extends Application {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final File MINECRAFT_DIRECTORY = OperatingSystem.getWorkingDirectory("minecraft");
|
||||
public static final File HMCL_DIRECTORY = OperatingSystem.getWorkingDirectory("hmcl");
|
||||
public static final File LOG_DIRECTORY = new File(Launcher.HMCL_DIRECTORY, "logs");
|
||||
|
||||
public static final CrashReporter CRASH_REPORTER = new CrashReporter();
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
package org.jackhuang.hmcl;
|
||||
|
||||
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
@@ -42,6 +44,8 @@ public final class Main {
|
||||
checkDirectoryPath();
|
||||
checkDSTRootCAX3();
|
||||
|
||||
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
||||
|
||||
if (UpdateHandler.processArguments(args)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
/**
|
||||
* Stores metadata about this application.
|
||||
@@ -33,4 +36,7 @@ public final class Metadata {
|
||||
public static final String CONTACT_URL = "https://hmcl.huangyuhui.net/contact";
|
||||
public static final String HELP_URL = "https://hmcl.huangyuhui.net/help";
|
||||
public static final String PUBLISH_URL = "http://www.mcbbs.net/thread-142335-1-1.html";
|
||||
|
||||
public static final Path MINECRAFT_DIRECTORY = OperatingSystem.getWorkingDirectory("minecraft");
|
||||
public static final Path HMCL_DIRECTORY = OperatingSystem.getWorkingDirectory("hmcl");
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.game;
|
||||
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.scene.image.Image;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.Texture;
|
||||
@@ -41,7 +41,7 @@ public final class AccountHelper {
|
||||
|
||||
private AccountHelper() {}
|
||||
|
||||
public static final File SKIN_DIR = new File(Launcher.HMCL_DIRECTORY, "skins");
|
||||
public static final File SKIN_DIR = Metadata.HMCL_DIRECTORY.resolve("skins").toFile();
|
||||
|
||||
public static void loadSkins() {
|
||||
for (Account account : Accounts.getAccounts()) {
|
||||
|
||||
@@ -93,6 +93,16 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
refreshVersionsAsync().start();
|
||||
}
|
||||
|
||||
private void clean(File directory) throws IOException {
|
||||
FileUtils.deleteDirectory(new File(directory, "crash-reports"));
|
||||
FileUtils.deleteDirectory(new File(directory, "logs"));
|
||||
}
|
||||
|
||||
public void clean(String id) throws IOException {
|
||||
clean(getBaseDirectory());
|
||||
clean(getRunDirectory(id));
|
||||
}
|
||||
|
||||
private File getVersionSettingFile(String id) {
|
||||
return new File(getVersionRoot(id), "hmclversion.cfg");
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import javafx.beans.property.ReadOnlyListProperty;
|
||||
import javafx.beans.property.ReadOnlyListWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
@@ -61,7 +61,7 @@ public final class Accounts {
|
||||
public static final OfflineAccountFactory FACTORY_OFFLINE = OfflineAccountFactory.INSTANCE;
|
||||
public static final YggdrasilAccountFactory FACTORY_YGGDRASIL = new YggdrasilAccountFactory(MojangYggdrasilProvider.INSTANCE);
|
||||
public static final AuthlibInjectorAccountFactory FACTORY_AUTHLIB_INJECTOR = new AuthlibInjectorAccountFactory(
|
||||
new AuthlibInjectorDownloader(Launcher.HMCL_DIRECTORY.toPath(), DownloadProviders::getDownloadProvider)::getArtifactInfo,
|
||||
new AuthlibInjectorDownloader(Metadata.HMCL_DIRECTORY, DownloadProviders::getDownloadProvider)::getArtifactInfo,
|
||||
Accounts::getOrCreateAuthlibInjectorServer);
|
||||
|
||||
private static final String TYPE_OFFLINE = "offline";
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.hildan.fxgson.creators.ObservableListCreator;
|
||||
import org.hildan.fxgson.creators.ObservableMapCreator;
|
||||
import org.hildan.fxgson.creators.ObservableSetCreator;
|
||||
import org.hildan.fxgson.factories.JavaFxPropertyTypeAdapterFactory;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChannel;
|
||||
import org.jackhuang.hmcl.util.gson.EnumOrdinalDeserializer;
|
||||
@@ -86,7 +86,7 @@ public final class Config implements Cloneable, Observable {
|
||||
private ObjectProperty<EnumCommonDirectory> commonDirType = new SimpleObjectProperty<>(EnumCommonDirectory.DEFAULT);
|
||||
|
||||
@SerializedName("commonpath")
|
||||
private StringProperty commonDirectory = new SimpleStringProperty(Launcher.MINECRAFT_DIRECTORY.getAbsolutePath());
|
||||
private StringProperty commonDirectory = new SimpleStringProperty(Metadata.MINECRAFT_DIRECTORY.toString());
|
||||
|
||||
@SerializedName("hasProxy")
|
||||
private BooleanProperty hasProxy = new SimpleBooleanProperty();
|
||||
|
||||
@@ -22,7 +22,7 @@ import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
|
||||
|
||||
@@ -104,7 +104,7 @@ public final class Profiles {
|
||||
private static void checkProfiles() {
|
||||
if (profiles.isEmpty()) {
|
||||
Profile current = new Profile(Profiles.DEFAULT_PROFILE, new File(".minecraft"), new VersionSetting(), null, true);
|
||||
Profile home = new Profile(Profiles.HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY);
|
||||
Profile home = new Profile(Profiles.HOME_PROFILE, Metadata.MINECRAFT_DIRECTORY.toFile());
|
||||
Platform.runLater(() -> profiles.addAll(current, home));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.game.HMCLCacheRepository;
|
||||
import org.jackhuang.hmcl.util.CacheRepository;
|
||||
|
||||
@@ -64,7 +64,7 @@ public class Settings {
|
||||
}
|
||||
|
||||
public static String getDefaultCommonDirectory() {
|
||||
return Launcher.MINECRAFT_DIRECTORY.getAbsolutePath();
|
||||
return Metadata.MINECRAFT_DIRECTORY.toString();
|
||||
}
|
||||
|
||||
public String getCommonDirectory() {
|
||||
|
||||
@@ -99,9 +99,10 @@ public final class Controllers {
|
||||
// FXThread
|
||||
public static AccountList getAccountListPage() {
|
||||
if (accountListPage == null) {
|
||||
accountListPage = new AccountList();
|
||||
AccountList accountListPage = new AccountList();
|
||||
accountListPage.selectedAccountProperty().bindBidirectional(Accounts.selectedAccountProperty());
|
||||
accountListPage.accountsProperty().bindContent(Accounts.accountsProperty());
|
||||
Controllers.accountListPage = accountListPage;
|
||||
}
|
||||
return accountListPage;
|
||||
}
|
||||
@@ -109,9 +110,10 @@ public final class Controllers {
|
||||
// FXThread
|
||||
public static ProfileList getProfileListPage() {
|
||||
if (profileListPage == null) {
|
||||
profileListPage = new ProfileList();
|
||||
ProfileList profileListPage = new ProfileList();
|
||||
profileListPage.selectedProfileProperty().bindBidirectional(Profiles.selectedProfileProperty());
|
||||
profileListPage.profilesProperty().bindContent(Profiles.profilesProperty());
|
||||
Controllers.profileListPage = profileListPage;
|
||||
}
|
||||
return profileListPage;
|
||||
}
|
||||
@@ -137,7 +139,7 @@ public final class Controllers {
|
||||
|
||||
public static MainPage getMainPage() {
|
||||
if (mainPage == null) {
|
||||
mainPage = new MainPage();
|
||||
MainPage mainPage = new MainPage();
|
||||
mainPage.setOnDragOver(event -> {
|
||||
if (event.getGestureSource() != mainPage && event.getDragboard().hasFiles()) {
|
||||
if (event.getDragboard().getFiles().stream().anyMatch(it -> "zip".equals(FileUtils.getExtension(it))))
|
||||
@@ -154,7 +156,7 @@ public final class Controllers {
|
||||
.collect(Collectors.toList());
|
||||
if (!modpacks.isEmpty()) {
|
||||
File modpack = modpacks.get(0);
|
||||
Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(modpack), i18n("install.modpack"));
|
||||
Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(Profiles.getSelectedProfile(), modpack), i18n("install.modpack"));
|
||||
event.setDropCompleted(true);
|
||||
}
|
||||
}
|
||||
@@ -177,7 +179,7 @@ public final class Controllers {
|
||||
HMCLGameRepository repository = profile.getRepository();
|
||||
List<Node> children = repository.getVersions().parallelStream()
|
||||
.filter(version -> !version.isHidden())
|
||||
.sorted(Comparator.comparing((Version version) -> version.getReleaseTime() == null ? new Date() : version.getReleaseTime())
|
||||
.sorted(Comparator.comparing((Version version) -> version.getReleaseTime() == null ? new Date(0L) : version.getReleaseTime())
|
||||
.thenComparing(a -> VersionNumber.asVersion(a.getId())))
|
||||
.map(version -> {
|
||||
Node node = PopupMenu.wrapPopupMenuItem(new GameItem(profile, version.getId()));
|
||||
@@ -190,6 +192,7 @@ public final class Controllers {
|
||||
mainPage.getVersions().setAll(children);
|
||||
});
|
||||
});
|
||||
Controllers.mainPage = mainPage;
|
||||
}
|
||||
return mainPage;
|
||||
}
|
||||
|
||||
@@ -28,24 +28,35 @@ import javafx.beans.property.*;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.setting.*;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChannel;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.reservedSelectedPropertyFor;
|
||||
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor;
|
||||
@@ -202,8 +213,29 @@ public final class SettingsPage extends SettingsView implements DecoratorPage {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOpenLogFolder() {
|
||||
FXUtils.openFolder(Launcher.LOG_DIRECTORY);
|
||||
protected void onExportLogs() {
|
||||
// We cannot determine which file is JUL using.
|
||||
// So we write all the logs to a new file.
|
||||
thread(() -> {
|
||||
Path logFile = Paths.get("hmcl-exported-logs-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".log").toAbsolutePath();
|
||||
|
||||
LOG.info("Exporting logs to " + logFile);
|
||||
try {
|
||||
Files.write(logFile, Logging.getRawLogs());
|
||||
} catch (IOException e) {
|
||||
Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.failed") + "\n" + e, null, MessageBox.ERROR_MESSAGE));
|
||||
LOG.log(Level.WARNING, "Failed to export logs", e);
|
||||
return;
|
||||
}
|
||||
|
||||
Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.success", logFile)));
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
try {
|
||||
Desktop.getDesktop().open(logFile.toFile());
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.jackhuang.hmcl.setting.EnumBackgroundImage;
|
||||
import org.jackhuang.hmcl.setting.EnumCommonDirectory;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
|
||||
|
||||
public abstract class SettingsView extends StackPane {
|
||||
@@ -337,8 +336,8 @@ public abstract class SettingsView extends StackPane {
|
||||
logPane.setTitle(i18n("settings.launcher.log"));
|
||||
|
||||
{
|
||||
JFXButton logButton = new JFXButton(i18n("settings.launcher.log.dir"));
|
||||
logButton.setOnMouseClicked(e -> onOpenLogFolder());
|
||||
JFXButton logButton = new JFXButton(i18n("settings.launcher.launcher_log.export"));
|
||||
logButton.setOnMouseClicked(e -> onExportLogs());
|
||||
logButton.getStyleClass().setAll("jfx-button-border");
|
||||
|
||||
logPane.setHeaderRight(logButton);
|
||||
@@ -492,5 +491,5 @@ public abstract class SettingsView extends StackPane {
|
||||
|
||||
protected abstract void onUpdate();
|
||||
protected abstract void onHelp();
|
||||
protected abstract void onOpenLogFolder();
|
||||
protected abstract void onExportLogs();
|
||||
}
|
||||
@@ -290,6 +290,8 @@ public class AddAccountPane extends StackPane {
|
||||
return exception.getMessage();
|
||||
} else if (exception instanceof AuthlibInjectorDownloadException) {
|
||||
return i18n("account.failed.injector_download_failure");
|
||||
} else if (exception.getClass() == AuthenticationException.class) {
|
||||
return exception.getLocalizedMessage();
|
||||
} else {
|
||||
return exception.getClass().getName() + ": " + exception.getLocalizedMessage();
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
@@ -40,19 +38,23 @@ public class Navigator extends StackPane {
|
||||
|
||||
private final Stack<Node> stack = new Stack<>();
|
||||
private final TransitionHandler animationHandler = new TransitionHandler(this);
|
||||
private final ReadOnlyBooleanWrapper canGoBack = new ReadOnlyBooleanWrapper();
|
||||
private boolean initialized = false;
|
||||
|
||||
public Navigator(Node init) {
|
||||
public void init(Node init) {
|
||||
stack.push(init);
|
||||
getChildren().setAll(init);
|
||||
|
||||
Platform.runLater(() ->
|
||||
fireEvent(new NavigationEvent(this, init, NavigationEvent.NAVIGATED)));
|
||||
fireEvent(new NavigationEvent(this, init, NavigationEvent.NAVIGATED));
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public void navigate(Node node) {
|
||||
FXUtils.checkFxUserThread();
|
||||
|
||||
if (!initialized)
|
||||
throw new IllegalStateException("Navigator must have a root page");
|
||||
|
||||
Node from = stack.peek();
|
||||
if (from == node)
|
||||
return;
|
||||
@@ -89,6 +91,9 @@ public class Navigator extends StackPane {
|
||||
public void close(Node from) {
|
||||
FXUtils.checkFxUserThread();
|
||||
|
||||
if (!initialized)
|
||||
throw new IllegalStateException("Navigator must have a root page");
|
||||
|
||||
Logging.LOG.info("Closed page " + from);
|
||||
|
||||
if (stack.peek() != from)
|
||||
|
||||
@@ -84,9 +84,10 @@ public class DecoratorController {
|
||||
decorator.titleProperty().set(Metadata.TITLE);
|
||||
decorator.setOnCloseButtonAction(Launcher::stopApplication);
|
||||
|
||||
navigator = new Navigator(mainPage);
|
||||
navigator = new Navigator();
|
||||
navigator.setOnNavigating(this::onNavigating);
|
||||
navigator.setOnNavigated(this::onNavigated);
|
||||
navigator.init(mainPage);
|
||||
|
||||
decorator.getContent().setAll(navigator);
|
||||
decorator.onCloseNavButtonActionProperty().set(e -> close());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.jackhuang.hmcl.ui.download;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Node;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.game.ModpackHelper;
|
||||
@@ -18,21 +19,20 @@ import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class ModpackInstallWizardProvider implements WizardProvider {
|
||||
private Profile profile;
|
||||
private final Profile profile;
|
||||
private final File file;
|
||||
|
||||
public ModpackInstallWizardProvider() {
|
||||
this(null);
|
||||
public ModpackInstallWizardProvider(Profile profile) {
|
||||
this(profile, null);
|
||||
}
|
||||
|
||||
public ModpackInstallWizardProvider(File modpackFile) {
|
||||
public ModpackInstallWizardProvider(Profile profile, File modpackFile) {
|
||||
this.profile = profile;
|
||||
this.file = modpackFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Map<String, Object> settings) {
|
||||
profile = Profiles.getSelectedProfile();
|
||||
|
||||
if (file != null)
|
||||
settings.put(ModpackPage.MODPACK_FILE, file);
|
||||
settings.put(PROFILE, profile);
|
||||
|
||||
@@ -33,14 +33,14 @@ import java.util.Map;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class VanillaInstallWizardProvider implements WizardProvider {
|
||||
private Profile profile;
|
||||
private final Profile profile;
|
||||
|
||||
public VanillaInstallWizardProvider() {
|
||||
public VanillaInstallWizardProvider(Profile profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Map<String, Object> settings) {
|
||||
profile = Profiles.getSelectedProfile();
|
||||
settings.put(PROFILE, profile);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -67,7 +68,8 @@ public class GameList extends Control implements DecoratorPage {
|
||||
toggleGroup.getProperties().put("ReferenceHolder", listenerHolder);
|
||||
List<GameListItem> children = repository.getVersions().parallelStream()
|
||||
.filter(version -> !version.isHidden())
|
||||
.sorted(Comparator.comparing(Version::getReleaseTime).thenComparing(a -> VersionNumber.asVersion(a.getId())))
|
||||
.sorted(Comparator.comparing((Version version) -> version.getReleaseTime() == null ? new Date(0L) : version.getReleaseTime())
|
||||
.thenComparing(a -> VersionNumber.asVersion(a.getId())))
|
||||
.map(version -> new GameListItem(toggleGroup, profile, version.getId()))
|
||||
.collect(Collectors.toList());
|
||||
JFXUtilities.runInFX(() -> {
|
||||
@@ -99,12 +101,18 @@ public class GameList extends Control implements DecoratorPage {
|
||||
return new GameListSkin(this);
|
||||
}
|
||||
|
||||
public void addNewGame() {
|
||||
Controllers.getDecorator().startWizard(new VanillaInstallWizardProvider(), i18n("install.new_game"));
|
||||
void addNewGame() {
|
||||
Profile profile = Profiles.getSelectedProfile();
|
||||
if (profile.getRepository().isLoaded()) {
|
||||
Controllers.getDecorator().startWizard(new VanillaInstallWizardProvider(profile), i18n("install.new_game"));
|
||||
}
|
||||
}
|
||||
|
||||
public void importModpack() {
|
||||
Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(), i18n("install.modpack"));
|
||||
void importModpack() {
|
||||
Profile profile = Profiles.getSelectedProfile();
|
||||
if (profile.getRepository().isLoaded()) {
|
||||
Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(profile), i18n("install.modpack"));
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
|
||||
@@ -98,7 +98,8 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
new IconedMenuItem(null, i18n("version.manage.rename"), FXUtils.withJFXPopupClosing(() -> Versions.renameVersion(profile, version), managementPopup)),
|
||||
new IconedMenuItem(null, i18n("version.manage.remove"), FXUtils.withJFXPopupClosing(() -> Versions.deleteVersion(profile, version), managementPopup)),
|
||||
new IconedMenuItem(null, i18n("version.manage.redownload_assets_index"), FXUtils.withJFXPopupClosing(() -> new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getResolvedVersion(version)).start(), managementPopup)),
|
||||
new IconedMenuItem(null, i18n("version.manage.remove_libraries"), FXUtils.withJFXPopupClosing(() -> FileUtils.deleteDirectoryQuietly(new File(profile.getRepository().getBaseDirectory(), "libraries")), managementPopup))
|
||||
new IconedMenuItem(null, i18n("version.manage.remove_libraries"), FXUtils.withJFXPopupClosing(() -> FileUtils.deleteDirectoryQuietly(new File(profile.getRepository().getBaseDirectory(), "libraries")), managementPopup)),
|
||||
new IconedMenuItem(null, i18n("version.manage.clean"), FXUtils.withJFXPopupClosing(() -> Versions.cleanVersion(profile, version), managementPopup))
|
||||
);
|
||||
|
||||
FXUtils.installTooltip(btnDelete, i18n("version.manage.remove"));
|
||||
|
||||
@@ -35,12 +35,14 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.ui.export.ExportWizardProvider;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -100,6 +102,14 @@ public class Versions {
|
||||
}
|
||||
}
|
||||
|
||||
public static void cleanVersion(Profile profile, String id) {
|
||||
try {
|
||||
profile.getRepository().clean(id);
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to clean game directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void generateLaunchScript(Profile profile, String id) {
|
||||
if (checkForLaunching(profile, id)) {
|
||||
GameRepository repository = profile.getRepository();
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParseException;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.layout.Region;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
@@ -231,7 +230,7 @@ public final class UpdateHandler {
|
||||
private static boolean isFirstLaunchAfterUpgrade() {
|
||||
Optional<Path> currentPath = JarUtils.thisJar();
|
||||
if (currentPath.isPresent()) {
|
||||
Path updated = Launcher.HMCL_DIRECTORY.toPath().resolve("HMCL-" + Metadata.VERSION + ".jar");
|
||||
Path updated = Metadata.HMCL_DIRECTORY.resolve("HMCL-" + Metadata.VERSION + ".jar");
|
||||
if (currentPath.get().toAbsolutePath().equals(updated.toAbsolutePath())) {
|
||||
return true;
|
||||
}
|
||||
@@ -240,7 +239,7 @@ public final class UpdateHandler {
|
||||
}
|
||||
|
||||
private static void breakForceUpdateFeature() {
|
||||
Path hmclVersionJson = Launcher.HMCL_DIRECTORY.toPath().resolve("hmclver.json");
|
||||
Path hmclVersionJson = Metadata.HMCL_DIRECTORY.resolve("hmclver.json");
|
||||
if (Files.isRegularFile(hmclVersionJson)) {
|
||||
try {
|
||||
Map<?, ?> content = new Gson().fromJson(new String(Files.readAllBytes(hmclVersionJson), UTF_8), Map.class);
|
||||
|
||||
@@ -67,10 +67,12 @@
|
||||
</body>
|
||||
<actions>
|
||||
<Label fx:id="lblCreationWarning"/>
|
||||
<SpinnerPane fx:id="acceptPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAccept" onMouseClicked="#onCreationAccept" text="%button.ok" styleClass="dialog-accept"/>
|
||||
</SpinnerPane>
|
||||
<JFXButton onMouseClicked="#onCreationCancel" text="%button.cancel" styleClass="dialog-cancel"/>
|
||||
<HBox>
|
||||
<SpinnerPane fx:id="acceptPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAccept" onMouseClicked="#onCreationAccept" text="%button.ok" styleClass="dialog-accept"/>
|
||||
</SpinnerPane>
|
||||
<JFXButton onMouseClicked="#onCreationCancel" text="%button.cancel" styleClass="dialog-cancel"/>
|
||||
</HBox>
|
||||
</actions>
|
||||
</JFXDialogLayout>
|
||||
</fx:root>
|
||||
@@ -19,10 +19,12 @@
|
||||
</body>
|
||||
<actions>
|
||||
<Label fx:id="lblCreationWarning" />
|
||||
<JFXButton onMouseClicked="#onAddCancel" text="%button.cancel" styleClass="dialog-cancel" />
|
||||
<SpinnerPane fx:id="nextPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAddNext" onMouseClicked="#onAddNext" text="%wizard.next" styleClass="dialog-accept" />
|
||||
</SpinnerPane>
|
||||
<HBox>
|
||||
<JFXButton onMouseClicked="#onAddCancel" text="%button.cancel" styleClass="dialog-cancel" />
|
||||
<SpinnerPane fx:id="nextPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAddNext" onMouseClicked="#onAddNext" text="%wizard.next" styleClass="dialog-accept" />
|
||||
</SpinnerPane>
|
||||
</HBox>
|
||||
</actions>
|
||||
</JFXDialogLayout>
|
||||
|
||||
|
||||
@@ -326,9 +326,11 @@ settings.launcher.common_path.tooltip=This app will save all game libraries and
|
||||
settings.launcher.download_source=Download Source
|
||||
settings.launcher.enable_game_list=Display game list in main page
|
||||
settings.launcher.language=Language
|
||||
settings.launcher.launcher_log.export=Export launcher logs
|
||||
settings.launcher.launcher_log.export.failed=Failed to export logs
|
||||
settings.launcher.launcher_log.export.success=Logs have been exported to %s
|
||||
settings.launcher.log=Log
|
||||
settings.launcher.log.font=Log Font
|
||||
settings.launcher.log.dir=Open Log Directory
|
||||
settings.launcher.proxy=Proxy
|
||||
settings.launcher.proxy.authentication=Proxy Authentication
|
||||
settings.launcher.proxy.disable=Use system proxies
|
||||
@@ -380,6 +382,7 @@ 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=Game List
|
||||
version.manage.clean=Clean game directory
|
||||
version.manage.manage=Manage Game
|
||||
version.manage.redownload_assets_index=Redownload Assets Index
|
||||
version.manage.remove=Delete this game
|
||||
|
||||
@@ -326,9 +326,11 @@ settings.launcher.common_path.tooltip=啟動器將所有遊戲資源及依賴庫
|
||||
settings.launcher.download_source=下載來源
|
||||
settings.launcher.enable_game_list=在首頁內顯示遊戲列表
|
||||
settings.launcher.language=語言
|
||||
settings.launcher.launcher_log.export=導出啟動器日誌
|
||||
settings.launcher.launcher_log.export.failed=無法導出日誌
|
||||
settings.launcher.launcher_log.export.success=日誌已保存到 %s
|
||||
settings.launcher.log=記錄
|
||||
settings.launcher.log.font=記錄字體
|
||||
settings.launcher.log.dir=打開記錄資料夾
|
||||
settings.launcher.proxy=代理
|
||||
settings.launcher.proxy.authentication=身份驗證
|
||||
settings.launcher.proxy.disable=使用系統代理
|
||||
@@ -380,6 +382,7 @@ version.launch_script.failed=生成啟動腳本失敗
|
||||
version.launch_script.save=儲存啟動腳本
|
||||
version.launch_script.success=啟動腳本已生成完畢:%s
|
||||
version.manage=遊戲列表
|
||||
version.manage.clean=清理遊戲目錄
|
||||
version.manage.manage=游戏管理
|
||||
version.manage.redownload_assets_index=更新資源列表
|
||||
version.manage.remove=刪除該版本
|
||||
|
||||
@@ -326,9 +326,11 @@ settings.launcher.common_path.tooltip=启动器将所有游戏资源及依赖库
|
||||
settings.launcher.download_source=下载源
|
||||
settings.launcher.enable_game_list=在主页内显示游戏列表
|
||||
settings.launcher.language=语言
|
||||
settings.launcher.launcher_log.export=导出启动器日志
|
||||
settings.launcher.launcher_log.export.failed=无法导出日志
|
||||
settings.launcher.launcher_log.export.success=日志已保存到 %s
|
||||
settings.launcher.log=日志
|
||||
settings.launcher.log.font=日志字体
|
||||
settings.launcher.log.dir=打开日志文件夹
|
||||
settings.launcher.proxy=代理
|
||||
settings.launcher.proxy.authentication=身份验证
|
||||
settings.launcher.proxy.disable=使用系统代理
|
||||
@@ -380,6 +382,7 @@ version.launch_script.failed=生成启动脚本失败
|
||||
version.launch_script.save=保存启动脚本
|
||||
version.launch_script.success=启动脚本已生成完毕:%s
|
||||
version.manage=游戏列表
|
||||
version.manage.clean=清理游戏目录
|
||||
version.manage.manage=游戏管理
|
||||
version.manage.redownload_assets_index=更新资源列表
|
||||
version.manage.remove=删除该版本
|
||||
|
||||
@@ -43,7 +43,7 @@ public class DefaultCacheRepository extends CacheRepository {
|
||||
private Index index = null;
|
||||
|
||||
public DefaultCacheRepository() {
|
||||
this(OperatingSystem.getWorkingDirectory("minecraft").toPath());
|
||||
this(OperatingSystem.getWorkingDirectory("minecraft"));
|
||||
}
|
||||
|
||||
public DefaultCacheRepository(Path commonDirectory) {
|
||||
|
||||
@@ -61,7 +61,7 @@ public class Version implements Comparable<Version>, Validation {
|
||||
this.jar = jar;
|
||||
this.assetIndex = assetIndex;
|
||||
this.assets = assets;
|
||||
this.libraries = new LinkedList<>(libraries);
|
||||
this.libraries = libraries == null ? new LinkedList<>() : new LinkedList<>(libraries);
|
||||
this.compatibilityRules = compatibilityRules == null ? null : new LinkedList<>(compatibilityRules);
|
||||
this.downloads = downloads == null ? null : new HashMap<>(downloads);
|
||||
this.logging = logging == null ? null : new HashMap<>(logging);
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.*;
|
||||
@@ -31,20 +32,16 @@ import java.util.logging.*;
|
||||
*/
|
||||
public final class Logging {
|
||||
|
||||
public static final Logger LOG;
|
||||
private static final ByteArrayOutputStream OUTPUT_STREAM = new ByteArrayOutputStream();
|
||||
public static final Logger LOG = Logger.getLogger("HMCL");
|
||||
private static ByteArrayOutputStream storedLogs = new ByteArrayOutputStream();
|
||||
|
||||
static {
|
||||
LOG = Logger.getLogger("HMCL");
|
||||
}
|
||||
|
||||
public static void start(File logFolder) {
|
||||
LOG.setLevel(Level.FINER);
|
||||
public static void start(Path logFolder) {
|
||||
LOG.setLevel(Level.ALL);
|
||||
LOG.setUseParentHandlers(false);
|
||||
|
||||
try {
|
||||
FileHandler fileHandler = new FileHandler(new File(logFolder, "hmcl.log").getAbsolutePath());
|
||||
fileHandler.setLevel(Level.FINEST);
|
||||
Files.createDirectories(logFolder);
|
||||
FileHandler fileHandler = new FileHandler(logFolder.resolve("hmcl.log").toAbsolutePath().toString());
|
||||
fileHandler.setFormatter(DefaultFormatter.INSTANCE);
|
||||
LOG.addHandler(fileHandler);
|
||||
} catch (IOException e) {
|
||||
@@ -52,31 +49,28 @@ public final class Logging {
|
||||
}
|
||||
|
||||
ConsoleHandler consoleHandler = new ConsoleHandler();
|
||||
consoleHandler.setLevel(Level.FINER);
|
||||
consoleHandler.setFormatter(DefaultFormatter.INSTANCE);
|
||||
LOG.addHandler(consoleHandler);
|
||||
|
||||
StreamHandler streamHandler = new StreamHandler(OUTPUT_STREAM, DefaultFormatter.INSTANCE) {
|
||||
StreamHandler streamHandler = new StreamHandler(storedLogs, DefaultFormatter.INSTANCE) {
|
||||
@Override
|
||||
public synchronized void publish(LogRecord record) {
|
||||
super.publish(record);
|
||||
flush();
|
||||
}
|
||||
};
|
||||
streamHandler.setLevel(Level.FINEST);
|
||||
LOG.addHandler(streamHandler);
|
||||
}
|
||||
|
||||
public static void stop() {
|
||||
for (Handler handler : LOG.getHandlers())
|
||||
LOG.removeHandler(handler);
|
||||
public static byte[] getRawLogs() {
|
||||
return storedLogs.toByteArray();
|
||||
}
|
||||
|
||||
public static String getLogs() {
|
||||
return OUTPUT_STREAM.toString();
|
||||
return storedLogs.toString();
|
||||
}
|
||||
|
||||
static final class DefaultFormatter extends Formatter {
|
||||
private static final class DefaultFormatter extends Formatter {
|
||||
|
||||
static final DefaultFormatter INSTANCE = new DefaultFormatter();
|
||||
private final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform;
|
||||
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
|
||||
import javax.management.JMException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
@@ -29,6 +26,8 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -135,30 +134,24 @@ public enum OperatingSystem {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static void setClipboard(String string) {
|
||||
ClipboardContent c = new ClipboardContent();
|
||||
c.putString(string);
|
||||
Clipboard.getSystemClipboard().setContent(c);
|
||||
}
|
||||
|
||||
public static void forceGC() {
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
|
||||
public static File getWorkingDirectory(String folder) {
|
||||
public static Path getWorkingDirectory(String folder) {
|
||||
String home = System.getProperty("user.home", ".");
|
||||
switch (OperatingSystem.CURRENT_OS) {
|
||||
case LINUX:
|
||||
return new File(home, "." + folder + "/");
|
||||
return Paths.get(home, "." + folder);
|
||||
case WINDOWS:
|
||||
String appdata = System.getenv("APPDATA");
|
||||
return new File(Lang.nonNull(appdata, home), "." + folder + "/");
|
||||
return Paths.get(Lang.nonNull(appdata, home), "." + folder);
|
||||
case OSX:
|
||||
return new File(home, "Library/Application Support/" + folder);
|
||||
return Paths.get(home, "Library", "Application Support", folder);
|
||||
default:
|
||||
return new File(home, folder + "/");
|
||||
return Paths.get(home, folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user