Compare commits

...
23 Commits
Author SHA1 Message Date
huanghongxun c9d825b6c1 Add a button for cleaning crash-reports and logs manually 2018-10-23 14:08:51 +08:00
huanghongxun 7dcd0eb616 Only display installer when game repository finished loading 2018-10-23 13:52:47 +08:00
huanghongxun e79a95b885 Fix NPE when version json missing "libraries" 2018-10-23 13:42:16 +08:00
huanghongxun 5d232c3f5e Fix NPE when app stopped 2018-10-23 13:36:45 +08:00
huanghongxun ee1c0d362d Fix NPE 2018-10-23 13:34:37 +08:00
huanghongxun aafcd57c82 Attempt to solve #481 2018-10-23 13:32:25 +08:00
huanghongxun 66a4566dc5 Update JFoenix.jar 2018-10-21 11:26:07 +08:00
huanghongxun 4c317f5b03 Merge branch 'javafx' of https://github.com/huanghongxun/HMCL into javafx 2018-10-21 11:05:33 +08:00
huanghongxun 12d706a812 Don't show AuthenticationException class name 2018-10-21 11:05:27 +08:00
yushijinhun c006068580 Keep 'Next' button and 'Cancel' button on the same line 2018-10-20 21:33:34 +08:00
yushijinhun f8a838506d Replace 'Open logs folder' with 'Export logs'
Related: 8905f2e855
2018-10-20 17:52:06 +08:00
yushijinhun 270844bbb1 Start logging earlier 2018-10-20 17:44:43 +08:00
yushijinhun c9466cf8ef Move MINECRAFT_DIRECTORY & HMCL_DIRECTORY to Metadata 2018-10-20 17:43:35 +08:00
yushijinhun 7d6f1f98f1 Fix verison comparing
Related: fb9418dead
2018-10-20 15:56:14 +08:00
huanghongxun fb9418dead Fixed NPE 2018-10-17 13:51:49 +08:00
huanghongxun 4a1b06b80c Update I18N 2018-10-16 20:32:52 +08:00
huanghongxun 8905f2e855 Add a button to open log directory 2018-10-16 00:20:56 +08:00
huanghongxun 6c74364fa5 Fix writeText 2018-10-15 22:27:26 +08:00
huanghongxun f0720c1755 Merge branch 'javafx' of https://github.com/huanghongxun/HMCL into javafx 2018-10-15 22:08:31 +08:00
huanghongxun f1acc1d2c1 Fix NPE 2018-10-15 13:05:32 +08:00
Haowei Wen 82bddbfa6e Merge pull request #474 from outloudvi/patch-1
Fix opening resourcepacks/ while opening saves/
2018-10-15 00:26:53 +08:00
Outvi V 841755fe56 Fix opening resourcepacks/ while opening saves/ 2018-10-14 22:39:22 +08:00
huanghongxun 340ee751e8 Suppress VersionNotFoundException 2018-10-14 19:10:04 +08:00
37 changed files with 283 additions and 135 deletions

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
# 公告
提交前请确保你的启动器版本是**最新的开发版**,可以在启动器设置中更换更新通道至开发版获取更新。
**如果你在阅读完本公告后仍然希望在此处发布 issue,请将公告内容删去,并按照下面的模板填入相关信息。**
我们发现,对于不经常检查邮箱的人来说,GitHub issues 的反馈效率太低,时隔几个小时甚至一天的情况很多。为了改善反馈效率,并将积极为 HMCL 提供问题反馈和建议的人聚集起来,我们希望在这里反馈的人可以在 HMCL 3 用户 QQ 群:219177735 中反馈信息,**而不在 GitHub Issues 中反馈问题**。
如果你希望在 QQ 群中反馈问题,也请将下面的信息表填好直接发在群中,加快我们的沟通速度。
# 问题提交
*完整地填下面的问题提交表对我们很重要,这可以加快我们分析问题原因的速度。*
* 启动器版本:
* 操作系统:
* Java 版本:
* 错误截图(最好请将整个电脑屏幕的截图发上来):
* 游戏版本(如果是启动通过启动器自带的自动安装功能安装的游戏):
* 对游戏做的修改(是否自行通过安装器安装 Rift 等 API,以及 mod):
* 游戏崩溃报告(如果有):
* 启动器崩溃报告(如果有):
* 启动器日志文件(在启动器设置中打开日志文件夹并将文件夹内所有文件打包发在这里):
* 问题描述(如何触发问题):
@@ -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() {
@@ -51,6 +51,7 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.File;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -98,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;
}
@@ -108,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;
}
@@ -136,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))))
@@ -153,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);
}
}
@@ -176,7 +179,8 @@ public final class Controllers {
HMCLGameRepository repository = profile.getRepository();
List<Node> 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 -> {
Node node = PopupMenu.wrapPopupMenuItem(new GameItem(profile, version.getId()));
node.setOnMouseClicked(e -> profile.setSelectedVersion(version.getId()));
@@ -188,6 +192,7 @@ public final class Controllers {
mainPage.getVersions().setAll(children);
});
});
Controllers.mainPage = mainPage;
}
return mainPage;
}
@@ -30,21 +30,33 @@ import javafx.scene.paint.Color;
import javafx.scene.text.Font;
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;
@@ -200,6 +212,32 @@ public final class SettingsPage extends SettingsView implements DecoratorPage {
UpdateHandler.updateFrom(target);
}
@Override
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
protected void onHelp() {
FXUtils.openLink(Metadata.HELP_URL);
@@ -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 {
@@ -333,34 +332,49 @@ public abstract class SettingsView extends StackPane {
}
{
VBox fontPane = new VBox();
fontPane.setSpacing(5);
settingsPane.getContent().add(fontPane);
ComponentSublist logPane = new ComponentSublist();
logPane.setTitle(i18n("settings.launcher.log"));
{
BorderPane borderPane = new BorderPane();
fontPane.getChildren().add(borderPane);
{
Label left = new Label(i18n("settings.launcher.log_font"));
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
borderPane.setLeft(left);
}
JFXButton logButton = new JFXButton(i18n("settings.launcher.launcher_log.export"));
logButton.setOnMouseClicked(e -> onExportLogs());
logButton.getStyleClass().setAll("jfx-button-border");
{
HBox hBox = new HBox();
hBox.setSpacing(3);
cboFont = new FontComboBox(12, false);
txtFontSize = new JFXTextField();
FXUtils.setLimitWidth(txtFontSize, 50);
hBox.getChildren().setAll(cboFont, txtFontSize);
borderPane.setRight(hBox);
}
logPane.setHeaderRight(logButton);
}
lblDisplay = new Label("[23:33:33] [Client Thread/INFO] [WaterPower]: Loaded mod WaterPower.");
fontPane.getChildren().add(lblDisplay);
{
VBox fontPane = new VBox();
fontPane.setSpacing(5);
{
BorderPane borderPane = new BorderPane();
fontPane.getChildren().add(borderPane);
{
Label left = new Label(i18n("settings.launcher.log.font"));
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
borderPane.setLeft(left);
}
{
HBox hBox = new HBox();
hBox.setSpacing(3);
cboFont = new FontComboBox(12, false);
txtFontSize = new JFXTextField();
FXUtils.setLimitWidth(txtFontSize, 50);
hBox.getChildren().setAll(cboFont, txtFontSize);
borderPane.setRight(hBox);
}
}
lblDisplay = new Label("[23:33:33] [Client Thread/INFO] [WaterPower]: Loaded mod WaterPower.");
fontPane.getChildren().add(lblDisplay);
logPane.getContent().add(fontPane);
}
settingsPane.getContent().add(logPane);
}
{
@@ -477,4 +491,5 @@ public abstract class SettingsView extends StackPane {
protected abstract void onUpdate();
protected abstract void onHelp();
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();
}
@@ -88,11 +88,16 @@ class ComponentListCell extends StackPane {
VBox labelVBox = new VBox();
labelVBox.setAlignment(Pos.CENTER_LEFT);
boolean overrideHeaderLeft = false;
if (list instanceof ComponentSublist) {
Node leftNode = ((ComponentSublist) list).getHeaderLeft();
if (leftNode != null)
if (leftNode != null) {
labelVBox.getChildren().setAll(leftNode);
} else {
overrideHeaderLeft = true;
}
}
if (!overrideHeaderLeft) {
Label label = new Label();
label.textProperty().bind(list.titleProperty());
labelVBox.getChildren().add(label);
@@ -108,6 +113,7 @@ class ComponentListCell extends StackPane {
groupNode.setLeft(labelVBox);
HBox right = new HBox();
right.setSpacing(16);
right.setAlignment(Pos.CENTER_RIGHT);
if (list instanceof ComponentSublist) {
Node rightNode = ((ComponentSublist) list).getHeaderRight();
@@ -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);
}
@@ -30,7 +30,8 @@ public class GameAdvancedListItem extends AdvancedListItem {
public GameAdvancedListItem() {
FXUtils.onChangeAndOperate(Profiles.selectedVersionProperty(), version -> {
if (version != null) {
if (version != null && Profiles.getSelectedProfile() != null &&
Profiles.getSelectedProfile().getRepository().hasVersion(version)) {
setTitle(version);
setSubtitle(null);
setImage(Profiles.getSelectedProfile().getRepository().getVersionIconImage(version));
@@ -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() {
@@ -89,7 +89,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
new IconedMenuItem(null, i18n("folder.config"), FXUtils.withJFXPopupClosing(() -> onBrowse("config"), browsePopup)),
new IconedMenuItem(null, i18n("folder.resourcepacks"), FXUtils.withJFXPopupClosing(() -> onBrowse("resourcepacks"), browsePopup)),
new IconedMenuItem(null, i18n("folder.screenshots"), FXUtils.withJFXPopupClosing(() -> onBrowse("screenshots"), browsePopup)),
new IconedMenuItem(null, i18n("folder.saves"), FXUtils.withJFXPopupClosing(() -> onBrowse("resourcepacks"), browsePopup))
new IconedMenuItem(null, i18n("folder.saves"), FXUtils.withJFXPopupClosing(() -> onBrowse("saves"), browsePopup))
);
PopupMenu managementList = new PopupMenu();
@@ -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);
+17 -1
View File
@@ -546,7 +546,7 @@
.jfx-button-raised {
-fx-text-fill: white;
-fx-background-color: -fx-base-color;
-fx-font-size:14px;
-fx-font-size: 14px;
}
.jfx-button-raised .jfx-rippler {
@@ -558,6 +558,22 @@
-fx-font-size: 14px;
}
.jfx-button-border {
-fx-text-fill: -fx-base-color;
-fx-border-color: gray;
-fx-border-radius: 5px;
-fx-border-width: 0.2px;
-fx-padding: 8px;
}
.jfx-button-border .jfx-rippler {
-jfx-rippler-fill: -fx-base-check-color;
}
.jfx-button-border .label {
-fx-text-fill: -fx-base-color;
}
.jfx-button-raised-round {
-fx-background-color: -fx-base-color;
-fx-background-radius: 50px;
@@ -29,7 +29,7 @@
<Label fx:id="lblInjectorServer" text="%account.injector.server" GridPane.halignment="RIGHT"
GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<JFXComboBox fx:id="cboServers" promptText="%general.prompt.empty" maxHeight="25" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<JFXComboBox fx:id="cboServers" promptText="%account.injector.empty" maxHeight="25" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<HBox GridPane.columnIndex="2" GridPane.rowIndex="1" spacing="8">
<JFXButton fx:id="btnAddServer" styleClass="toggle-icon4" onMouseClicked="#onAddInjecterServer">
@@ -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>
@@ -22,7 +22,7 @@
<BorderPane><left><Label text="%archive.version"/></left><right><Label fx:id="lblVersion" /></right></BorderPane>
<BorderPane><left><Label text="%archive.author"/></left><right><Label fx:id="lblAuthor" /></right></BorderPane>
<BorderPane>
<left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.wizard.step.3" styleClass="jfx-button" /></left>
<left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.wizard.step.3" styleClass="jfx-button-border" /></left>
<right><JFXButton buttonType="RAISED" fx:id="btnInstall" onMouseClicked="#onInstall" text="%button.install" styleClass="jfx-button-raised" /></right>
</BorderPane>
</ComponentList>
@@ -43,6 +43,7 @@ account.failed.invalid_token=Please log out and re-input your password to login.
account.failed.no_character=No character in this account.
account.failed.server_response_malformed=Invalid server response. The authentication server may have a failure.
account.injector.add=Add an authentication server
account.injector.empty=Empty (Click the plus button right to add)
account.injector.manage=Manage authentication servers
account.injector.manage.title=Authentication servers
account.injector.http=Warning: This server is using HTTP, which will cause your password be transmitted in clear text.
@@ -109,8 +110,6 @@ folder.resourcepacks=Resource packs
folder.saves=Saves
folder.screenshots=Screenshots
general.prompt.empty=(None)
help=Documentations
help.detail=For datapacks, modpacks, etc. makers.
@@ -327,7 +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.log_font=Log Font
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.proxy=Proxy
settings.launcher.proxy.authentication=Proxy Authentication
settings.launcher.proxy.disable=Use system proxies
@@ -379,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
@@ -43,6 +43,7 @@ account.failed.invalid_token=請嘗試登出並重新輸入密碼登入
account.failed.no_character=該帳號沒有角色
account.failed.server_response_malformed=無法解析認證伺服器響應,可能是伺服器故障
account.injector.add=新增認證伺服器
account.injector.empty=無(點擊右側加號添加)
account.injector.manage=管理認證伺服器
account.injector.manage.title=認證伺服器
account.injector.http=警告:此伺服器使用不安全的 HTTP 協議,您的密碼在登入時會被明文傳輸。
@@ -109,8 +110,6 @@ folder.resourcepacks=資源包資料夾
folder.saves=遊戲存檔資料夾
folder.screenshots=截圖資料夾
general.prompt.empty=(無)
help=HMCL 幫助文檔
help.detail=可查閱資料包、整合包製作指南等內容。
@@ -327,7 +326,11 @@ settings.launcher.common_path.tooltip=啟動器將所有遊戲資源及依賴庫
settings.launcher.download_source=下載來源
settings.launcher.enable_game_list=在首頁內顯示遊戲列表
settings.launcher.language=語言
settings.launcher.log_font=記錄字體
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.proxy=代理
settings.launcher.proxy.authentication=身份驗證
settings.launcher.proxy.disable=使用系統代理
@@ -379,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,6 +43,7 @@ account.failed.invalid_token=请尝试登出并重新输入密码登录
account.failed.no_character=该帐号没有角色
account.failed.server_response_malformed=无法解析认证服务器响应,可能是服务器故障
account.injector.add=添加认证服务器
account.injector.empty=无(点击右侧加号添加)
account.injector.manage=管理认证服务器
account.injector.manage.title=认证服务器
account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您的密码在登录时会被明文传输。
@@ -109,8 +110,6 @@ folder.resourcepacks=资源包文件夹
folder.saves=存档文件夹
folder.screenshots=截图文件夹
general.prompt.empty=(无)
help=HMCL 帮助文档
help.detail=可查阅数据包、整合包制作指南等内容。
@@ -327,7 +326,11 @@ settings.launcher.common_path.tooltip=启动器将所有游戏资源及依赖库
settings.launcher.download_source=下载源
settings.launcher.enable_game_list=在主页内显示游戏列表
settings.launcher.language=语言
settings.launcher.log_font=日志字体
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.proxy=代理
settings.launcher.proxy.authentication=身份验证
settings.launcher.proxy.disable=使用系统代理
@@ -379,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");
@@ -95,7 +95,7 @@ public final class FileUtils {
public static void writeBytes(File file, byte[] array) throws IOException {
Files.createDirectories(file.toPath().getParent());
Files.write(file.toPath(), array, StandardOpenOption.CREATE);
Files.write(file.toPath(), array);
}
public static void deleteDirectory(File directory)
@@ -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);
}
}
}
BIN
View File
Binary file not shown.