diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index 434b739b6f..4083ba77d1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -17,6 +17,10 @@ */ package org.jackhuang.hmcl.game; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonBar; +import javafx.scene.control.ButtonType; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.auth.AuthInfo; import org.jackhuang.hmcl.launch.DefaultLauncher; @@ -26,12 +30,16 @@ import org.jackhuang.hmcl.util.platform.ManagedProcess; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import java.io.File; import java.io.IOException; import java.nio.file.*; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.stream.Stream; import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; /** @@ -62,8 +70,18 @@ protected Map getConfigurations() { private void generateOptionsTxt() { if (config().isDisableAutoGameOptions()) return; - Path runDir = repository.getRunDirectory(version.getId()); + HMCLGameRepository HMCLRepository = (HMCLGameRepository) repository; + if (HMCLRepository.getGameDirectoryType(version.getId()) == GameDirectoryType.ROOT_FOLDER) { + if (Files.exists(Path.of(HMCLRepository.getVersionRoot(version.getId()).toString(), "resourcepacks")) || + Files.exists(Path.of(HMCLRepository.getVersionRoot(version.getId()).toString(), "saves")) || + Files.exists(Path.of(HMCLRepository.getVersionRoot(version.getId()).toString(), "mods")) || + Files.exists(Path.of(HMCLRepository.getVersionRoot(version.getId()).toString(), "shaderpacks")) || + Files.exists(Path.of(HMCLRepository.getVersionRoot(version.getId()).toString(), "crash-report")) + ) { + runDir = switchWorkingDirectory(HMCLRepository, version); + } + } Path optionsFile = runDir.resolve("options.txt"); Path configFolder = runDir.resolve("config"); @@ -82,12 +100,12 @@ private void generateOptionsTxt() { Locale locale = Locale.getDefault(); /* - * 1.0 : No language option, do not set for these versions - * 1.1 ~ 1.5 : zh_CN works fine, zh_cn will crash (the last two letters must be uppercase, otherwise it will cause an NPE crash) - * 1.6 ~ 1.10 : zh_CN works fine, zh_cn will automatically switch to English - * 1.11 ~ 1.12 : zh_cn works fine, zh_CN will display Chinese but the language setting will incorrectly show English as selected - * 1.13+ : zh_cn works fine, zh_CN will automatically switch to English - */ + * 1.0 : No language option, do not set for these versions + * 1.1 ~ 1.5 : zh_CN works fine, zh_cn will crash (the last two letters must be uppercase, otherwise it will cause an NPE crash) + * 1.6 ~ 1.10 : zh_CN works fine, zh_cn will automatically switch to English + * 1.11 ~ 1.12 : zh_cn works fine, zh_CN will display Chinese but the language setting will incorrectly show English as selected + * 1.13+ : zh_cn works fine, zh_CN will automatically switch to English + */ GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(version)); if (gameVersion.compareTo("1.1") < 0) return; @@ -107,6 +125,54 @@ private void generateOptionsTxt() { } } + private Path switchWorkingDirectory(HMCLGameRepository HMCLRepository, Version version) { + if (Platform.isFxApplicationThread()) { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION, + String.format(i18n("launcher.info.switch_working_directory.content"), File.separatorChar, version.getId()), + ButtonType.YES, ButtonType.NO, new ButtonType(i18n("Dialog.this_launch_only.button"), ButtonBar.ButtonData.APPLY) + ); + alert.setTitle(i18n("launcher.info.switch_working_directory.title")); + switch (alert.showAndWait().orElse(ButtonType.YES).getButtonData()) { + case YES -> { + HMCLRepository.getVersionSetting(version.getId()).setGameDirType(GameDirectoryType.VERSION_FOLDER); + return HMCLRepository.getVersionRoot(version.getId()); + } + case NO -> { return HMCLRepository.getBaseDirectory(); } + case APPLY -> HMCLRepository.getVersionRoot(version.getId()); + default -> { return HMCLRepository.getBaseDirectory(); } + } + } else { + CompletableFuture buttonDataFuture = new CompletableFuture<>(); + + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION, + String.format(i18n("launcher.info.switch_working_directory.content"), File.separatorChar, version.getId()), + ButtonType.YES, ButtonType.NO, new ButtonType(i18n("Dialog.this_launch_only.button"), ButtonBar.ButtonData.APPLY) + ); + alert.setTitle(i18n("launcher.info.switch_working_directory.title")); + buttonDataFuture.complete(alert.showAndWait().orElse(ButtonType.YES).getButtonData()); + }); + + ButtonBar.ButtonData result; + try { + result = buttonDataFuture.get(); + } catch (InterruptedException | ExecutionException e) { + result = ButtonBar.ButtonData.YES; + } + + switch (result) { + case YES -> { + HMCLRepository.getVersionSetting(version.getId()).setGameDirType(GameDirectoryType.VERSION_FOLDER); + return HMCLRepository.getVersionRoot(version.getId()); + } + case NO -> { return HMCLRepository.getBaseDirectory(); } + case APPLY -> HMCLRepository.getVersionRoot(version.getId()); + default -> { return HMCLRepository.getBaseDirectory(); } + } + } + return HMCLRepository.getVersionRoot(version.getId()); + } + private static String normalizedLanguageTag(Locale locale, GameVersionNumber gameVersion) { String region = locale.getCountry(); 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 f77a236a42..b6e258ddfc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -201,7 +201,7 @@ private void loadLocalVersionSetting(String id) { VersionSetting versionSetting = JsonUtils.fromJsonFile(file, VersionSetting.class); initLocalVersionSetting(id, versionSetting); } catch (Exception ex) { - // If [JsonParseException], [IOException] or [NullPointerException] happens, the json file is malformed and needed to be recreated. + // If [JsonParseException], [IOException] or [NullPointerException] happens, the JSON file is malformed and needed to be recreated. initLocalVersionSetting(id, new VersionSetting()); } } @@ -231,7 +231,7 @@ private VersionSetting initLocalVersionSetting(String id, VersionSetting vs) { * Get the version setting for version id. * * @param id version id - * @return corresponding version setting, null if the version has no its own version setting. + * @return corresponding version setting, null if the version does not have its own version setting. */ @Nullable public VersionSetting getLocalVersionSetting(String id) { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index e34ee60f2d..8fabe5f89b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -376,6 +376,8 @@ download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s +Dialog.this_launch_only.button=This Launch only + exception.access_denied=HMCL is unable to access the file "%s". It may be locked by another process.\n\ \n\ For Windows users, you can open "Resource Monitor" to check if another process is currently using it. If so, you can try again after terminating that process.\n\ @@ -827,6 +829,7 @@ launch.state.modpack=Downloading required files launch.state.waiting_launching=Waiting for the game to launch launch.invalid_java=Invalid Java path. Please reset the Java path. + launcher=Launcher launcher.agreement=ToS and EULA launcher.agreement.accept=Accept @@ -850,6 +853,9 @@ launcher.crash=Hello Minecraft! Launcher has encountered a fatal error! Please c launcher.crash.java_internal_error=Hello Minecraft! Launcher has encountered a fatal error because your Java is corrupted. Please uninstall your Java and download a suitable Java here. launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher has encountered a fatal error! Your launcher is outdated. Please update your launcher! launcher.update_java=Please update your Java version. +launcher.info.switch_working_directory.title=Would you like to switch the Working Directory? +# %1$s = java.io.File.separator, %2$s = version root (Don't ends with java.io.File.separator) +launcher.info.switch_working_directory.content=We detected unexpected content in .minecraft%1$sversions%1$s%2$s. It's likely you selected "Isolated" mode, but it was switched to "Default" mode automatically or manually. The choice to readjust to "Isolated" mode is available to you.\nRe-adjust to 'Isolated' mode and make it effective for this launch, press "Yes". if you want to retain current version settings, press "No". if you want make it effective for this launch only, press "This launch only". libraries.download=Downloading Libraries diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index 187db50375..46d308e148 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -305,6 +305,8 @@ download.javafx.notes=ネットワークを介したHMCLに必要なコンポー download.javafx.component=ダウンロード中のモジュール %s download.javafx.prepare=ダウンロードする準備ができました +Dialog.this_launch_only.button=今回のみ + exception.access_denied=ファイル %s にアクセスできないので、HMCL がファイルにアクセスできないか、ファイルが他のプログラムによって開かれています。\n\ 例えば、管理者でないユーザーは、他のアカウントの個人フォルダーにあるファイルにアクセスできない場合があります。\n\ Windowsユーザーの場合、リソースモニターでプログラムがファイルを占有しているかどうかを確認し、もしそうなら、このファイルを占有している関連プログラムを閉じるか、コンピュータを再起動して、もう一度試してみることも可能です。 @@ -539,6 +541,9 @@ launcher.contact=お問い合わせ launcher.crash=Hello Minecraft!ランチャーがクラッシュしました!次のコンテンツをコピーして、MCBBS、Baidu Tieba、GitHub、またはMinecraftForumを介してフィードバックを送信してください。 launcher.crash.hmcl_out_dated=Hello Minecraft!ランチャーがクラッシュしました!ランチャーが古くなっています。ランチャーを更新してください! launcher.update_java=Javaを更新してください。 +launcher.info.switch_working_directory.title=作業ディレクトリを切り替えますか? +# %1$s = java.io.File.separator, %2$s = version root (Don't ends with java.io.File.separator) +launcher.info.switch_working_directory.content=.minecraft%1$sversions%1$s%2$s で予期しないコンテンツを検出しました。「隔離」モードを選択していた可能性がありますが、自動または手動で「デフォルト」モードに切り替えられています。今から「隔離」モードに再調整できます。\n「隔離」モードに再調整し、今回の起動で有効にするには「はい」を、現在のバージョン設定を維持するには「いいえ」を、今回の起動のみに有効にするには「今回のみ」を押してください。 login.empty_username=ユーザー名を設定していません! login.enter_password=パスワードを入力してください。 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 27be6ce69e..b3fa1907f6 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -367,6 +367,8 @@ download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s +Dialog.this_launch_only.button=僅限本次啟動 + exception.access_denied=無法存取檔案「%s」。因為 HMCL 沒有對該檔案的訪問權限,或者該檔案已被其他程式開啟。\n\ 請你檢查目前作業系統帳戶是否能訪存取檔案,比如非管理員使用者可能不能訪問其他帳戶的個人目錄內的檔案。\n\ 對於 Windows 使用者,你還可以嘗試透過資源監視器查看是否有程式占用了該檔案。如果是,你可以關閉占用此檔案的程式,或者重啟電腦再試。 @@ -649,6 +651,9 @@ launcher.crash=Hello Minecraft! Launcher 遇到了無法處理的錯誤。請複 launcher.crash.java_internal_error=Hello Minecraft! Launcher 由於目前 Java 損壞而無法繼續執行。請移除目前 Java,點擊 此處 安裝合適的 Java 版本。 launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了無法處理的錯誤。已偵測到你的啟動器不是最新版本,請更新後重試! launcher.update_java=請更新你的 Java +launcher.info.switch_working_directory.title=是否要切換工作目錄? +# %1$s = java.io.File.separator, %2$s = version root (Don't ends with java.io.File.separator) +launcher.info.switch_working_directory.content=我們在 .minecraft%1$sversions%1$s%2$s 中偵測到意外的內容。您可能選擇了「隔離」模式,但該模式已被自動或手動切換為「預設」模式。您現在可以重新調整為「隔離」模式。\n若想重新調整為「隔離」模式並在本次啟動中生效,請按「是」;若想保留目前版本設定,請按「否」;若想僅對本次啟動生效,請按「僅本次啟動」。 libraries.download=下載依賴庫 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index c3a5549d31..1ab0505371 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -369,6 +369,8 @@ download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s +Dialog.this_launch_only.button=仅本次启动 + exception.access_denied=无法访问文件“%s”。HMCL 没有对该文件的访问权限,或者该文件已被其他程序打开。\n\ 请你检查当前操作系统账户是否能访问该文件,比如非管理员用户可能无法访问其他账户的个人文件夹内的文件。\n\ 对于 Windows 用户,你还可以尝试通过资源监视器查看是否有程序占用了该文件。如果是,请关闭占用该文件的程序,或者重启电脑再试。\n\ @@ -653,6 +655,9 @@ launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误。请复 launcher.crash.java_internal_error=Hello Minecraft! Launcher 由于当前 Java 损坏而无法继续运行。请卸载当前 Java,点击 此处 安装合适的 Java 版本。 launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误。已检测到你的启动器不是最新版本,请更新后再试。 launcher.update_java=请更新你的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。 +launcher.info.switch_working_directory.title=是否要切换工作目录? +# %1$s = java.io.File.separator, %2$s = version root (Don't ends with java.io.File.separator) +launcher.info.switch_working_directory.content=我们在 .minecraft%1$sversions%1$s%2$s 中检测到意外的内容。您可能选择了“隔离”模式,但该模式已被自动或手动切换为“默认”模式。您现在可以重新调整为“隔离”模式。\n若想重新调整为“隔离”模式并在本次启动中生效,请按“是”;若想保留当前版本设置,请按“否”;若想仅对本次启动生效,请按“仅本次启动”。 libraries.download=下载依赖库