Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5be81d6
Prompt user to switch working directory if unexpected content found
deepsleep-v3 Jan 18, 2026
92ea64e
Refactor path construction in ROOT_FOLDER check
deepsleep-v3 Jan 18, 2026
00bf5a2
Handle user response in working directory switch alert
deepsleep-v3 Jan 18, 2026
a4e66d7
fix(ui): 修复Alert在非JavaFX线程中使用的线程安全问题
deepsleep-v3 Jan 19, 2026
37a03fc
fix(JavaDoc): 修复了一些文档中的语法错误和文件格式应当保持大写
deepsleep-v3 Jan 19, 2026
bcd4a87
Moved "Switch Working Directory".
deepsleep-v3 Jan 19, 2026
e238c11
fix(bug):填补了未填充的大括号
deepsleep-v3 Jan 19, 2026
f0329a3
fix:格式
deepsleep-v3 Jan 19, 2026
5becbaf
修复了额外导入的同包下内容
deepsleep-v3 Jan 19, 2026
612aa8f
fix:修复了本人发现的问题及PR#5260所提到的一些批判
deepsleep-v3 Jan 19, 2026
1d52a58
add+fix: 修正了格式,并添加亚洲语言(zh-Hant、zh-CN、ja-JP)的翻译。
deepsleep-v3 Jan 19, 2026
95d2876
UPDATE
CiiLu Jan 19, 2026
cd090e7
Update HMCL/src/main/resources/assets/lang/I18N.properties
deepsleep-v3 Jan 20, 2026
5d2ff48
Update HMCL/src/main/resources/assets/lang/I18N.properties
deepsleep-v3 Jan 20, 2026
9c5df2f
Update HMCL/src/main/resources/assets/lang/I18N.properties
deepsleep-v3 Jan 20, 2026
1619480
Update HMCL/src/main/resources/assets/lang/I18N_zh.properties
deepsleep-v3 Jan 20, 2026
84b1969
Update HMCL/src/main/resources/assets/lang/I18N_zh.properties
deepsleep-v3 Jan 20, 2026
e35dc82
Update HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties
deepsleep-v3 Jan 20, 2026
31cc0c4
Update HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties
deepsleep-v3 Jan 20, 2026
9c5440d
Merge pull request #1 from CiiLu/deepsleep-v3/main
deepsleep-v3 Jan 20, 2026
c6e33a9
fallback ja_JP
deepsleep-v3 Jan 20, 2026
6bab585
Merge branch 'main' of https://github.com/deepsleep-v3/HMCL
deepsleep-v3 Jan 20, 2026
294299d
Merge branch 'HMCL-dev:main' into main
deepsleep-v3 Jan 20, 2026
d69676b
fixed format
deepsleep-v3 Jan 20, 2026
06c0cc1
Merge branch 'main' of https://github.com/deepsleep-v3/HMCL
deepsleep-v3 Jan 20, 2026
9fea525
一些语法错误(Part Ⅰ)
deepsleep-v3 Jan 20, 2026
0f5e7ef
移动语法错误到新PR、更改按钮分类(半成))
deepsleep-v3 Jan 22, 2026
78b3ea3
阻止用户关闭Dialog
deepsleep-v3 Jan 24, 2026
d0bb7eb
注明从awt库导入什么,以及修正格式
deepsleep-v3 Jan 24, 2026
f647c0d
修复ESC逻辑
deepsleep-v3 Jan 24, 2026
650ad3b
修正格式
deepsleep-v3 Jan 24, 2026
7d276f9
修复This launch only逻辑
deepsleep-v3 Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 104 additions & 35 deletions HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -62,49 +70,110 @@ protected Map<String, String> getConfigurations() {
private void generateOptionsTxt() {
if (config().isDisableAutoGameOptions())
return;

Path runDir = repository.getRunDirectory(version.getId());
Path optionsFile = runDir.resolve("options.txt");
Path configFolder = runDir.resolve("config");

if (Files.exists(optionsFile))
return;
if (repository instanceof HMCLGameRepository HMCLRepository) {
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
if (HMCLRepository.getVersionSetting(version.getId()).getGameDirType() == 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");

if (Files.exists(optionsFile))
return;

if (Files.isDirectory(configFolder)) {
try (Stream<Path> stream = Files.walk(configFolder, 2, FileVisitOption.FOLLOW_LINKS)) {
if (stream.anyMatch(file -> "options.txt".equals(FileUtils.getName(file))))
return;
} catch (IOException e) {
LOG.warning("Failed to visit config folder", e);
}
}

if (Files.isDirectory(configFolder)) {
try (Stream<Path> stream = Files.walk(configFolder, 2, FileVisitOption.FOLLOW_LINKS)) {
if (stream.anyMatch(file -> "options.txt".equals(FileUtils.getName(file))))
return;
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
*/
GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(version));
if (gameVersion.compareTo("1.1") < 0)
return;

String lang = normalizedLanguageTag(locale, gameVersion);
if (lang.isEmpty())
return;

if (gameVersion.compareTo("1.11") >= 0)
lang = lang.toLowerCase(Locale.ROOT);

try {
Files.createDirectories(optionsFile.getParent());
Files.writeString(optionsFile, String.format("lang:%s\n", lang));
} catch (IOException e) {
LOG.warning("Failed to visit config folder", e);
LOG.warning("Unable to generate options.txt", e);
}
}
}

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
*/
GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(version));
if (gameVersion.compareTo("1.1") < 0)
return;

String lang = normalizedLanguageTag(locale, gameVersion);
if (lang.isEmpty())
return;

if (gameVersion.compareTo("1.11") >= 0)
lang = lang.toLowerCase(Locale.ROOT);
protected Path switchWorkingDirectory(HMCLGameRepository HMCLRepository, Version version) {
if (Platform.isFxApplicationThread()) {
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
// 在JavaFX线程中直接使用原代码
Alert alert = new Alert(Alert.AlertType.CONFIRMATION,
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
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 {
// 在非JavaFX线程中使用上面的代码
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
CompletableFuture<ButtonBar.ButtonData> 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;
}

try {
Files.createDirectories(optionsFile.getParent());
Files.writeString(optionsFile, String.format("lang:%s\n", lang));
} catch (IOException e) {
LOG.warning("Unable to generate options.txt", e);
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@ public Path getRunDirectory(String id) {
case VERSION_FOLDER:
return getVersionRoot(id);
case ROOT_FOLDER:
return super.getRunDirectory(id);
return getBaseDirectory();
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
case CUSTOM:
try {
return Path.of(getVersionSetting(id).getGameDir());
} catch (InvalidPathException ignored) {
getBaseDirectory();
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
return getVersionRoot(id);
}
default:
Expand Down Expand Up @@ -201,7 +202,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());
}
}
Expand Down Expand Up @@ -231,7 +232,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.
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
*/
@Nullable
public VersionSetting getLocalVersionSetting(String id) {
Expand Down
6 changes: 6 additions & 0 deletions HMCL/src/main/resources/assets/lang/I18N.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated

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\
Expand Down Expand Up @@ -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
Expand All @@ -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 <a href="https://bell-sw.com/pages/downloads/#downloads">here</a>.
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?
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated
# %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".
Comment thread
deepsleep-v3 marked this conversation as resolved.
Outdated

libraries.download=Downloading Libraries

Expand Down