diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index b496177db0..23df78538f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -27,16 +27,11 @@ import org.jackhuang.hmcl.game.LauncherHelper; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.java.JavaManager; import org.jackhuang.hmcl.java.JavaRuntime; -import org.glavo.url.WebURL; import org.jackhuang.hmcl.modpack.Modpack; -import org.jackhuang.hmcl.modpack.server.ServerModpackManifest; import org.jackhuang.hmcl.setting.*; -import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; -import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; -import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.ui.account.AccountListPage; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.construct.*; @@ -359,8 +354,8 @@ public final class Controllers { ); } - /// Installs a launcher-bundled modpack when present; otherwise, when the repository has no - /// instances yet, pulls the org client [Metadata#getClientManifestUrl] and opens the install wizard. + /// Installs a launcher-bundled modpack when present. First-run remote install is deferred to the + /// home Start button ([Instances#ensureClientAndJoin]) so players never enter the install wizard. private static void tryInstallBundledModpack(HMCLGameRepository repository) { @Nullable Path modpackFile = Metadata.findBundledModpackFile(); if (modpackFile != null) { @@ -384,45 +379,7 @@ public final class Controllers { } }), i18n("modpack.installing"), TaskCancellationAction.NO_CANCEL ); - return; } - - if (repository.getDisplayInstances().findAny().isPresent()) { - return; - } - - String manifestUrl = Metadata.getClientManifestUrl(); - LOG.info("No instances yet; fetching client Server Modpack from " + manifestUrl); - Controllers.taskDialog( - new GetTask(WebURL.parse(manifestUrl)) - .whenComplete(Schedulers.javafx(), (result, exception) -> { - if (exception != null) { - LOG.warning("Failed to fetch client Server Modpack manifest", exception); - Controllers.dialog( - manifestUrl + "\n" + exception.getMessage(), - i18n("message.error"), - MessageType.ERROR); - Instances.importModpack(); - return; - } - ServerModpackManifest manifest = - JsonUtils.fromMaybeMalformedJson(result, ServerModpackManifest.class); - if (manifest == null) { - LOG.warning("Malformed client Server Modpack manifest from " + manifestUrl); - Controllers.dialog( - i18n("modpack.type.server.malformed"), - i18n("message.error"), - MessageType.ERROR); - Instances.importModpack(); - return; - } - Controllers.getDecorator().startWizard( - new ModpackInstallWizardProvider(repository, manifest), - i18n("install.modpack")); - }), - i18n("message.downloading"), - TaskCancellationAction.NO_CANCEL - ); } /// Shows the content in the main-window dialog stack without waiting for it to close. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java index 232ad88c51..aac6ac6b40 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java @@ -14,6 +14,7 @@ import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.setting.*; import org.glavo.url.WebURL; +import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.modpack.server.ServerModpackManifest; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Schedulers; @@ -25,21 +26,26 @@ import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.PromptDialogPane; import org.jackhuang.hmcl.ui.construct.Validator; -import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; +import org.jackhuang.hmcl.ui.download.LocalModpackPage; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.jackhuang.hmcl.modpack.Modpack; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.stream.Collectors; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -48,63 +54,198 @@ public final class Instances { private Instances() { } - /// Installs or updates the TDLM client. + /// Returns whether the installed server modpack is behind the remote `server-manifest.json`. /// - /// Prefers a launcher-bundled zip, then the org [Metadata#getClientManifestUrl] Server Modpack, - /// then the generic local/remote import wizard. - public static void installServerModpack() { + /// Compares the local embedded manifest version (and file path/hash set) with + /// `fileApi/server-manifest.json`. Network or parse failures return `false` so the home button + /// stays on "join". + public static boolean isClientUpdateAvailable(@Nullable HMCLGameInstance instance) { + if (instance == null) { + return false; + } + try { + Path configurationFile = instance.getModpackConfigurationFile(); + if (Files.notExists(configurationFile)) { + return false; + } + ModpackConfiguration configuration = JsonUtils.fromJsonFile( + configurationFile, ModpackConfiguration.typeOf(ServerModpackManifest.class)); + ServerModpackManifest local = configuration.getManifest(); + if (local == null || StringUtils.isBlank(local.getFileApi())) { + return false; + } + String manifestUrl = StringUtils.removeSuffix(local.getFileApi().trim(), "/") + "/server-manifest.json"; + ServerModpackManifest remote = JsonUtils.fromNonNullJson( + NetworkUtils.doGet(manifestUrl), ServerModpackManifest.class); + if (!Objects.equals(local.getVersion(), remote.getVersion())) { + return true; + } + return !fileHashes(local).equals(fileHashes(remote)); + } catch (Exception e) { + LOG.warning("Failed to check TDLM client update", e); + return false; + } + } + + private static Map fileHashes(ServerModpackManifest manifest) { + return manifest.getFiles().stream().collect(Collectors.toMap( + ModpackConfiguration.FileInformation::getPath, + ModpackConfiguration.FileInformation::getHash, + (left, right) -> right)); + } + + /// Installs or updates the TDLM client from fileApi / bundled zip without opening the install UI, + /// then joins the configured server when ready. + /// + /// Prefer this for the home Start / Join button. Existing server-modpack instances get differential + /// updates during launch via [org.jackhuang.hmcl.modpack.server.ServerModpackCompletionTask]. + public static void ensureClientAndJoin() { HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); if (!repository.isLoaded()) { return; } + // Already installed: join immediately. Launch runs ServerModpackCompletionTask for updates. + if (repository.getSelectedInstance() != null && Metadata.findBundledModpackFile() == null) { + joinServer(repository.getSelectedInstance()); + return; + } + Path bundled = Metadata.findBundledModpackFile(); if (bundled != null) { - Controllers.getDecorator().startWizard( - new ModpackInstallWizardProvider(repository, bundled), - i18n("install.modpack")); + installBundledThenJoin(repository, bundled); return; } Controllers.taskDialog( new GetTask(WebURL.parse(Metadata.getClientManifestUrl())) - .whenComplete(Schedulers.javafx(), (result, exception) -> { - if (exception != null) { - Controllers.dialog( - exception.getMessage(), - i18n("message.error"), - MessageDialogPane.MessageType.ERROR); - importModpack(); - return; - } + .thenComposeAsync(Schedulers.javafx(), result -> { ServerModpackManifest manifest = JsonUtils.fromMaybeMalformedJson( result, ServerModpackManifest.class); if (manifest == null) { + throw new IOException(i18n("modpack.type.server.malformed")); + } + return ensureRemoteClientInstalled(repository, manifest); + }) + .whenComplete(Schedulers.javafx(), (ignored, exception) -> { + if (exception != null) { + LOG.warning("Failed to install or update TDLM client", exception); Controllers.dialog( - i18n("modpack.type.server.malformed"), - i18n("message.error"), + StringUtils.getStackTrace(exception), + i18n("install.failed"), MessageDialogPane.MessageType.ERROR); - importModpack(); return; } - Controllers.getDecorator().startWizard( - new ModpackInstallWizardProvider(repository, manifest), - i18n("install.modpack")); + joinServer(GameDirectoryManager.getSelectedRepository().getSelectedInstance()); }), - i18n("message.downloading"), + i18n("modpack.installing"), TaskCancellationAction.NORMAL); } - public static void importModpack() { - HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); - if (repository.isLoaded()) { - Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(repository), i18n("install.modpack")); - } + /// Silent first-install / update from the org client manifest — no wizard UI. + public static void installServerModpack() { + ensureClientAndJoin(); } - /// @deprecated use [#installServerModpack()] + /// Install wizard is disabled for the dedicated server-client launcher. + public static void importModpack() { + LOG.info("Ignoring importModpack: install UI is disabled; use Start to install/update"); + } + + /// @deprecated use [#ensureClientAndJoin()] public static void addNewGame() { - installServerModpack(); + ensureClientAndJoin(); + } + + /// Installs a local modpack zip without the wizard, then selects the new instance. + public static void installLocalModpackSilently(HMCLGameRepository repository, Path file) { + if (!repository.isLoaded()) { + return; + } + Controllers.taskDialog( + Task.composeAsync(Schedulers.io(), () -> { + Modpack modpack = ModpackHelper.readModpackManifest(file); + String name = LocalModpackPage.processModpackName(modpack.getName()); + if (StringUtils.isBlank(name)) { + name = "TDLM"; + } + GameInstanceID instanceId = new GameInstanceID(name); + if (repository.hasInstance(instanceId)) { + instanceId = new GameInstanceID(name + "-" + System.currentTimeMillis()); + } + GameInstanceID id = instanceId; + return ModpackHelper.getInstallTask(repository, file, id, modpack, null) + .thenRunAsync(Schedulers.javafx(), () -> + repository.setSelectedInstance(repository.getInstance(id))); + }) + .whenComplete(Schedulers.javafx(), (ignored, exception) -> { + if (exception != null) { + LOG.warning("Failed to install local modpack", exception); + Controllers.dialog( + StringUtils.getStackTrace(exception), + i18n("install.failed"), + MessageDialogPane.MessageType.ERROR); + } + }), + i18n("modpack.installing"), + TaskCancellationAction.NORMAL); + } + + private static void installBundledThenJoin(HMCLGameRepository repository, Path bundled) { + Controllers.taskDialog( + Task.composeAsync(Schedulers.io(), () -> { + Modpack modpack = ModpackHelper.readModpackManifest(bundled); + String name = LocalModpackPage.processModpackName(modpack.getName()); + if (StringUtils.isBlank(name)) { + name = "TDLM"; + } + GameInstanceID instanceId = new GameInstanceID(name); + return ModpackHelper.getInstallTask(repository, bundled, instanceId, modpack, null) + .thenRunAsync(Schedulers.javafx(), () -> { + repository.setSelectedInstance(repository.getInstance(instanceId)); + try { + Files.deleteIfExists(bundled); + } catch (IOException e) { + LOG.warning("Failed to delete bundled modpack: " + bundled, e); + } + }); + }) + .whenComplete(Schedulers.javafx(), (ignored, exception) -> { + if (exception != null) { + LOG.warning("Failed to install bundled modpack", exception); + Controllers.dialog( + StringUtils.getStackTrace(exception), + i18n("install.failed"), + MessageDialogPane.MessageType.ERROR); + return; + } + joinServer(GameDirectoryManager.getSelectedRepository().getSelectedInstance()); + }), + i18n("modpack.installing"), + TaskCancellationAction.NO_CANCEL); + } + + /// Installs when missing; selects an existing instance otherwise. + /// + /// Differential updates for an installed server modpack run during launch via + /// [org.jackhuang.hmcl.modpack.server.ServerModpackCompletionTask]. + private static Task ensureRemoteClientInstalled( + HMCLGameRepository repository, ServerModpackManifest manifest) throws IOException { + Modpack modpack = manifest.toModpack(); + String name = LocalModpackPage.processModpackName(manifest.getName()); + if (StringUtils.isBlank(name)) { + name = "TDLM"; + } + GameInstanceID instanceId = new GameInstanceID(name); + + if (repository.hasInstance(instanceId)) { + repository.setSelectedInstance(repository.getInstance(instanceId)); + return Task.completed(null); + } + + return ModpackHelper.getInstallTask(repository, manifest, instanceId, modpack) + .thenRunAsync(Schedulers.javafx(), () -> + repository.setSelectedInstance(repository.getInstance(instanceId))); } public static void deleteInstance(HMCLGameInstance gameInstance) { @@ -257,8 +398,10 @@ public final class Instances { .addQuestion(new PromptDialogPane.Builder.BooleanQuestion(i18n("instance.manage.duplicate.duplicate_save"), false))); } + /// Updates via fileApi through the Start flow — no install wizard. public static void updateInstance(HMCLGameInstance gameInstance) { - Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(gameInstance.getRepository(), gameInstance.getId())); + gameInstance.getRepository().setSelectedInstance(gameInstance); + ensureClientAndJoin(); } /// Queues an asset check with a fresh asset index for the supplied instance. @@ -338,14 +481,7 @@ public final class Instances { @SafeVarargs public static void launch(@Nullable HMCLGameInstance gameInstance, Consumer... injecters) { if (gameInstance == null) { - JFXButton installModpack = new JFXButton(i18n("install.modpack")); - installModpack.getStyleClass().add("dialog-accept"); - installModpack.setOnAction(e -> installServerModpack()); - - Controllers.confirmAction(i18n("instance.empty.launch"), i18n("launch.failed"), - MessageDialogPane.MessageType.ERROR, - installModpack, - null); + ensureClientAndJoin(); return; } @@ -412,11 +548,10 @@ public final class Instances { Controllers.navigate(Controllers.getSettingsPage()); } + /// Instance management UI is disabled for the dedicated server-client launcher. public static void modifyGameSettings(HMCLGameInstance gameInstance) { - Controllers.getGameInstancePage().setInstance(gameInstance.getId(), gameInstance.getRepository()); - Controllers.getGameInstancePage().showInstanceSettings(); - // VersionPage.loadVersion will be invoked after navigation - Controllers.navigate(Controllers.getGameInstancePage()); + LOG.info("Ignoring modifyGameSettings for " + gameInstance.getId() + + ": instance management UI is disabled"); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 003aad6206..9807400fea 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -58,6 +58,9 @@ public final class MainPage extends StackPane implements DecoratorPage { BindingMapping.of(GameDirectoryManager.selectedRepositoryProperty()) .flatMap(HMCLGameRepository::snapshotProperty); + /// Bumped when the home join button starts a new remote-update check, so stale results are ignored. + private int updateCheckGeneration; + { HBox titleNode = new HBox(8); titleNode.setPadding(new Insets(0, 0, 0, 2)); @@ -135,18 +138,20 @@ public final class MainPage extends StackPane implements DecoratorPage { card.setMaxHeight(Region.USE_PREF_SIZE); StackPane.setAlignment(card, Pos.BOTTOM_CENTER); + // Start always installs/updates from fileApi then joins — never opens install or instance UI. + FXUtils.setOnActionWithCooldown(joinButton, MainPage.this::launch); + FXUtils.onChangeAndOperate(currentGameProperty(), currentGame -> { + refreshJoinButton(joinButton, currentGame); if (currentGame == null) { - joinButton.setText(i18n("instance.launch.empty")); - FXUtils.setOnActionWithCooldown(joinButton, MainPage.this::launchNoGame); packValue.setText(i18n("home.not_installed")); routeFact.setVisible(false); routeFact.setManaged(false); serverFact.setVisible(false); serverFact.setManaged(false); + routeFact.setOnMouseClicked(null); + routeFact.setCursor(Cursor.DEFAULT); } else { - joinButton.setText(i18n("server.join")); - FXUtils.setOnActionWithCooldown(joinButton, MainPage.this::launch); packValue.setText(currentGame.getId().toString()); ServerEndpoint.@Nullable Route route = ServerEndpoint.selected(currentGame.getModpackConfigurationFile()); routeFact.setVisible(route != null); @@ -181,6 +186,25 @@ public final class MainPage extends StackPane implements DecoratorPage { getChildren().addAll(art, hero, card); } + /// Sets the home button to install / update / join based on the selected instance and remote manifest. + private void refreshJoinButton(JFXButton joinButton, @Nullable HMCLGameInstance currentGame) { + int generation = ++updateCheckGeneration; + if (currentGame == null) { + joinButton.setText(i18n("instance.launch.empty")); + return; + } + joinButton.setText(i18n("server.join")); + Schedulers.io().execute(() -> { + boolean updateAvailable = Instances.isClientUpdateAvailable(currentGame); + FXUtils.runInFX(() -> { + if (generation != updateCheckGeneration || getCurrentGame() != currentGame) { + return; + } + joinButton.setText(i18n(updateAvailable ? "server.update" : "server.join")); + }); + }); + } + /// Pings the server behind `route` in the background and shows the result in `serverValue`. /// /// WebSocket routes cannot be pinged with plain TCP, so the status is hidden for them. @@ -214,14 +238,9 @@ public final class MainPage extends StackPane implements DecoratorPage { return label; } + /// Installs or updates the client from fileApi when needed, then launches into the server. private void launch() { - HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); - Instances.joinServer(repository.getSelectedInstance()); - } - - /// Opens the server modpack install/update flow when no instance is selected. - private void launchNoGame() { - Instances.installServerModpack(); + Instances.ensureClientAndJoin(); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 22ff961f62..058f40da86 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -4,7 +4,6 @@ package org.jackhuang.hmcl.ui.main; import com.jfoenix.controls.JFXPopup; import javafx.beans.property.ReadOnlyObjectProperty; -import org.jackhuang.hmcl.game.HMCLGameInstance; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.GameDirectoryManager; @@ -17,8 +16,6 @@ import org.jackhuang.hmcl.ui.account.AccountListPopupMenu; import org.jackhuang.hmcl.ui.construct.AdvancedListItem; import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; -import org.jackhuang.hmcl.ui.instances.GameAdvancedListItem; import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.util.io.FileUtils; import org.jetbrains.annotations.Nullable; @@ -43,20 +40,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { accountListItem.setOnAction(e -> AccountListPopupMenu.show(accountListItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, 0, -accountListItem.getHeight())); accountListItem.accountProperty().bind(Accounts.selectedAccountProperty()); - // second item in left sidebar - GameAdvancedListItem gameInstanceItem = new GameAdvancedListItem(); - gameInstanceItem.setOnAction(e -> { - @Nullable HMCLGameInstance instance = GameDirectoryManager.getSelectedRepository().getSelectedInstance(); - if (instance == null) { - Instances.installServerModpack(); - } else { - Instances.modifyGameSettings(instance); - } - }); - if (AnimationUtils.isAnimationEnabled()) { - gameInstanceItem.setPrepareAction(Controllers::prepareGameInstancePage); - } - + // Instance management entry removed: Start on the home page installs/updates and joins. AdvancedListItem launcherSettingsItem = new AdvancedListItem(); launcherSettingsItem.setLeftIcon(SVG.SETTINGS); launcherSettingsItem.setTitle(i18n("settings")); @@ -68,9 +52,8 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { launcherSettingsItem.setPrepareAction(Controllers::prepareSettingsPage); } - // The sidebar holds the modpack and settings entries; the account sits at the bottom-left. + // Sidebar: settings only; account sits at the bottom-left. getSidebar() - .add(gameInstanceItem) .add(launcherSettingsItem); setSidebarBottom(accountListItem); @@ -92,9 +75,9 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { modpacks -> { Path file = modpacks.get(0); if (ModpackHelper.isFileModpackByExtension(file)) { - Controllers.getDecorator().startWizard( - new ModpackInstallWizardProvider(GameDirectoryManager.getSelectedRepository(), file), - i18n("install.modpack")); + // Silent install — no modpack wizard UI. + Instances.installLocalModpackSilently( + GameDirectoryManager.getSelectedRepository(), file); } else if ("json".equalsIgnoreCase(FileUtils.getExtension(file))) { Instances.installFromJson(GameDirectoryManager.getSelectedRepository(), file); } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index c15f09d905..b4d4b2384e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -809,6 +809,7 @@ first_run.invalid=Use 1 to 16 letters, digits or underscores. home.server=Server home.server.unreachable=Unreachable server.join=Join Server +server.update=Update instance.launch_and_enter_world=Play World instance.launch.empty=Start Game instance.launch.empty.installing=Installing Game diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 312af8ad62..ffabf38d43 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -629,6 +629,7 @@ first_run.invalid=一至十六字,限英文數字底綫。 home.server=服 home.server.unreachable=不通 server.join=入服 +server.update=更新 # instance.launch_and_enter_world= instance.launch.empty=啟戲 instance.launch.empty.installing=裝戲 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index b4744ecd6d..ccfe52bb9c 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -632,6 +632,7 @@ first_run.invalid=只能用 1 到 16 个英文字母、数字或下划线。 home.server=服务器 home.server.unreachable=连接不上 server.join=进入服务器 +server.update=更新 instance.launch_and_enter_world=进入世界 instance.launch.empty=开始游戏 instance.launch.empty.installing=安装游戏 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index a43cad2e00..a59141ecad 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -626,6 +626,7 @@ first_run.invalid=只能使用 1 到 16 個英文字母、數字或底線。 home.server=伺服器 home.server.unreachable=無法連線 server.join=進入伺服器 +server.update=更新 instance.launch_and_enter_world=進入世界 instance.launch.empty=開始遊戲 instance.launch.empty.installing=安裝遊戲 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java index 3416d25f7e..02a4a4dfb4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java @@ -213,6 +213,6 @@ public class ServerModpackCompletionTask extends Task { public void postExecute() throws Exception { if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return; Files.createDirectories(configurationFile.getParent()); - JsonUtils.writeToJsonFile(configurationFile, new ModpackConfiguration<>(remoteManifest, this.manifest.getType(), this.manifest.getName(), this.manifest.getVersion(), remoteManifest.getFiles())); + JsonUtils.writeToJsonFile(configurationFile, new ModpackConfiguration<>(remoteManifest, this.manifest.getType(), this.manifest.getName(), remoteManifest.getVersion(), remoteManifest.getFiles())); } }