diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index c7e70fa56..26c637e0b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -19,13 +19,24 @@ package org.jackhuang.hmcl.game; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; import javafx.scene.image.Image; +import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.mod.Modpack; +import org.jackhuang.hmcl.mod.ModpackConfiguration; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackLocalInstallTask; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.ProxyManager; import org.jackhuang.hmcl.setting.VersionSetting; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Logging; +import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.platform.JavaVersion; import java.io.File; import java.io.IOException; @@ -33,6 +44,7 @@ import java.nio.file.Files; import java.util.*; import java.util.logging.Level; +import static org.jackhuang.hmcl.setting.ConfigHolder.config; import static org.jackhuang.hmcl.ui.FXUtils.newImage; public class HMCLGameRepository extends DefaultGameRepository { @@ -273,6 +285,54 @@ public class HMCLGameRepository extends DefaultGameRepository { return FORBIDDEN.contains(id); } + public LaunchOptions getLaunchOptions(String version, File gameDir, boolean checkJava) throws InterruptedException { + VersionSetting vs = getVersionSetting(version); + + JavaVersion javaVersion = Optional.ofNullable(vs.getJavaVersion(checkJava)).orElse(JavaVersion.fromCurrentEnvironment()); + LaunchOptions.Builder builder = new LaunchOptions.Builder() + .setGameDir(gameDir) + .setJava(javaVersion) + .setVersionName(Metadata.TITLE) + .setVersionType(Metadata.TITLE) + .setProfileName(Metadata.TITLE) + .setGameArguments(StringUtils.tokenize(vs.getMinecraftArgs())) + .setJavaArguments(StringUtils.tokenize(vs.getJavaArgs())) + .setMaxMemory(vs.getMaxMemory()) + .setMinMemory(vs.getMinMemory()) + .setMetaspace(Lang.toIntOrNull(vs.getPermSize())) + .setWidth(vs.getWidth()) + .setHeight(vs.getHeight()) + .setFullscreen(vs.isFullscreen()) + .setServerIp(vs.getServerIp()) + .setWrapper(vs.getWrapper()) + .setPrecalledCommand(vs.getPreLaunchCommand()) + .setNoGeneratedJVMArgs(vs.isNoJVMArgs()); + if (config().hasProxy()) { + builder.setProxy(ProxyManager.getProxy()); + if (config().hasProxyAuth()) { + builder.setProxyUser(config().getProxyUser()); + builder.setProxyPass(config().getProxyPass()); + } + } + + File json = getModpackConfiguration(version); + if (json.exists()) { + try { + String jsonText = FileUtils.readText(json); + ModpackConfiguration modpackConfiguration = JsonUtils.GSON.fromJson(jsonText, ModpackConfiguration.class); + if (McbbsModpackLocalInstallTask.MODPACK_TYPE.equals(modpackConfiguration.getType())) { + ModpackConfiguration config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + config.getManifest().injectLaunchOptions(builder); + } + } catch (IOException | JsonParseException e) { + e.printStackTrace(); + } + } + + return builder.create(); + } + @Override public File getModpackConfiguration(String version) { return new File(getVersionRoot(version), "modpack.cfg"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackExportTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackExportTask.java deleted file mode 100644 index b9ebff032..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackExportTask.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * 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 . - */ -package org.jackhuang.hmcl.game; - -import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.Logging; -import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.io.Zipper; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Export the game to a mod pack file. - */ -public class HMCLModpackExportTask extends Task { - private final DefaultGameRepository repository; - private final String version; - private final List whitelist; - private final Modpack modpack; - private final File output; - - /** - * @param output mod pack file. - * @param version to locate version.json - */ - public HMCLModpackExportTask(DefaultGameRepository repository, String version, List whitelist, Modpack modpack, File output) { - this.repository = repository; - this.version = version; - this.whitelist = whitelist; - this.modpack = modpack; - this.output = output; - - onDone().register(event -> { - if (event.isFailed()) output.delete(); - }); - } - - @Override - public void execute() throws Exception { - ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); - blackList.add(version + ".jar"); - blackList.add(version + ".json"); - Logging.LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); - try (Zipper zip = new Zipper(output.toPath())) { - zip.putDirectory(repository.getRunDirectory(version).toPath(), "minecraft", path -> Modpack.acceptFile(path, blackList, whitelist)); - - Version mv = repository.getResolvedPreservingPatchesVersion(version); - String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version)) - .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); - zip.putTextFile(JsonUtils.GSON.toJson(mv.setJar(gameVersion)), "minecraft/pack.json"); // Making "jar" to gameVersion is to be compatible with old HMCL. - zip.putTextFile(JsonUtils.GSON.toJson(modpack.setGameVersion(gameVersion)), "modpack.json"); // Newer HMCL only reads 'gameVersion' field. - } - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 1d54cdc15..2274efeec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -169,7 +169,7 @@ public final class LauncherHelper { repository, version.getPatches().isEmpty() ? repository.getResolvedVersion(selectedVersion) : version, authInfo, - setting.toLaunchOptions(profile.getGameDir(), !setting.isNotCheckJVM()), + repository.getLaunchOptions(selectedVersion, profile.getGameDir(), !setting.isNotCheckJVM()), launcherVisibility == LauncherVisibility.CLOSE ? null // Unnecessary to start listening to game process output when close launcher immediately after game launched. : new HMCLProcessListener(repository, selectedVersion, authInfo, launchingLatch, gameVersion.isPresent()) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 119b5a8cf..81b1d592c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -23,6 +23,8 @@ import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.mod.curse.CurseCompletionException; import org.jackhuang.hmcl.mod.curse.CurseInstallTask; import org.jackhuang.hmcl.mod.curse.CurseManifest; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackLocalInstallTask; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest; import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration; import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask; import org.jackhuang.hmcl.mod.server.ServerModpackLocalInstallTask; @@ -43,12 +45,20 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Path; +import java.util.Objects; import java.util.Optional; public final class ModpackHelper { private ModpackHelper() {} public static Modpack readModpackManifest(Path file, Charset charset) throws UnsupportedModpackException { + try { + return McbbsModpackManifest.readManifest(file, charset); + } catch (Exception ignored) { + ignored.printStackTrace(); + // ignore it, not a valid MCBBS modpack. + } + try { return CurseManifest.readCurseForgeModpackManifest(file, charset); } catch (Exception e) { @@ -148,12 +158,16 @@ public final class ModpackHelper { return new HMCLModpackInstallTask(profile, zipFile, modpack, name) .whenComplete(Schedulers.defaultScheduler(), success, failure); else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) - return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) + return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, (MultiMCInstanceConfiguration) modpack.getManifest(), name) .whenComplete(Schedulers.defaultScheduler(), success, failure) - .thenComposeAsync(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)); + .thenComposeAsync(createMultiMCPostInstallTask(profile, (MultiMCInstanceConfiguration) modpack.getManifest(), name)); else if (modpack.getManifest() instanceof ServerModpackManifest) - return new ServerModpackLocalInstallTask(profile.getDependency(), zipFile, modpack, ((ServerModpackManifest) modpack.getManifest()), name) + return new ServerModpackLocalInstallTask(profile.getDependency(), zipFile, modpack, (ServerModpackManifest) modpack.getManifest(), name) .whenComplete(Schedulers.defaultScheduler(), success, failure); + else if (modpack.getManifest() instanceof McbbsModpackManifest) + return new McbbsModpackLocalInstallTask(profile.getDependency(), zipFile, modpack, (McbbsModpackManifest) modpack.getManifest(), name) + .whenComplete(Schedulers.defaultScheduler(), success, failure) + .thenComposeAsync(createMcbbsPostInstallTask(profile, (McbbsModpackManifest) modpack.getManifest(), name)); else throw new IllegalArgumentException("Unrecognized modpack: " + modpack.getManifest()); } @@ -232,5 +246,18 @@ public final class ModpackHelper { } } + private static Task createMultiMCPostInstallTask(Profile profile, MultiMCInstanceConfiguration manifest, String version) { + return Task.runAsync(Schedulers.javafx(), () -> { + VersionSetting vs = Objects.requireNonNull(profile.getRepository().specializeVersionSetting(version)); + ModpackHelper.toVersionSetting(manifest, vs); + }); + } + private static Task createMcbbsPostInstallTask(Profile profile, McbbsModpackManifest manifest, String version) { + return Task.runAsync(Schedulers.javafx(), () -> { + VersionSetting vs = Objects.requireNonNull(profile.getRepository().specializeVersionSetting(version)); + if (manifest.getLaunchInfo().getMinMemory() > vs.getMaxMemory()) + vs.setMaxMemory(manifest.getLaunchInfo().getMinMemory()); + }); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/MultiMCInstallVersionSettingTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/MultiMCInstallVersionSettingTask.java deleted file mode 100644 index 64b143250..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/MultiMCInstallVersionSettingTask.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * 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 . - */ -package org.jackhuang.hmcl.game; - -import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration; -import org.jackhuang.hmcl.setting.Profile; -import org.jackhuang.hmcl.setting.VersionSetting; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.task.Task; - -import java.util.Objects; - -public final class MultiMCInstallVersionSettingTask extends Task { - private final Profile profile; - private final MultiMCInstanceConfiguration manifest; - private final String version; - - public MultiMCInstallVersionSettingTask(Profile profile, MultiMCInstanceConfiguration manifest, String version) { - this.profile = profile; - this.manifest = manifest; - this.version = version; - - setExecutor(Schedulers.javafx()); - } - - @Override - public void execute() { - VersionSetting vs = Objects.requireNonNull(profile.getRepository().specializeVersionSetting(version)); - ModpackHelper.toVersionSetting(manifest, vs); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java index a3276d451..390a415a6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java @@ -21,16 +21,13 @@ import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; import javafx.beans.InvalidationListener; import javafx.beans.property.*; -import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.game.GameDirectoryType; -import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.platform.JavaVersion; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.Platform; -import java.io.File; import java.io.IOException; import java.lang.reflect.Type; import java.nio.file.InvalidPathException; @@ -39,8 +36,6 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import static org.jackhuang.hmcl.setting.ConfigHolder.config; - /** * * @author huangyuhui @@ -533,36 +528,6 @@ public final class VersionSetting implements Cloneable { defaultJavaPathProperty.addListener(listener); } - public LaunchOptions toLaunchOptions(File gameDir, boolean checkJava) throws InterruptedException { - JavaVersion javaVersion = Optional.ofNullable(getJavaVersion(checkJava)).orElse(JavaVersion.fromCurrentEnvironment()); - LaunchOptions.Builder builder = new LaunchOptions.Builder() - .setGameDir(gameDir) - .setJava(javaVersion) - .setVersionName(Metadata.TITLE) - .setVersionType(Metadata.TITLE) - .setProfileName(Metadata.TITLE) - .setMinecraftArgs(getMinecraftArgs()) - .setJavaArgs(getJavaArgs()) - .setMaxMemory(getMaxMemory()) - .setMinMemory(getMinMemory()) - .setMetaspace(Lang.toIntOrNull(getPermSize())) - .setWidth(getWidth()) - .setHeight(getHeight()) - .setFullscreen(isFullscreen()) - .setServerIp(getServerIp()) - .setWrapper(getWrapper()) - .setPrecalledCommand(getPreLaunchCommand()) - .setNoGeneratedJVMArgs(isNoJVMArgs()); - if (config().hasProxy()) { - builder.setProxy(ProxyManager.getProxy()); - if (config().hasProxyAuth()) { - builder.setProxyUser(config().getProxyUser()); - builder.setProxyPass(config().getProxyPass()); - } - } - return builder.create(); - } - @Override public VersionSetting clone() { VersionSetting versionSetting = new VersionSetting(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java index 957a692df..8babba79f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java @@ -17,9 +17,6 @@ */ package org.jackhuang.hmcl.ui; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.concurrent.Worker; import javafx.scene.Scene; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; @@ -41,7 +38,6 @@ public class WebStage extends Stage { getScene().getStylesheets().addAll(config().getTheme().getStylesheets()); getIcons().add(newImage("/assets/img/icon.png")); webView.setContextMenuEnabled(false); - titleProperty().bind(webEngine.titleProperty()); } public WebView getWebView() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/MicrosoftAccountLoginStage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/MicrosoftAccountLoginStage.java index 00f882976..cb2e52c0e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/MicrosoftAccountLoginStage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/MicrosoftAccountLoginStage.java @@ -35,6 +35,8 @@ public class MicrosoftAccountLoginStage extends WebStage implements MicrosoftSer super(600, 600); initModality(Modality.APPLICATION_MODAL); + titleProperty().bind(webEngine.titleProperty()); + webEngine.locationProperty().addListener((observable, oldValue, newValue) -> { if (urlTester != null && urlTester.test(newValue)) { future.complete(newValue); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java index 6893612de..a765b36d8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java @@ -34,14 +34,16 @@ import org.jackhuang.hmcl.download.game.GameAssetDownloadTask; import org.jackhuang.hmcl.download.game.GameInstallTask; import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; -import org.jackhuang.hmcl.game.HMCLModpackExportTask; import org.jackhuang.hmcl.game.HMCLModpackInstallTask; import org.jackhuang.hmcl.mod.MinecraftInstanceTask; import org.jackhuang.hmcl.mod.ModpackInstallTask; import org.jackhuang.hmcl.mod.ModpackUpdateTask; import org.jackhuang.hmcl.mod.curse.CurseCompletionTask; import org.jackhuang.hmcl.mod.curse.CurseInstallTask; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask; +import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask; +import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; @@ -128,7 +130,7 @@ public final class TaskListPane extends StackPane { task.setName(i18n("modpack.install", i18n("modpack.type.multimc"))); } else if (task instanceof HMCLModpackInstallTask) { task.setName(i18n("modpack.install", i18n("modpack.type.hmcl"))); - } else if (task instanceof HMCLModpackExportTask) { + } else if (task instanceof McbbsModpackExportTask || task instanceof MultiMCModpackExportTask || task instanceof ServerModpackExportTask) { task.setName(i18n("modpack.export")); } else if (task instanceof MinecraftInstanceTask) { task.setName(i18n("modpack.scan")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index 1bfe7f9ab..b6058f496 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -19,32 +19,25 @@ package org.jackhuang.hmcl.ui.export; import javafx.scene.Node; import org.jackhuang.hmcl.Launcher; -import org.jackhuang.hmcl.game.HMCLModpackExportTask; import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.Modpack; +import org.jackhuang.hmcl.mod.ModpackExportInfo; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask; import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration; import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; -import org.jackhuang.hmcl.setting.Config; -import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.io.Zipper; import java.io.File; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import static org.jackhuang.hmcl.setting.ConfigHolder.config; - public final class ExportWizardProvider implements WizardProvider { private final Profile profile; private final String version; @@ -62,73 +55,31 @@ public final class ExportWizardProvider implements WizardProvider { public Object finish(Map settings) { @SuppressWarnings("unchecked") List whitelist = (List) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION); - File modpackFile = (File) settings.get(ModpackInfoPage.MODPACK_FILE); - String modpackName = (String) settings.get(ModpackInfoPage.MODPACK_NAME); - String modpackAuthor = (String) settings.get(ModpackInfoPage.MODPACK_AUTHOR); - String modpackFileApi = (String) settings.get(ModpackInfoPage.MODPACK_FILE_API); - String modpackVersion = (String) settings.get(ModpackInfoPage.MODPACK_VERSION); - String modpackDescription = (String) settings.get(ModpackInfoPage.MODPACK_DESCRIPTION); + ModpackExportInfo exportInfo = (ModpackExportInfo) settings.get(ModpackInfoPage.MODPACK_INFO); + exportInfo.setWhitelist(whitelist); String modpackType = (String) settings.get(ModpackTypeSelectionPage.MODPACK_TYPE); - boolean includeLauncher = (Boolean) settings.get(ModpackInfoPage.MODPACK_INCLUDE_LAUNCHER); switch (modpackType) { - case ModpackTypeSelectionPage.MODPACK_TYPE_HMCL: - return exportAsHMCL(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, includeLauncher); + case ModpackTypeSelectionPage.MODPACK_TYPE_MCBBS: + return exportAsMcbbs(exportInfo); case ModpackTypeSelectionPage.MODPACK_TYPE_MULTIMC: - return exportAsMultiMC(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription); + return exportAsMultiMC(exportInfo); case ModpackTypeSelectionPage.MODPACK_TYPE_SERVER: - return exportAsServer(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, modpackFileApi); + return exportAsServer(exportInfo); default: throw new IllegalStateException("Unrecognized modpack type " + modpackType); } } - private Task exportAsHMCL(List whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, boolean includeLauncherRaw) { + private Task exportAsMcbbs(ModpackExportInfo exportInfo) { List launcherJar = Launcher.getCurrentJarFiles(); - boolean includeLauncher = includeLauncherRaw && launcherJar != null; return new Task() { Task dependency = null; @Override - public void execute() throws Exception { - File tempModpack = includeLauncher ? Files.createTempFile("hmcl", ".zip").toFile() : modpackFile; - - dependency = new HMCLModpackExportTask(profile.getRepository(), version, whitelist, - new Modpack(modpackName, modpackAuthor, modpackVersion, null, modpackDescription, StandardCharsets.UTF_8, null), tempModpack); - - if (includeLauncher) { - dependency = dependency.thenRunAsync(() -> { - try (Zipper zip = new Zipper(modpackFile.toPath())) { - Config exported = new Config(); - - exported.setBackgroundImageType(config().getBackgroundImageType()); - exported.setBackgroundImage(config().getBackgroundImage()); - exported.setTheme(config().getTheme()); - exported.setDownloadType(config().getDownloadType()); - exported.setPreferredLoginType(config().getPreferredLoginType()); - exported.getAuthlibInjectorServers().setAll(config().getAuthlibInjectorServers()); - - zip.putTextFile(exported.toJson(), ConfigHolder.CONFIG_FILENAME); - zip.putFile(tempModpack, "modpack.zip"); - - File bg = new File("bg").getAbsoluteFile(); - if (bg.isDirectory()) - zip.putDirectory(bg.toPath(), "bg"); - - File background_png = new File("background.png").getAbsoluteFile(); - if (background_png.isFile()) - zip.putFile(background_png, "background.png"); - - File background_jpg = new File("background.jpg").getAbsoluteFile(); - if (background_jpg.isFile()) - zip.putFile(background_jpg, "background.jpg"); - - for (File jar : launcherJar) - zip.putFile(jar, jar.getName()); - } - }); - } + public void execute() { + dependency = new McbbsModpackExportTask(profile.getRepository(), version, exportInfo); } @Override @@ -138,30 +89,30 @@ public final class ExportWizardProvider implements WizardProvider { }; } - private Task exportAsMultiMC(List whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription) { + private Task exportAsMultiMC(ModpackExportInfo exportInfo) { return new Task() { Task dependency; @Override public void execute() { VersionSetting vs = profile.getVersionSetting(version); - dependency = new MultiMCModpackExportTask(profile.getRepository(), version, whitelist, + dependency = new MultiMCModpackExportTask(profile.getRepository(), version, exportInfo.getWhitelist(), new MultiMCInstanceConfiguration( "OneSix", - modpackName + "-" + modpackVersion, + exportInfo.getName() + "-" + exportInfo.getVersion(), null, Lang.toIntOrNull(vs.getPermSize()), vs.getWrapper(), vs.getPreLaunchCommand(), null, - modpackDescription, + exportInfo.getDescription(), null, - vs.getJavaArgs(), + exportInfo.getJavaArguments(), vs.isFullscreen(), vs.getWidth(), vs.getHeight(), vs.getMaxMemory(), - vs.getMinMemory(), + exportInfo.getMinMemory(), vs.isShowLogs(), /* showConsoleOnError */ true, /* autoCloseConsole */ false, @@ -171,7 +122,7 @@ public final class ExportWizardProvider implements WizardProvider { /* overrideConsole */ true, /* overrideCommands */ true, /* overrideWindow */ true - ), modpackFile); + ), exportInfo.getOutput().toFile()); } @Override @@ -181,13 +132,13 @@ public final class ExportWizardProvider implements WizardProvider { }; } - private Task exportAsServer(List whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, String modpackFileApi) { + private Task exportAsServer(ModpackExportInfo exportInfo) { return new Task() { Task dependency; @Override public void execute() { - dependency = new ServerModpackExportTask(profile.getRepository(), version, whitelist, modpackName, modpackAuthor, modpackVersion, modpackDescription, modpackFileApi, modpackFile); + dependency = new ServerModpackExportTask(profile.getRepository(), version, exportInfo); } @Override @@ -203,7 +154,7 @@ public final class ExportWizardProvider implements WizardProvider { case 0: return new ModpackTypeSelectionPage(controller); case 1: - return new ModpackInfoPage(controller, version); + return new ModpackInfoPage(controller, profile.getRepository(), version); case 2: return new ModpackFileSelectionPage(controller, profile, version, ModAdviser::suggestMod); default: diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java index 14f3ce88d..f04520a25 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java @@ -17,76 +17,100 @@ */ package org.jackhuang.hmcl.ui.export; -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXTextArea; -import com.jfoenix.controls.JFXTextField; -import com.jfoenix.controls.JFXToggleButton; +import com.jfoenix.controls.*; import com.jfoenix.validation.RequiredFieldValidator; import javafx.beans.binding.Bindings; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.*; +import javafx.collections.ObservableList; import javafx.event.EventHandler; -import javafx.fxml.FXML; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import javafx.stage.FileChooser; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; +import org.jackhuang.hmcl.game.HMCLGameRepository; +import org.jackhuang.hmcl.mod.ModpackExportInfo; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest; import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.ComponentList; +import org.jackhuang.hmcl.ui.construct.NumberValidator; +import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; +import org.jackhuang.hmcl.util.StringUtils; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; -import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.*; +import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory; +import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; +import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE; +import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_SERVER; +import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class ModpackInfoPage extends Control implements WizardPage { private final WizardController controller; + private final HMCLGameRepository gameRepository; + private final ModpackExportInfo.Options options; + private final String versionName; private final boolean canIncludeLauncher; - private final boolean showFileApi; - private SimpleStringProperty versionName = new SimpleStringProperty(); - private SimpleStringProperty modpackName = new SimpleStringProperty(); - private SimpleStringProperty modpackFileApi = new SimpleStringProperty(); - private SimpleStringProperty modpackAuthor = new SimpleStringProperty(); - private SimpleStringProperty modpackVersion = new SimpleStringProperty("1.0"); - private SimpleStringProperty modpackDescription = new SimpleStringProperty(); - private SimpleBooleanProperty includingLauncher = new SimpleBooleanProperty(); - private ObjectProperty> next = new SimpleObjectProperty<>(); + private final ModpackExportInfo exportInfo = new ModpackExportInfo(); - public ModpackInfoPage(WizardController controller, String version) { + private final SimpleStringProperty name = new SimpleStringProperty(""); + private final SimpleStringProperty author = new SimpleStringProperty(""); + private final SimpleStringProperty version = new SimpleStringProperty("1.0"); + private final SimpleStringProperty description = new SimpleStringProperty(""); + private final SimpleStringProperty url = new SimpleStringProperty(""); + private final SimpleBooleanProperty forceUpdate = new SimpleBooleanProperty(); + private final SimpleBooleanProperty packWithLauncher = new SimpleBooleanProperty(); + private final SimpleStringProperty fileApi = new SimpleStringProperty(); + private final SimpleIntegerProperty minMemory = new SimpleIntegerProperty(0); + private final SimpleStringProperty authlibInjectorServer = new SimpleStringProperty(); + private final SimpleStringProperty launchArguments = new SimpleStringProperty(""); + private final SimpleStringProperty javaArguments = new SimpleStringProperty(""); + private final ObjectProperty> next = new SimpleObjectProperty<>(); + private final SimpleStringProperty mcbbsThreadId = new SimpleStringProperty(); + + public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) { this.controller = controller; - modpackName.set(version); - modpackAuthor.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse("")); - versionName.set(version); + this.gameRepository = gameRepository; + this.options = tryCast(controller.getSettings().get(MODPACK_INFO_OPTION), ModpackExportInfo.Options.class) + .orElseThrow(() -> new IllegalArgumentException("Settings.MODPACK_INFO_OPTION is required")); + this.versionName = version; + + name.set(version); + author.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse("")); + + VersionSetting versionSetting = gameRepository.getVersionSetting(versionName); + minMemory.set(Optional.ofNullable(versionSetting.getMinMemory()).orElse(0)); + launchArguments.set(versionSetting.getMinecraftArgs()); + javaArguments.set(versionSetting.getJavaArgs()); List launcherJar = Launcher.getCurrentJarFiles(); canIncludeLauncher = launcherJar != null; - showFileApi = controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_SERVER; next.set(e -> onNext()); } - @FXML private void onNext() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle(i18n("modpack.wizard.step.initialization.save")); @@ -96,24 +120,35 @@ public final class ModpackInfoPage extends Control implements WizardPage { controller.onEnd(); return; } - controller.getSettings().put(MODPACK_NAME, modpackName.get()); - controller.getSettings().put(MODPACK_FILE_API, modpackFileApi.get()); - controller.getSettings().put(MODPACK_VERSION, modpackVersion.get()); - controller.getSettings().put(MODPACK_AUTHOR, modpackAuthor.get()); - controller.getSettings().put(MODPACK_FILE, file); - controller.getSettings().put(MODPACK_DESCRIPTION, modpackDescription.get()); - controller.getSettings().put(MODPACK_INCLUDE_LAUNCHER, includingLauncher.get()); + + exportInfo.setName(name.get()); + exportInfo.setFileApi(fileApi.get()); + exportInfo.setVersion(version.get()); + exportInfo.setAuthor(author.get()); + exportInfo.setOutput(file.toPath()); + exportInfo.setDescription(description.get()); + exportInfo.setPackWithLauncher(packWithLauncher.get()); + exportInfo.setUrl(url.get()); + exportInfo.setForceUpdate(forceUpdate.get()); + exportInfo.setPackWithLauncher(packWithLauncher.get()); + exportInfo.setMinMemory(minMemory.get()); + exportInfo.setLaunchArguments(launchArguments.get()); + exportInfo.setJavaArguments(javaArguments.get()); + exportInfo.setAuthlibInjectorServer(authlibInjectorServer.get()); + + if (StringUtils.isNotBlank(mcbbsThreadId.get())) { + exportInfo.setOrigins(Collections.singletonList(new McbbsModpackManifest.Origin( + "mcbbs", Integer.parseInt(mcbbsThreadId.get()) + ))); + } + + controller.getSettings().put(MODPACK_INFO, exportInfo); controller.onNext(); } @Override public void cleanup(Map settings) { - controller.getSettings().remove(MODPACK_NAME); - controller.getSettings().remove(MODPACK_VERSION); - controller.getSettings().remove(MODPACK_AUTHOR); - controller.getSettings().remove(MODPACK_DESCRIPTION); - controller.getSettings().remove(MODPACK_INCLUDE_LAUNCHER); - controller.getSettings().remove(MODPACK_FILE); + controller.getSettings().remove(MODPACK_INFO); } @Override @@ -126,19 +161,11 @@ public final class ModpackInfoPage extends Control implements WizardPage { return new ModpackInfoPageSkin(this); } - public static final String MODPACK_NAME = "modpack.name"; - public static final String MODPACK_FILE_API = "modpack.file_api"; - public static final String MODPACK_VERSION = "modpack.version"; - public static final String MODPACK_AUTHOR = "archive.author"; - public static final String MODPACK_DESCRIPTION = "modpack.description"; - public static final String MODPACK_INCLUDE_LAUNCHER = "modpack.include_launcher"; - public static final String MODPACK_FILE = "modpack.file"; + public static final String MODPACK_INFO = "modpack.info"; + public static final String MODPACK_INFO_OPTION = "modpack.info.option"; public static class ModpackInfoPageSkin extends SkinBase { - private final JFXTextField txtModpackName; - private final JFXTextField txtModpackFileApi; - private final JFXTextField txtModpackAuthor; - private final JFXTextField txtModpackVersion; + private ObservableList originList; public ModpackInfoPageSkin(ModpackInfoPage skinnable) { super(skinnable); @@ -151,6 +178,8 @@ public final class ModpackInfoPage extends Control implements WizardPage { scroll.setFitToHeight(true); getChildren().setAll(scroll); + List validatingFields = new ArrayList<>(); + { BorderPane borderPane = new BorderPane(); borderPane.setStyle("-fx-padding: 16;"); @@ -179,14 +208,14 @@ public final class ModpackInfoPage extends Control implements WizardPage { borderPane1.setLeft(new Label(i18n("modpack.wizard.step.initialization.exported_version"))); Label versionNameLabel = new Label(); - versionNameLabel.textProperty().bind(skinnable.versionName); + versionNameLabel.setText(skinnable.versionName); borderPane1.setRight(versionNameLabel); list.getContent().add(borderPane1); } { - txtModpackName = new JFXTextField(); - txtModpackName.textProperty().bindBidirectional(skinnable.modpackName); + JFXTextField txtModpackName = new JFXTextField(); + txtModpackName.textProperty().bindBidirectional(skinnable.name); txtModpackName.setLabelFloat(true); txtModpackName.setPromptText(i18n("modpack.name")); RequiredFieldValidator validator = new RequiredFieldValidator(); @@ -194,16 +223,25 @@ public final class ModpackInfoPage extends Control implements WizardPage { txtModpackName.getValidators().add(validator); StackPane.setMargin(txtModpackName, insets); list.getContent().add(txtModpackName); + + validatingFields.add(txtModpackName); } - if (skinnable.showFileApi) { - txtModpackFileApi = new JFXTextField(); - txtModpackFileApi.textProperty().bindBidirectional(skinnable.modpackFileApi); + if (skinnable.options.isRequireFileApi()) { + JFXTextField txtModpackFileApi = new JFXTextField(); + txtModpackFileApi.textProperty().bindBidirectional(skinnable.fileApi); txtModpackFileApi.setLabelFloat(true); txtModpackFileApi.setPromptText(i18n("modpack.file_api")); - RequiredFieldValidator validator = new RequiredFieldValidator(); - txtModpackFileApi.getValidators().add(validator); + + if (skinnable.options.isValidateFileApi()) { + RequiredFieldValidator validator = new RequiredFieldValidator(); + txtModpackFileApi.getValidators().add(validator); + } + txtModpackFileApi.getValidators().add(new Validator(s -> { + if (s.isEmpty()) { + return true; + } try { new URL(s).toURI(); return true; @@ -213,35 +251,39 @@ public final class ModpackInfoPage extends Control implements WizardPage { })); StackPane.setMargin(txtModpackFileApi, insets); list.getContent().add(txtModpackFileApi); - } else { - txtModpackFileApi = null; + + validatingFields.add(txtModpackFileApi); } { - txtModpackAuthor = new JFXTextField(); - txtModpackAuthor.textProperty().bindBidirectional(skinnable.modpackAuthor); + JFXTextField txtModpackAuthor = new JFXTextField(); + txtModpackAuthor.textProperty().bindBidirectional(skinnable.author); txtModpackAuthor.setLabelFloat(true); txtModpackAuthor.setPromptText(i18n("archive.author")); RequiredFieldValidator validator = new RequiredFieldValidator(); txtModpackAuthor.getValidators().add(validator); StackPane.setMargin(txtModpackAuthor, insets); list.getContent().add(txtModpackAuthor); + + validatingFields.add(txtModpackAuthor); } { - txtModpackVersion = new JFXTextField(); - txtModpackVersion.textProperty().bindBidirectional(skinnable.modpackVersion); + JFXTextField txtModpackVersion = new JFXTextField(); + txtModpackVersion.textProperty().bindBidirectional(skinnable.version); txtModpackVersion.setLabelFloat(true); txtModpackVersion.setPromptText(i18n("archive.version")); RequiredFieldValidator validator = new RequiredFieldValidator(); txtModpackVersion.getValidators().add(validator); StackPane.setMargin(txtModpackVersion, insets); list.getContent().add(txtModpackVersion); + + validatingFields.add(txtModpackVersion); } { JFXTextArea area = new JFXTextArea(); - area.textProperty().bindBidirectional(skinnable.modpackDescription); + area.textProperty().bindBidirectional(skinnable.description); area.setLabelFloat(true); area.setPromptText(i18n("modpack.desc")); area.setMinHeight(400); @@ -249,18 +291,110 @@ public final class ModpackInfoPage extends Control implements WizardPage { list.getContent().add(area); } - if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_HMCL) { - BorderPane borderPane1 = new BorderPane(); - borderPane1.setLeft(new Label(i18n("modpack.wizard.step.initialization.include_launcher"))); - list.getContent().add(borderPane1); + if (skinnable.options.isRequireForceUpdate()) { + BorderPane pane = new BorderPane(); + pane.setLeft(new Label(i18n("modpack.wizard.step.initialization.force_update"))); + list.getContent().add(pane); JFXToggleButton button = new JFXToggleButton(); button.setDisable(!skinnable.canIncludeLauncher); - button.selectedProperty().bindBidirectional(skinnable.includingLauncher); + button.selectedProperty().bindBidirectional(skinnable.packWithLauncher); button.setSize(8); button.setMinHeight(16); button.setMaxHeight(16); - borderPane1.setRight(button); + pane.setRight(button); + } + + if (skinnable.options.isRequireAuthlibInjectorServer()) { + JFXComboBox cboServers = new JFXComboBox<>(); + cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl()))); + cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName)); + Bindings.bindContent(cboServers.getItems(), config().getAuthlibInjectorServers()); + + skinnable.authlibInjectorServer.bind(Bindings.createStringBinding(() -> + Optional.ofNullable(cboServers.getSelectionModel().getSelectedItem()) + .map(AuthlibInjectorServer::getUrl) + .orElse(null))); + + BorderPane pane = new BorderPane(); + + Label left = new Label(i18n("account.injector.server")); + BorderPane.setAlignment(left, Pos.CENTER_LEFT); + pane.setLeft(left); + pane.setRight(cboServers); + + list.getContent().add(pane); + } + + if (skinnable.options.isRequireMinMemory()) { + JFXTextField txtMinMemory = new JFXTextField(); + FXUtils.bindInt(txtMinMemory, skinnable.minMemory); + txtMinMemory.getValidators().add(new NumberValidator(i18n("input.number"), false)); + FXUtils.setLimitWidth(txtMinMemory, 300); + validatingFields.add(txtMinMemory); + + BorderPane pane = new BorderPane(); + Label label = new Label(i18n("settings.min_memory")); + BorderPane.setAlignment(label, Pos.CENTER_LEFT); + pane.setLeft(label); + BorderPane.setAlignment(txtMinMemory, Pos.CENTER_RIGHT); + pane.setRight(txtMinMemory); + + list.getContent().add(pane); + } + + if (skinnable.options.isRequireLaunchArguments()) { + JFXTextField txtLaunchArguments = new JFXTextField(); + txtLaunchArguments.textProperty().bindBidirectional(skinnable.launchArguments); + txtLaunchArguments.setLabelFloat(true); + txtLaunchArguments.setPromptText(i18n("settings.advanced.minecraft_arguments")); + StackPane.setMargin(txtLaunchArguments, insets); + list.getContent().add(txtLaunchArguments); + } + + if (skinnable.options.isRequireJavaArguments()) { + JFXTextField txtJavaArguments = new JFXTextField(); + txtJavaArguments.textProperty().bindBidirectional(skinnable.javaArguments); + txtJavaArguments.setLabelFloat(true); + txtJavaArguments.setPromptText(i18n("settings.advanced.jvm_args")); + StackPane.setMargin(txtJavaArguments, insets); + list.getContent().add(txtJavaArguments); + } + + if (skinnable.options.isRequireOrigins() || skinnable.options.isRequireUrl()) { + BorderPane originPane = new BorderPane(); + Label title = new Label(i18n("modpack.origin")); + originPane.setTop(title); + VBox container = new VBox(); + BorderPane.setMargin(container, new Insets(0, 0, 0, 16)); + originPane.setCenter(container); + list.getContent().add(originPane); + + if (skinnable.options.isRequireUrl()) { + BorderPane pane = new BorderPane(); + pane.setPadding(new Insets(8, 0, 8, 0)); + Label left = new Label(i18n("modpack.origin.url")); + pane.setLeft(left); + JFXTextField txtModpackUrl = new JFXTextField(); + txtModpackUrl.textProperty().bindBidirectional(skinnable.url); + pane.setRight(txtModpackUrl); + container.getChildren().add(pane); + } + + if (skinnable.options.isRequireOrigins()) { + BorderPane pane = new BorderPane(); + pane.setPadding(new Insets(8, 0, 8, 0)); + Label left = new Label(i18n("modpack.origin.mcbbs")); + pane.setLeft(left); + JFXTextField txtMcbbs = new JFXTextField(); + FXUtils.setValidateWhileTextChanged(txtMcbbs, true); + txtMcbbs.getValidators().add(new NumberValidator(i18n("input.number"), true)); + txtMcbbs.textProperty().bindBidirectional(skinnable.mcbbsThreadId); + pane.setRight(txtMcbbs); + container.getChildren().add(pane); + + validatingFields.add(txtMcbbs); + } } } @@ -276,15 +410,13 @@ public final class ModpackInfoPage extends Control implements WizardPage { nextButton.setButtonType(JFXButton.ButtonType.RAISED); nextButton.setText(i18n("wizard.next")); nextButton.getStyleClass().add("jfx-button-raised"); - if (skinnable.showFileApi) { - nextButton.disableProperty().bind(Bindings.createBooleanBinding(() -> - !txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate() || !txtModpackFileApi.validate(), - txtModpackName.textProperty(), txtModpackAuthor.textProperty(), txtModpackVersion.textProperty(), txtModpackFileApi.textProperty())); - } else { - nextButton.disableProperty().bind(Bindings.createBooleanBinding(() -> - !txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate(), - txtModpackName.textProperty(), txtModpackAuthor.textProperty(), txtModpackVersion.textProperty())); - } + nextButton.disableProperty().bind( + // Disable nextButton if any text of JFXTextFields in validatingFields does not fulfill + // our requirement. + Bindings.createBooleanBinding(() -> validatingFields.stream() + .map(field -> !field.validate()) + .reduce(true, (left, right) -> left || right), + validatingFields.stream().map(JFXTextField::textProperty).toArray(StringProperty[]::new))); hbox.getChildren().add(nextButton); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java index 80dd4e59c..63e4f0046 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java @@ -20,18 +20,23 @@ package org.jackhuang.hmcl.ui.export; import com.jfoenix.controls.JFXButton; import javafx.fxml.FXML; import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.mod.ModpackExportInfo; +import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask; +import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; +import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; import java.util.Map; +import static org.jackhuang.hmcl.ui.export.ModpackInfoPage.MODPACK_INFO_OPTION; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class ModpackTypeSelectionPage extends StackPane implements WizardPage { private final WizardController controller; @FXML - private JFXButton btnHMCL; + private JFXButton btnMCBBS; @FXML private JFXButton btnMultiMC; @FXML @@ -41,12 +46,15 @@ public final class ModpackTypeSelectionPage extends StackPane implements WizardP this.controller = controller; FXUtils.loadFXML(this, "/assets/fxml/modpack/type.fxml"); - JFXButton[] buttons = new JFXButton[]{btnHMCL, btnMultiMC, btnServer}; - String[] types = new String[]{MODPACK_TYPE_HMCL, MODPACK_TYPE_MULTIMC, MODPACK_TYPE_SERVER}; + JFXButton[] buttons = new JFXButton[]{btnMCBBS, btnMultiMC, btnServer}; + String[] types = new String[]{MODPACK_TYPE_MCBBS, MODPACK_TYPE_MULTIMC, MODPACK_TYPE_SERVER}; + ModpackExportInfo.Options[] options = new ModpackExportInfo.Options[]{McbbsModpackExportTask.OPTION, MultiMCModpackExportTask.OPTION, ServerModpackExportTask.OPTION}; for (int i = 0; i < types.length; ++i) { String type = types[i]; + ModpackExportInfo.Options option = options[i]; buttons[i].setOnMouseClicked(e -> { controller.getSettings().put(MODPACK_TYPE, type); + controller.getSettings().put(MODPACK_INFO_OPTION, option); controller.onNext(); }); } @@ -63,7 +71,7 @@ public final class ModpackTypeSelectionPage extends StackPane implements WizardP public static final String MODPACK_TYPE = "modpack.type"; - public static final String MODPACK_TYPE_HMCL = "hmcl"; + public static final String MODPACK_TYPE_MCBBS = "mcbbs"; public static final String MODPACK_TYPE_MULTIMC = "multimc"; public static final String MODPACK_TYPE_SERVER = "server"; } diff --git a/HMCL/src/main/resources/assets/fxml/modpack/type.fxml b/HMCL/src/main/resources/assets/fxml/modpack/type.fxml index e2b7feed0..e684a5e68 100644 --- a/HMCL/src/main/resources/assets/fxml/modpack/type.fxml +++ b/HMCL/src/main/resources/assets/fxml/modpack/type.fxml @@ -13,11 +13,11 @@