支持以类型安全的方式读写 Navigation#getSettings() (#4504)

This commit is contained in:
Glavo
2025-09-18 16:58:07 +08:00
committed by GitHub
parent a0568e34a8
commit 66524613b4
27 changed files with 256 additions and 177 deletions

View File

@@ -23,8 +23,8 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.construct.Navigator; import org.jackhuang.hmcl.ui.construct.Navigator;
import org.jackhuang.hmcl.ui.construct.PageCloseEvent; import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
import org.jackhuang.hmcl.ui.wizard.*; import org.jackhuang.hmcl.ui.wizard.*;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements WizardDisplayer { public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements WizardDisplayer {
@@ -94,7 +94,7 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements
} }
@Override @Override
public void handleTask(Map<String, Object> settings, Task<?> task) { public void handleTask(SettingsMap settings, Task<?> task) {
displayer.handleTask(settings, task); displayer.handleTask(settings, task);
} }

View File

@@ -35,8 +35,7 @@ import org.jackhuang.hmcl.ui.InstallerItem;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -80,12 +79,12 @@ public abstract class AbstractInstallersPage extends Control implements WizardPa
protected abstract void reload(); protected abstract void reload();
@Override @Override
public void onNavigate(Map<String, Object> settings) { public void onNavigate(SettingsMap settings) {
reload(); reload();
} }
@Override @Override
public abstract void cleanup(Map<String, Object> settings); public abstract void cleanup(SettingsMap settings);
protected abstract void onInstall(); protected abstract void onInstall();

View File

@@ -29,8 +29,8 @@ import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.ui.InstallerItem; import org.jackhuang.hmcl.ui.InstallerItem;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
@@ -109,6 +109,6 @@ class AdditionalInstallersPage extends AbstractInstallersPage {
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
} }
} }

View File

@@ -51,14 +51,13 @@ import org.jackhuang.hmcl.ui.versions.Versions;
import org.jackhuang.hmcl.ui.wizard.Navigation; import org.jackhuang.hmcl.ui.wizard.Navigation;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -223,7 +222,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
} }
private static final class DownloadNavigator implements Navigation { private static final class DownloadNavigator implements Navigation {
private final Map<String, Object> settings = new HashMap<>(); private final SettingsMap settings = new SettingsMap();
@Override @Override
public void onStart() { public void onStart() {
@@ -260,7 +259,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
} }
@Override @Override
public Map<String, Object> getSettings() { public SettingsMap getSettings() {
return settings; return settings;
} }
@@ -287,37 +286,39 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
} }
@Override @Override
public void start(Map<String, Object> settings) { public void start(SettingsMap settings) {
settings.put(PROFILE, profile); settings.put(ModpackPage.PROFILE, profile);
settings.put(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion); settings.put(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion);
} }
private Task<Void> finishVersionDownloadingAsync(Map<String, Object> settings) { private Task<Void> finishVersionDownloadingAsync(SettingsMap settings) {
GameBuilder builder = dependencyManager.gameBuilder(); GameBuilder builder = dependencyManager.gameBuilder();
String name = (String) settings.get("name"); String name = (String) settings.get("name");
builder.name(name); builder.name(name);
builder.gameVersion(((RemoteVersion) settings.get(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId())).getGameVersion()); builder.gameVersion(((RemoteVersion) settings.get(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId())).getGameVersion());
for (Map.Entry<String, Object> entry : settings.entrySet()) settings.asStringMap().forEach((key, value) -> {
if (!LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(entry.getKey()) && entry.getValue() instanceof RemoteVersion) if (!LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(key)
builder.version((RemoteVersion) entry.getValue()); && value instanceof RemoteVersion remoteVersion)
builder.version(remoteVersion);
});
return builder.buildAsync().whenComplete(any -> profile.getRepository().refreshVersions()) return builder.buildAsync().whenComplete(any -> profile.getRepository().refreshVersions())
.thenRunAsync(Schedulers.javafx(), () -> profile.setSelectedVersion(name)); .thenRunAsync(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
} }
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(SettingsMap settings) {
settings.put("title", i18n("install.new_game.installation")); settings.put("title", i18n("install.new_game.installation"));
settings.put("success_message", i18n("install.success")); settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> UpdateInstallerWizardProvider.alertFailureMessage(exception, next)); settings.put(FailureCallback.KEY, (settings1, exception, next) -> UpdateInstallerWizardProvider.alertFailureMessage(exception, next));
return finishVersionDownloadingAsync(settings); return finishVersionDownloadingAsync(settings);
} }
@Override @Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) { public Node createPage(WizardController controller, int step, SettingsMap settings) {
switch (step) { switch (step) {
case 0: case 0:
return new InstallersPage(controller, profile.getRepository(), ((RemoteVersion) controller.getSettings().get("game")).getGameVersion(), downloadProvider); return new InstallersPage(controller, profile.getRepository(), ((RemoteVersion) controller.getSettings().get("game")).getGameVersion(), downloadProvider);
@@ -330,7 +331,5 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
public boolean cancel() { public boolean cancel() {
return true; return true;
} }
public static final String PROFILE = "PROFILE";
} }
} }

View File

@@ -27,8 +27,7 @@ import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.RequiredValidator; import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import static javafx.beans.binding.Bindings.createBooleanBinding; import static javafx.beans.binding.Bindings.createBooleanBinding;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -73,7 +72,7 @@ public class InstallersPage extends AbstractInstallersPage {
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
} }
private static boolean checkName(String name) { private static boolean checkName(String name) {

View File

@@ -36,16 +36,14 @@ import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.RequiredValidator; import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.logging.Logger.LOG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -58,11 +56,11 @@ public final class LocalModpackPage extends ModpackPage {
public LocalModpackPage(WizardController controller) { public LocalModpackPage(WizardController controller) {
super(controller); super(controller);
Profile profile = (Profile) controller.getSettings().get("PROFILE"); Profile profile = controller.getSettings().get(ModpackPage.PROFILE);
Optional<String> name = tryCast(controller.getSettings().get(MODPACK_NAME), String.class); String name = controller.getSettings().get(MODPACK_NAME);
if (name.isPresent()) { if (name != null) {
txtModpackName.setText(name.get()); txtModpackName.setText(name);
txtModpackName.setDisable(true); txtModpackName.setDisable(true);
} else { } else {
FXUtils.onChangeAndOperate(installAsVersion, installAsVersion -> { FXUtils.onChangeAndOperate(installAsVersion, installAsVersion -> {
@@ -83,9 +81,9 @@ public final class LocalModpackPage extends ModpackPage {
btnDescription.setVisible(false); btnDescription.setVisible(false);
Path selectedFile; Path selectedFile;
Optional<Path> filePath = tryCast(controller.getSettings().get(MODPACK_FILE), Path.class); Path filePath = controller.getSettings().get(MODPACK_FILE);
if (filePath.isPresent()) { if (filePath != null) {
selectedFile = filePath.get(); selectedFile = filePath;
} else { } else {
FileChooser chooser = new FileChooser(); FileChooser chooser = new FileChooser();
chooser.setTitle(i18n("modpack.choose")); chooser.setTitle(i18n("modpack.choose"));
@@ -112,7 +110,7 @@ public final class LocalModpackPage extends ModpackPage {
lblName.setText(FileUtils.getName(selectedFile)); lblName.setText(FileUtils.getName(selectedFile));
installAsVersion.set(false); installAsVersion.set(false);
if (name.isEmpty()) { if (name == null) {
// trim: https://github.com/HMCL-dev/HMCL/issues/962 // trim: https://github.com/HMCL-dev/HMCL/issues/962
txtModpackName.setText(FileUtils.getNameWithoutExtension(selectedFile)); txtModpackName.setText(FileUtils.getNameWithoutExtension(selectedFile));
} }
@@ -133,7 +131,7 @@ public final class LocalModpackPage extends ModpackPage {
lblVersion.setText(manifest.getVersion()); lblVersion.setText(manifest.getVersion());
lblAuthor.setText(manifest.getAuthor()); lblAuthor.setText(manifest.getAuthor());
if (name.isEmpty()) { if (name == null) {
// trim: https://github.com/HMCL-dev/HMCL/issues/962 // trim: https://github.com/HMCL-dev/HMCL/issues/962
txtModpackName.setText(manifest.getName().trim()); txtModpackName.setText(manifest.getName().trim());
} }
@@ -144,7 +142,7 @@ public final class LocalModpackPage extends ModpackPage {
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
settings.remove(MODPACK_FILE); settings.remove(MODPACK_FILE);
} }
@@ -177,9 +175,9 @@ public final class LocalModpackPage extends ModpackPage {
Controllers.navigate(new WebPage(i18n("modpack.description"), manifest.getDescription())); Controllers.navigate(new WebPage(i18n("modpack.description"), manifest.getDescription()));
} }
public static final String MODPACK_FILE = "MODPACK_FILE"; public static final SettingsMap.Key<Path> MODPACK_FILE = new SettingsMap.Key<>("MODPACK_FILE");
public static final String MODPACK_NAME = "MODPACK_NAME"; public static final SettingsMap.Key<String> MODPACK_NAME = new SettingsMap.Key<>("MODPACK_NAME");
public static final String MODPACK_MANIFEST = "MODPACK_MANIFEST"; public static final SettingsMap.Key<Modpack> MODPACK_MANIFEST = new SettingsMap.Key<>("MODPACK_MANIFEST");
public static final String MODPACK_CHARSET = "MODPACK_CHARSET"; public static final SettingsMap.Key<Charset> MODPACK_CHARSET = new SettingsMap.Key<>("MODPACK_CHARSET");
public static final String MODPACK_MANUALLY_CREATED = "MODPACK_MANUALLY_CREATED"; public static final SettingsMap.Key<Boolean> MODPACK_MANUALLY_CREATED = new SettingsMap.Key<>("MODPACK_MANUALLY_CREATED");
} }

View File

@@ -32,14 +32,13 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import org.jackhuang.hmcl.util.SettingsMap;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class ModpackInstallWizardProvider implements WizardProvider { public final class ModpackInstallWizardProvider implements WizardProvider {
@@ -66,21 +65,21 @@ public final class ModpackInstallWizardProvider implements WizardProvider {
} }
@Override @Override
public void start(Map<String, Object> settings) { public void start(SettingsMap settings) {
if (file != null) if (file != null)
settings.put(LocalModpackPage.MODPACK_FILE, file); settings.put(LocalModpackPage.MODPACK_FILE, file);
if (updateVersion != null) if (updateVersion != null)
settings.put(LocalModpackPage.MODPACK_NAME, updateVersion); settings.put(LocalModpackPage.MODPACK_NAME, updateVersion);
settings.put(PROFILE, profile); settings.put(ModpackPage.PROFILE, profile);
} }
private Task<?> finishModpackInstallingAsync(Map<String, Object> settings) { private Task<?> finishModpackInstallingAsync(SettingsMap settings) {
Path selected = tryCast(settings.get(LocalModpackPage.MODPACK_FILE), Path.class).orElse(null); Path selected = settings.get(LocalModpackPage.MODPACK_FILE);
ServerModpackManifest serverModpackManifest = tryCast(settings.get(RemoteModpackPage.MODPACK_SERVER_MANIFEST), ServerModpackManifest.class).orElse(null); ServerModpackManifest serverModpackManifest = settings.get(RemoteModpackPage.MODPACK_SERVER_MANIFEST);
Modpack modpack = tryCast(settings.get(LocalModpackPage.MODPACK_MANIFEST), Modpack.class).orElse(null); Modpack modpack = settings.get(LocalModpackPage.MODPACK_MANIFEST);
String name = tryCast(settings.get(LocalModpackPage.MODPACK_NAME), String.class).orElse(null); String name = settings.get(LocalModpackPage.MODPACK_NAME);
Charset charset = tryCast(settings.get(LocalModpackPage.MODPACK_CHARSET), Charset.class).orElse(null); Charset charset = settings.get(LocalModpackPage.MODPACK_CHARSET);
boolean isManuallyCreated = tryCast(settings.get(LocalModpackPage.MODPACK_MANUALLY_CREATED), Boolean.class).orElse(false); boolean isManuallyCreated = settings.getOrDefault(LocalModpackPage.MODPACK_MANUALLY_CREATED, false);
if (isManuallyCreated) { if (isManuallyCreated) {
return ModpackHelper.getInstallManuallyCreatedModpackTask(profile, selected, name, charset); return ModpackHelper.getInstallManuallyCreatedModpackTask(profile, selected, name, charset);
@@ -119,12 +118,10 @@ public final class ModpackInstallWizardProvider implements WizardProvider {
} }
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(SettingsMap settings) {
settings.put("title", i18n("install.modpack.installation")); settings.put("title", i18n("install.modpack.installation"));
settings.put("success_message", i18n("install.success")); settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", new FailureCallback() { settings.put(FailureCallback.KEY, (ignored, exception, next) -> {
@Override
public void onFail(Map<String, Object> settings, Exception exception, Runnable next) {
if (exception instanceof ModpackCompletionException) { if (exception instanceof ModpackCompletionException) {
if (exception.getCause() instanceof FileNotFoundException) { if (exception.getCause() instanceof FileNotFoundException) {
Controllers.dialog(i18n("modpack.type.curse.not_found"), i18n("install.failed"), MessageType.ERROR, next); Controllers.dialog(i18n("modpack.type.curse.not_found"), i18n("install.failed"), MessageType.ERROR, next);
@@ -134,14 +131,13 @@ public final class ModpackInstallWizardProvider implements WizardProvider {
} else { } else {
UpdateInstallerWizardProvider.alertFailureMessage(exception, next); UpdateInstallerWizardProvider.alertFailureMessage(exception, next);
} }
}
}); });
return finishModpackInstallingAsync(settings); return finishModpackInstallingAsync(settings);
} }
@Override @Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) { public Node createPage(WizardController controller, int step, SettingsMap settings) {
switch (step) { switch (step) {
case 0: case 0:
return new ModpackSelectionPage(controller); return new ModpackSelectionPage(controller);
@@ -161,6 +157,4 @@ public final class ModpackInstallWizardProvider implements WizardProvider {
public boolean cancel() { public boolean cancel() {
return true; return true;
} }
public static final String PROFILE = "PROFILE";
} }

View File

@@ -7,16 +7,20 @@ import javafx.geometry.Pos;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.construct.ComponentList;
import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.SettingsMap;
import static javafx.beans.binding.Bindings.createBooleanBinding; import static javafx.beans.binding.Bindings.createBooleanBinding;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public abstract class ModpackPage extends SpinnerPane implements WizardPage { public abstract class ModpackPage extends SpinnerPane implements WizardPage {
public static final SettingsMap.Key<Profile> PROFILE = new SettingsMap.Key<>("PROFILE");
protected final WizardController controller; protected final WizardController controller;
protected final Label lblName; protected final Label lblName;

View File

@@ -36,6 +36,7 @@ import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
@@ -43,13 +44,10 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import static org.jackhuang.hmcl.ui.download.LocalModpackPage.MODPACK_FILE; import static org.jackhuang.hmcl.ui.download.LocalModpackPage.MODPACK_FILE;
import static org.jackhuang.hmcl.ui.download.LocalModpackPage.MODPACK_NAME; import static org.jackhuang.hmcl.ui.download.LocalModpackPage.MODPACK_NAME;
import static org.jackhuang.hmcl.ui.download.RemoteModpackPage.MODPACK_SERVER_MANIFEST; import static org.jackhuang.hmcl.ui.download.RemoteModpackPage.MODPACK_SERVER_MANIFEST;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class ModpackSelectionPage extends VBox implements WizardPage { public final class ModpackSelectionPage extends VBox implements WizardPage {
@@ -71,9 +69,9 @@ public final class ModpackSelectionPage extends VBox implements WizardPage {
createButton("repository", this::onChooseRepository) createButton("repository", this::onChooseRepository)
); );
Optional<Path> filePath = tryCast(controller.getSettings().get(MODPACK_FILE), Path.class); Path filePath = controller.getSettings().get(MODPACK_FILE);
if (filePath.isPresent()) { if (filePath != null) {
controller.getSettings().put(MODPACK_FILE, filePath.get()); controller.getSettings().put(MODPACK_FILE, filePath);
Platform.runLater(controller::onNext); Platform.runLater(controller::onNext);
} }
@@ -167,14 +165,14 @@ public final class ModpackSelectionPage extends VBox implements WizardPage {
} }
public void onChooseRepository() { public void onChooseRepository() {
String modPackName = (String) controller.getSettings().get(MODPACK_NAME); String modPackName = controller.getSettings().get(MODPACK_NAME);
DownloadPage downloadPage = new DownloadPage(modPackName); DownloadPage downloadPage = new DownloadPage(modPackName);
downloadPage.showModpackDownloads(); downloadPage.showModpackDownloads();
Controllers.navigate(downloadPage); Controllers.navigate(downloadPage);
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
} }
@Override @Override

View File

@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui.download;
import javafx.application.Platform; import javafx.application.Platform;
import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.server.ServerModpackManifest; import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.Controllers;
@@ -27,13 +28,11 @@ import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.RequiredValidator; import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class RemoteModpackPage extends ModpackPage { public final class RemoteModpackPage extends ModpackPage {
@@ -42,8 +41,9 @@ public final class RemoteModpackPage extends ModpackPage {
public RemoteModpackPage(WizardController controller) { public RemoteModpackPage(WizardController controller) {
super(controller); super(controller);
manifest = tryCast(controller.getSettings().get(MODPACK_SERVER_MANIFEST), ServerModpackManifest.class) manifest = controller.getSettings().get(MODPACK_SERVER_MANIFEST);
.orElseThrow(() -> new IllegalStateException("MODPACK_SERVER_MANIFEST should exist")); if (manifest == null)
throw new IllegalStateException("MODPACK_SERVER_MANIFEST should exist");
try { try {
controller.getSettings().put(MODPACK_MANIFEST, manifest.toModpack(null)); controller.getSettings().put(MODPACK_MANIFEST, manifest.toModpack(null));
@@ -57,10 +57,10 @@ public final class RemoteModpackPage extends ModpackPage {
lblVersion.setText(manifest.getVersion()); lblVersion.setText(manifest.getVersion());
lblAuthor.setText(manifest.getAuthor()); lblAuthor.setText(manifest.getAuthor());
Profile profile = (Profile) controller.getSettings().get("PROFILE"); Profile profile = controller.getSettings().get(ModpackPage.PROFILE);
Optional<String> name = tryCast(controller.getSettings().get(MODPACK_NAME), String.class); String name = controller.getSettings().get(MODPACK_NAME);
if (name.isPresent()) { if (name != null) {
txtModpackName.setText(name.get()); txtModpackName.setText(name);
txtModpackName.setDisable(true); txtModpackName.setDisable(true);
} else { } else {
// trim: https://github.com/HMCL-dev/HMCL/issues/962 // trim: https://github.com/HMCL-dev/HMCL/issues/962
@@ -75,7 +75,7 @@ public final class RemoteModpackPage extends ModpackPage {
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
settings.remove(MODPACK_SERVER_MANIFEST); settings.remove(MODPACK_SERVER_MANIFEST);
} }
@@ -89,7 +89,7 @@ public final class RemoteModpackPage extends ModpackPage {
Controllers.navigate(new WebPage(i18n("modpack.description"), manifest.getDescription())); Controllers.navigate(new WebPage(i18n("modpack.description"), manifest.getDescription()));
} }
public static final String MODPACK_SERVER_MANIFEST = "MODPACK_SERVER_MANIFEST"; public static final SettingsMap.Key<ServerModpackManifest> MODPACK_SERVER_MANIFEST = new SettingsMap.Key<>("MODPACK_SERVER_MANIFEST");
public static final String MODPACK_NAME = "MODPACK_NAME"; public static final SettingsMap.Key<String> MODPACK_NAME = new SettingsMap.Key<>("MODPACK_NAME");
public static final String MODPACK_MANIFEST = "MODPACK_MANIFEST"; public static final SettingsMap.Key<Modpack> MODPACK_MANIFEST = new SettingsMap.Key<>("MODPACK_MANIFEST");
} }

View File

@@ -30,6 +30,7 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.io.ResponseCodeException; import org.jackhuang.hmcl.util.io.ResponseCodeException;
@@ -40,7 +41,6 @@ import java.net.SocketTimeoutException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.zip.ZipException; import java.util.zip.ZipException;
@@ -66,30 +66,29 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
} }
@Override @Override
public void start(Map<String, Object> settings) { public void start(SettingsMap settings) {
} }
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(SettingsMap settings) {
settings.put("title", i18n("install.change_version.process")); settings.put("title", i18n("install.change_version.process"));
settings.put("success_message", i18n("install.success")); settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next)); settings.put(FailureCallback.KEY, (settings1, exception, next) -> alertFailureMessage(exception, next));
// We remove library but not save it, // We remove library but not save it,
// so if installation failed will not break down current version. // so if installation failed will not break down current version.
Task<Version> ret = Task.supplyAsync(() -> version); Task<Version> ret = Task.supplyAsync(() -> version);
List<String> stages = new ArrayList<>(); List<String> stages = new ArrayList<>();
for (Object value : settings.values()) { for (Object value : settings.asStringMap().values()) {
if (value instanceof RemoteVersion) { if (value instanceof RemoteVersion remoteVersion) {
RemoteVersion remoteVersion = (RemoteVersion) value;
ret = ret.thenComposeAsync(version -> dependencyManager.installLibraryAsync(version, remoteVersion)); ret = ret.thenComposeAsync(version -> dependencyManager.installLibraryAsync(version, remoteVersion));
stages.add(String.format("hmcl.install.%s:%s", remoteVersion.getLibraryId(), remoteVersion.getSelfVersion())); stages.add(String.format("hmcl.install.%s:%s", remoteVersion.getLibraryId(), remoteVersion.getSelfVersion()));
if ("game".equals(remoteVersion.getLibraryId())) { if ("game".equals(remoteVersion.getLibraryId())) {
stages.add("hmcl.install.libraries"); stages.add("hmcl.install.libraries");
stages.add("hmcl.install.assets"); stages.add("hmcl.install.assets");
} }
} else if (value instanceof RemoveVersionAction) { } else if (value instanceof RemoveVersionAction removeVersionAction) {
ret = ret.thenComposeAsync(version -> dependencyManager.removeLibraryAsync(version, ((RemoveVersionAction) value).libraryId)); ret = ret.thenComposeAsync(version -> dependencyManager.removeLibraryAsync(version, removeVersionAction.libraryId));
} }
} }
@@ -97,7 +96,7 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
} }
@Override @Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) { public Node createPage(WizardController controller, int step, SettingsMap settings) {
switch (step) { switch (step) {
case 0: case 0:
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> { return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> {

View File

@@ -28,8 +28,7 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -45,36 +44,37 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
} }
@Override @Override
public void start(Map<String, Object> settings) { public void start(SettingsMap settings) {
settings.put(PROFILE, profile); settings.put(ModpackPage.PROFILE, profile);
} }
private Task<Void> finishVersionDownloadingAsync(Map<String, Object> settings) { private Task<Void> finishVersionDownloadingAsync(SettingsMap settings) {
GameBuilder builder = dependencyManager.gameBuilder(); GameBuilder builder = dependencyManager.gameBuilder();
String name = (String) settings.get("name"); String name = (String) settings.get("name");
builder.name(name); builder.name(name);
builder.gameVersion(((RemoteVersion) settings.get("game")).getGameVersion()); builder.gameVersion(((RemoteVersion) settings.get("game")).getGameVersion());
for (Map.Entry<String, Object> entry : settings.entrySet()) settings.asStringMap().forEach((key, value) -> {
if (!"game".equals(entry.getKey()) && entry.getValue() instanceof RemoteVersion) if (!"game".equals(key) && value instanceof RemoteVersion remoteVersion)
builder.version((RemoteVersion) entry.getValue()); builder.version(remoteVersion);
});
return builder.buildAsync().whenComplete(any -> profile.getRepository().refreshVersions()) return builder.buildAsync().whenComplete(any -> profile.getRepository().refreshVersions())
.thenRunAsync(Schedulers.javafx(), () -> profile.setSelectedVersion(name)); .thenRunAsync(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
} }
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(SettingsMap settings) {
settings.put("title", i18n("install.new_game.installation")); settings.put("title", i18n("install.new_game.installation"));
settings.put("success_message", i18n("install.success")); settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> UpdateInstallerWizardProvider.alertFailureMessage(exception, next)); settings.put(FailureCallback.KEY, (settings1, exception, next) -> UpdateInstallerWizardProvider.alertFailureMessage(exception, next));
return finishVersionDownloadingAsync(settings); return finishVersionDownloadingAsync(settings);
} }
@Override @Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) { public Node createPage(WizardController controller, int step, SettingsMap settings) {
switch (step) { switch (step) {
case 0: case 0:
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", downloadProvider, "game", return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", downloadProvider, "game",
@@ -88,6 +88,4 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
public boolean cancel() { public boolean cancel() {
return true; return true;
} }
public static final String PROFILE = "PROFILE";
} }

View File

@@ -54,12 +54,12 @@ import org.jackhuang.hmcl.ui.wizard.Navigation;
import org.jackhuang.hmcl.ui.wizard.Refreshable; import org.jackhuang.hmcl.ui.wizard.Refreshable;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -124,7 +124,7 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
settings.remove(libraryId); settings.remove(libraryId);
} }

View File

@@ -35,6 +35,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.io.JarUtils;
import org.jackhuang.hmcl.util.io.Zipper; import org.jackhuang.hmcl.util.io.Zipper;
@@ -54,17 +55,16 @@ public final class ExportWizardProvider implements WizardProvider {
} }
@Override @Override
public void start(Map<String, Object> settings) { public void start(SettingsMap settings) {
} }
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(SettingsMap settings) {
@SuppressWarnings("unchecked") List<String> whitelist = settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION);
List<String> whitelist = (List<String>) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION); Path modpackFile = settings.get(ModpackInfoPage.MODPACK_FILE);
Path modpackFile = (Path) settings.get(ModpackInfoPage.MODPACK_FILE); ModpackExportInfo exportInfo = settings.get(ModpackInfoPage.MODPACK_INFO);
ModpackExportInfo exportInfo = (ModpackExportInfo) settings.get(ModpackInfoPage.MODPACK_INFO);
exportInfo.setWhitelist(whitelist); exportInfo.setWhitelist(whitelist);
String modpackType = (String) settings.get(ModpackTypeSelectionPage.MODPACK_TYPE); String modpackType = settings.get(ModpackTypeSelectionPage.MODPACK_TYPE);
return exportWithLauncher(modpackType, exportInfo, modpackFile); return exportWithLauncher(modpackType, exportInfo, modpackFile);
} }
@@ -278,7 +278,7 @@ public final class ExportWizardProvider implements WizardProvider {
} }
@Override @Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) { public Node createPage(WizardController controller, int step, SettingsMap settings) {
return switch (step) { return switch (step) {
case 0 -> new ModpackTypeSelectionPage(controller); case 0 -> new ModpackTypeSelectionPage(controller);
case 1 -> new ModpackInfoPage(controller, profile.getRepository(), version); case 1 -> new ModpackInfoPage(controller, profile.getRepository(), version);

View File

@@ -33,6 +33,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel; import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
@@ -162,7 +163,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
controller.getSettings().remove(MODPACK_FILE_SELECTION); controller.getSettings().remove(MODPACK_FILE_SELECTION);
} }
@@ -178,7 +179,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard
return i18n("modpack.wizard.step.2.title"); return i18n("modpack.wizard.step.2.title");
} }
public static final String MODPACK_FILE_SELECTION = "modpack.accepted"; public static final SettingsMap.Key<List<String>> MODPACK_FILE_SELECTION = new SettingsMap.Key<>("modpack.accepted");
private static final Map<String, String> TRANSLATION = mapOf( private static final Map<String, String> TRANSLATION = mapOf(
pair("minecraft/hmclversion.cfg", i18n("modpack.files.hmclversion_cfg")), pair("minecraft/hmclversion.cfg", i18n("modpack.files.hmclversion_cfg")),
pair("minecraft/servers.dat", i18n("modpack.files.servers_dat")), pair("minecraft/servers.dat", i18n("modpack.files.servers_dat")),

View File

@@ -41,6 +41,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.io.JarUtils;
@@ -57,7 +58,6 @@ import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE
import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_MODRINTH; import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_MODRINTH;
import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_SERVER; import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_SERVER;
import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES; import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class ModpackInfoPage extends Control implements WizardPage { public final class ModpackInfoPage extends Control implements WizardPage {
@@ -88,10 +88,12 @@ public final class ModpackInfoPage extends Control implements WizardPage {
public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) { public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) {
this.controller = controller; this.controller = controller;
this.gameRepository = gameRepository; this.gameRepository = gameRepository;
this.options = tryCast(controller.getSettings().get(MODPACK_INFO_OPTION), ModpackExportInfo.Options.class) this.options = controller.getSettings().get(MODPACK_INFO_OPTION);
.orElseThrow(() -> new IllegalArgumentException("Settings.MODPACK_INFO_OPTION is required"));
this.versionName = version; this.versionName = version;
if (this.options == null)
throw new IllegalArgumentException("Settings.MODPACK_INFO_OPTION is required");
name.set(version); name.set(version);
author.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse("")); author.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse(""));
@@ -147,7 +149,7 @@ public final class ModpackInfoPage extends Control implements WizardPage {
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
controller.getSettings().remove(MODPACK_INFO); controller.getSettings().remove(MODPACK_INFO);
} }
@@ -161,9 +163,9 @@ public final class ModpackInfoPage extends Control implements WizardPage {
return new ModpackInfoPageSkin(this); return new ModpackInfoPageSkin(this);
} }
public static final String MODPACK_INFO = "modpack.info"; public static final SettingsMap.Key<ModpackExportInfo> MODPACK_INFO = new SettingsMap.Key<>("modpack.info");
public static final String MODPACK_FILE = "modpack.file"; public static final SettingsMap.Key<Path> MODPACK_FILE = new SettingsMap.Key<>("modpack.file");
public static final String MODPACK_INFO_OPTION = "modpack.info.option"; public static final SettingsMap.Key<ModpackExportInfo.Options> MODPACK_INFO_OPTION = new SettingsMap.Key<>("modpack.info.option");
public static class ModpackInfoPageSkin extends SkinBase<ModpackInfoPage> { public static class ModpackInfoPageSkin extends SkinBase<ModpackInfoPage> {
private ObservableList<Node> originList; private ObservableList<Node> originList;
@@ -190,7 +192,8 @@ public final class ModpackInfoPage extends Control implements WizardPage {
Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server")); Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server"));
hyperlink.setOnAction(e -> FXUtils.openLink(Metadata.DOCS_URL + "/modpack/serverpack.html")); hyperlink.setOnAction(e -> FXUtils.openLink(Metadata.DOCS_URL + "/modpack/serverpack.html"));
borderPane.setTop(hyperlink); borderPane.setTop(hyperlink);
} if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_MODRINTH) { }
if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_MODRINTH) {
HintPane pane = new HintPane(MessageDialogPane.MessageType.INFO); HintPane pane = new HintPane(MessageDialogPane.MessageType.INFO);
pane.setText(i18n("modpack.wizard.step.initialization.modrinth.info")); pane.setText(i18n("modpack.wizard.step.initialization.modrinth.info"));
borderPane.setTop(pane); borderPane.setTop(pane);

View File

@@ -33,8 +33,7 @@ import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import static org.jackhuang.hmcl.ui.export.ModpackInfoPage.MODPACK_INFO_OPTION; import static org.jackhuang.hmcl.ui.export.ModpackInfoPage.MODPACK_INFO_OPTION;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -89,7 +88,7 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
} }
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
} }
@Override @Override
@@ -97,7 +96,7 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
return i18n("modpack.wizard.step.3.title"); return i18n("modpack.wizard.step.3.title");
} }
public static final String MODPACK_TYPE = "modpack.type"; public static final SettingsMap.Key<String> MODPACK_TYPE = new SettingsMap.Key<>("modpack.type");
public static final String MODPACK_TYPE_MCBBS = "mcbbs"; public static final String MODPACK_TYPE_MCBBS = "mcbbs";
public static final String MODPACK_TYPE_MULTIMC = "multimc"; public static final String MODPACK_TYPE_MULTIMC = "multimc";

View File

@@ -22,8 +22,8 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.construct.TaskListPane; import org.jackhuang.hmcl.ui.construct.TaskListPane;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
public abstract class AbstractWizardDisplayer implements WizardDisplayer { public abstract class AbstractWizardDisplayer implements WizardDisplayer {
@@ -34,7 +34,7 @@ public abstract class AbstractWizardDisplayer implements WizardDisplayer {
} }
@Override @Override
public void handleTask(Map<String, Object> settings, Task<?> task) { public void handleTask(SettingsMap settings, Task<?> task) {
TaskExecutor executor = task.withRunAsync(Schedulers.javafx(), this::navigateToSuccess).executor(); TaskExecutor executor = task.withRunAsync(Schedulers.javafx(), this::navigateToSuccess).executor();
TaskListPane pane = new TaskListPane(); TaskListPane pane = new TaskListPane();
pane.setExecutor(executor); pane.setExecutor(executor);

View File

@@ -18,8 +18,7 @@
package org.jackhuang.hmcl.ui.wizard; package org.jackhuang.hmcl.ui.wizard;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
public interface Navigation { public interface Navigation {
@@ -37,7 +36,7 @@ public interface Navigation {
void onCancel(); void onCancel();
Map<String, Object> getSettings(); SettingsMap getSettings();
enum NavigationDirection { enum NavigationDirection {
START(ContainerAnimations.NONE), START(ContainerAnimations.NONE),

View File

@@ -18,8 +18,8 @@
package org.jackhuang.hmcl.ui.wizard; package org.jackhuang.hmcl.ui.wizard;
import javafx.scene.Node; import javafx.scene.Node;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class SinglePageWizardProvider implements WizardProvider { public class SinglePageWizardProvider implements WizardProvider {
@@ -32,16 +32,16 @@ public class SinglePageWizardProvider implements WizardProvider {
} }
@Override @Override
public void start(Map<String, Object> settings) { public void start(SettingsMap settings) {
} }
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(SettingsMap settings) {
return page.finish(); return page.finish();
} }
@Override @Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) { public Node createPage(WizardController controller, int step, SettingsMap settings) {
if (step != 0) throw new IllegalStateException("Step must be 0"); if (step != 0) throw new IllegalStateException("Step must be 0");
return page = provider.apply(controller); return page = provider.apply(controller);

View File

@@ -25,10 +25,10 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.TaskCancellationAction;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
@@ -42,7 +42,7 @@ public abstract class TaskExecutorDialogWizardDisplayer extends AbstractWizardDi
} }
@Override @Override
public void handleTask(Map<String, Object> settings, Task<?> task) { public void handleTask(SettingsMap settings, Task<?> task) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(new TaskCancellationAction(it -> { TaskExecutorDialogPane pane = new TaskExecutorDialogPane(new TaskCancellationAction(it -> {
it.fireEvent(new DialogCloseEvent()); it.fireEvent(new DialogCloseEvent());
onEnd(); onEnd();
@@ -51,10 +51,10 @@ public abstract class TaskExecutorDialogWizardDisplayer extends AbstractWizardDi
pane.setTitle(i18n("message.doing")); pane.setTitle(i18n("message.doing"));
if (settings.containsKey("title")) { if (settings.containsKey("title")) {
Object title = settings.get("title"); Object title = settings.get("title");
if (title instanceof StringProperty) if (title instanceof StringProperty titleProperty)
pane.titleProperty().bind((StringProperty) title); pane.titleProperty().bind(titleProperty);
else if (title instanceof String) else if (title instanceof String titleMessage)
pane.setTitle((String) title); pane.setTitle(titleMessage);
} }
runInFX(() -> { runInFX(() -> {
@@ -63,8 +63,8 @@ public abstract class TaskExecutorDialogWizardDisplayer extends AbstractWizardDi
public void onStop(boolean success, TaskExecutor executor) { public void onStop(boolean success, TaskExecutor executor) {
runInFX(() -> { runInFX(() -> {
if (success) { if (success) {
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String) if (settings.get("success_message") instanceof String successMessage)
Controllers.dialog((String) settings.get("success_message"), null, MessageType.SUCCESS, () -> onEnd()); Controllers.dialog(successMessage, null, MessageType.SUCCESS, () -> onEnd());
else if (!settings.containsKey("forbid_success_message")) else if (!settings.containsKey("forbid_success_message"))
Controllers.dialog(i18n("message.success"), null, MessageType.SUCCESS, () -> onEnd()); Controllers.dialog(i18n("message.success"), null, MessageType.SUCCESS, () -> onEnd());
} else { } else {
@@ -77,10 +77,10 @@ public abstract class TaskExecutorDialogWizardDisplayer extends AbstractWizardDi
} }
String appendix = StringUtils.getStackTrace(executor.getException()); String appendix = StringUtils.getStackTrace(executor.getException());
if (settings.get("failure_callback") instanceof WizardProvider.FailureCallback) if (settings.get(WizardProvider.FailureCallback.KEY) != null)
((WizardProvider.FailureCallback) settings.get("failure_callback")).onFail(settings, executor.getException(), () -> onEnd()); settings.get(WizardProvider.FailureCallback.KEY).onFail(settings, executor.getException(), () -> onEnd());
else if (settings.get("failure_message") instanceof String) else if (settings.get("failure_message") instanceof String failureMessage)
Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageType.ERROR, () -> onEnd()); Controllers.dialog(appendix, failureMessage, MessageType.ERROR, () -> onEnd());
else if (!settings.containsKey("forbid_failure_message")) else if (!settings.containsKey("forbid_failure_message"))
Controllers.dialog(appendix, i18n("wizard.failed"), MessageType.ERROR, () -> onEnd()); Controllers.dialog(appendix, i18n("wizard.failed"), MessageType.ERROR, () -> onEnd());
} }

View File

@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui.wizard;
import javafx.scene.Node; import javafx.scene.Node;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.*; import java.util.*;
@@ -27,7 +28,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public class WizardController implements Navigation { public class WizardController implements Navigation {
private final WizardDisplayer displayer; private final WizardDisplayer displayer;
private WizardProvider provider = null; private WizardProvider provider = null;
private final Map<String, Object> settings = new HashMap<>(); private final SettingsMap settings = new SettingsMap();
private final Stack<Node> pages = new Stack<>(); private final Stack<Node> pages = new Stack<>();
private boolean stopped = false; private boolean stopped = false;
@@ -36,7 +37,7 @@ public class WizardController implements Navigation {
} }
@Override @Override
public Map<String, Object> getSettings() { public SettingsMap getSettings() {
return settings; return settings;
} }

View File

@@ -19,8 +19,7 @@ package org.jackhuang.hmcl.ui.wizard;
import javafx.scene.Node; import javafx.scene.Node;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
public interface WizardDisplayer { public interface WizardDisplayer {
default void onStart() { default void onStart() {
@@ -34,5 +33,5 @@ public interface WizardDisplayer {
void navigateTo(Node page, Navigation.NavigationDirection nav); void navigateTo(Node page, Navigation.NavigationDirection nav);
void handleTask(Map<String, Object> settings, Task<?> task); void handleTask(SettingsMap settings, Task<?> task);
} }

View File

@@ -17,13 +17,13 @@
*/ */
package org.jackhuang.hmcl.ui.wizard; package org.jackhuang.hmcl.ui.wizard;
import java.util.Map; import org.jackhuang.hmcl.util.SettingsMap;
public interface WizardPage { public interface WizardPage {
default void onNavigate(Map<String, Object> settings) { default void onNavigate(SettingsMap settings) {
} }
default void cleanup(Map<String, Object> settings) { default void cleanup(SettingsMap settings) {
} }
String getTitle(); String getTitle();

View File

@@ -18,15 +18,14 @@
package org.jackhuang.hmcl.ui.wizard; package org.jackhuang.hmcl.ui.wizard;
import javafx.scene.Node; import javafx.scene.Node;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
public interface WizardProvider { public interface WizardProvider {
void start(Map<String, Object> settings); void start(SettingsMap settings);
Object finish(Map<String, Object> settings); Object finish(SettingsMap settings);
Node createPage(WizardController controller, int step, Map<String, Object> settings); Node createPage(WizardController controller, int step, SettingsMap settings);
boolean cancel(); boolean cancel();
@@ -35,6 +34,8 @@ public interface WizardProvider {
} }
interface FailureCallback { interface FailureCallback {
void onFail(Map<String, Object> settings, Exception exception, Runnable next); SettingsMap.Key<FailureCallback> KEY = new SettingsMap.Key<>("failure_callback");
void onFail(SettingsMap settings, Exception exception, Runnable next);
} }
} }

View File

@@ -18,8 +18,7 @@
package org.jackhuang.hmcl.ui.wizard; package org.jackhuang.hmcl.ui.wizard;
import javafx.scene.control.Control; import javafx.scene.control.Control;
import org.jackhuang.hmcl.util.SettingsMap;
import java.util.Map;
public abstract class WizardSinglePage extends Control implements WizardPage { public abstract class WizardSinglePage extends Control implements WizardPage {
protected final Runnable onFinish; protected final Runnable onFinish;
@@ -31,6 +30,6 @@ public abstract class WizardSinglePage extends Control implements WizardPage {
protected abstract Object finish(); protected abstract Object finish();
@Override @Override
public void cleanup(Map<String, Object> settings) { public void cleanup(SettingsMap settings) {
} }
} }

View File

@@ -0,0 +1,89 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
/// A wrapper for `Map<String, Object>`, supporting type-safe reading and writing of values.
///
/// @author Glavo
public final class SettingsMap {
public record Key<T>(String key) {
}
private final Map<String, Object> map = new HashMap<>();
public SettingsMap() {
}
public Map<String, Object> asStringMap() {
return map;
}
public boolean containsKey(@NotNull Key<?> key) {
return map.containsKey(key.key);
}
public boolean containsKey(@NotNull String key) {
return map.containsKey(key);
}
public <T> @Nullable T get(@NotNull Key<T> key) {
@SuppressWarnings("unchecked")
T value = (T) map.get(key.key);
return value;
}
public Object get(@NotNull String key) {
return map.get(key);
}
public <T> T getOrDefault(@NotNull Key<T> key, T defaultValue) {
@SuppressWarnings("unchecked")
T value = (T) map.get(key.key);
return value != null ? value : defaultValue;
}
public <T> T put(@NotNull Key<T> key, @Nullable T value) {
@SuppressWarnings("unchecked")
T result = (T) map.put(key.key, value);
return result;
}
public Object put(@NotNull String key, @Nullable Object value) {
return map.put(key, value);
}
public <T> T remove(@NotNull Key<T> key) {
@SuppressWarnings("unchecked")
T result = (T) map.remove(key.key);
return result;
}
public Object remove(@NotNull String key) {
return map.remove(key);
}
public void clear() {
map.clear();
}
}