Compare commits

..
Author SHA1 Message Date
Burning_TNT 15c1042991 Fix #3086 修复模组下载界面版本推荐功能 (#3089)
* Fix #3086

* Update
2024-05-31 23:12:33 +08:00
Burning_TNT 1275e38995 Fix #3006: 全局游戏设置中版本隔离开启后的显示的路径的版本号为”null“ (#3028) 2024-05-31 23:10:02 +08:00
Glavo 312a2e0cde 更新 Linux ARM64 支持 (#3097) 2024-05-31 23:06:38 +08:00
Burning_TNT 35ce95857a Fix #3026: resources 目录应该跟随游戏目录 (#3027) 2024-05-31 22:48:19 +08:00
Burning_TNT 6b78f56298 修复自动安装在 1.20.4 的严重问题 (#3023)
* Fix: OptiFine and Forge has compatible problem on 1.20.4 . Fix: Forge changes it's main class on 1.20.4 .

* Fix: HMCL cannot display the analyed versions installed by external launchers.

* Refuse to configure installable widgets when it's from external process.

* Add javadoc about LibraryStatus

* Adapt to the new change of Forge: https://github.com/MinecraftForge/MinecraftForge/commit/b8270cd9ff71678bd71f93938b35e5904b5f507a

* Re-arrange entries.

* Fix.
2024-05-31 22:02:56 +08:00
Glavo b43c8ceae5 移除 KOOK 入口 (#3092)
* 移除 KOOK 入口

* update
2024-05-31 21:41:41 +08:00
Burning_TNT 704d4e9cc0 Fix #3079 修复 NeoForge 整合包版本识别不正确的问题 (#3082)
* Fix #3079

* Fix.

* Fix: checkstyle.
2024-05-28 08:28:35 +08:00
CalbootandGlavo 96471e3a92 Fix #3071: 在 HMCL 被终结时停止日志线程 (#3081)
* update

* update

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2024-05-28 08:17:55 +08:00
32 changed files with 293 additions and 191 deletions

No files matched your search

+4 -4
View File
@@ -8,16 +8,16 @@ body:
attributes:
value: |
提交前请确认:
* 该问题确实是 **HMCL 的错误**,而**不是 Minecraft 非正常退出**,如果你的 Minecraft 非正常退出,请前往 [QQ 群](https://docs.hmcl.net/groups.html)/[KOOK 频道](https://kook.top/Kx7n3t)/[Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
* 该问题确实是 **HMCL 的错误**,而**不是 Minecraft 非正常退出**,如果你的 Minecraft 非正常退出,请前往 [QQ 群](https://docs.hmcl.net/groups.html)/[Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
* 你的启动器版本是**最新的快照版本**,可以点击 [此处](https://github.com/burningtnt/HMCL-Snapshot-Update/raw/master/datas/HMCL-dev.jar) 下载最新快照版本。
如果你的问题并不属于上述两类,你可以选取另一种 Issue 类型,或者直接前往 [QQ 群](https://docs.hmcl.net/groups.html)/[KOOK 频道](https://kook.top/Kx7n3t)/[Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
如果你的问题并不属于上述两类,你可以选取另一种 Issue 类型,或者直接前往 [QQ 群](https://docs.hmcl.net/groups.html)/[Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
Before submitting, please confirm:
* The issue is indeed **an error of HMCL**, not **Minecraft abnormal exit**. If your Minecraft exits abnormally, please go to the [QQ group](https://docs.hmcl.net/groups.html) or [KOOK channel](https://kook.top/Kx7n3t) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
* The issue is indeed **an error of HMCL**, not **Minecraft abnormal exit**. If your Minecraft exits abnormally, please go to the [QQ group](https://docs.hmcl.net/groups.html) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
* Your launcher version is **the latest snapshot version**. You can click [here](https://github.com/burningtnt/HMCL-Snapshot-Update/raw/master/datas/HMCL-dev.jar) to download the latest snapshot version.
If your issue does not fall into the above two categories, you can choose another type of issue or directly go to the [QQ group](https://docs.hmcl.net/groups.html) or [KOOK channel](https://kook.top/Kx7n3t) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
If your issue does not fall into the above two categories, you can choose another type of issue or directly go to the [QQ group](https://docs.hmcl.net/groups.html) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
- type: input
id: platform
attributes:
+3 -6
View File
@@ -1,14 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: KOOK 频道 | KOOK Channel
url: https://kook.top/Kx7n3t
about: Hello Minecraft! Launcher 的官方 KOOK 频道。| The official KOOK channel of Hello Minecraft! Laucher.
- name: Discord 频道 | Discord Channel
url: https://discord.gg/jVvC7HfM6U
about: Hello Minecraft! Launcher 的官方 Discord 频道。| The official Discord channel of Hello Minecraft! Launcher.
- name: QQ 群 | QQ Groups
url: https://docs.hmcl.net/groups.html
about: Hello Minecraft! Launcher 的官方 QQ 交流群。| The official QQ groups of Hello Minecraft! Laucher.
- name: Discord 频道 | Discord Channel
url: https://discord.gg/jVvC7HfM6U
about: Hello Minecraft! Launcher 的官方 Discord 频道。| The official Discord channel of Hello Minecraft! Launcher.
- name: 其他反馈 | Others
url: https://github.com/HMCL-dev/HMCL/discussions/new/choose
about: 通过 Discussions 反馈其他问题。| Report other problems in Discussions.
@@ -270,10 +270,12 @@ public class InstallerItem extends Control {
return i18n("install.installer.incompatible", i18n("install.installer." + incompatibleWith));
} else if (version == null) {
return i18n("install.installer.not_installed");
} else {
} else if (control.id.equals(MINECRAFT.getPatchId()) || control.removable.get() || control.upgradable.get()) {
return i18n("install.installer.version", version);
} else {
return i18n("install.installer.external_version", version);
}
}, control.incompatibleLibraryName, control.incompatibleWithGame, control.libraryVersion));
}, control.incompatibleLibraryName, control.incompatibleWithGame, control.libraryVersion, control.installable, control.removable, control.upgradable));
BorderPane.setMargin(statusLabel, new Insets(0, 0, 0, 8));
BorderPane.setAlignment(statusLabel, Pos.CENTER_LEFT);
@@ -297,8 +299,9 @@ public class InstallerItem extends Control {
control.upgradable));
arrowButton.getStyleClass().add("toggle-icon4");
arrowButton.visibleProperty().bind(Bindings.createBooleanBinding(
() -> control.installable.get() && control.incompatibleLibraryName.get() == null,
control.installable, control.incompatibleLibraryName));
() -> control.installable.get() && control.libraryVersion.get() == null && control.incompatibleLibraryName.get() == null,
control.installable, control.libraryVersion, control.incompatibleLibraryName
));
arrowButton.managedProperty().bind(arrowButton.visibleProperty());
arrowButton.onMouseClickedProperty().bind(control.action);
buttonsContainer.getChildren().add(arrowButton);
@@ -59,13 +59,7 @@ public class FeedbackPage extends SpinnerPane {
discord.setSubtitle(i18n("feedback.discord.statement"));
discord.setExternalLink("https://discord.gg/jVvC7HfM6U");
IconedTwoLineListItem kookapp = new IconedTwoLineListItem();
kookapp.setImage(FXUtils.newBuiltinImage("/assets/img/kookapp.png"));
kookapp.setTitle(i18n("feedback.kookapp"));
kookapp.setSubtitle(i18n("feedback.kookapp.statement"));
kookapp.setExternalLink("https://kook.top/Kx7n3t");
community.getContent().setAll(users, github, discord, kookapp);
community.getContent().setAll(users, github, discord);
}
content.getChildren().addAll(
@@ -94,22 +94,11 @@ public class DownloadPage extends Control implements DecoratorPage {
}
private void loadModVersions() {
File versionJar = StringUtils.isNotBlank(version.getVersion())
? version.getProfile().getRepository().getVersionJar(version.getVersion())
: null;
setLoading(true);
setFailed(false);
Task.supplyAsync(() -> {
Stream<RemoteMod.Version> versions = addon.getData().loadVersions(repository);
// if (StringUtils.isNotBlank(version.getVersion())) {
// Optional<String> gameVersion = GameVersion.minecraftVersion(versionJar);
// if (gameVersion.isPresent()) {
// return sortVersions(
// .filter(file -> file.getGameVersions().contains(gameVersion.get())));
// }
// }
return sortVersions(versions);
}).whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
@@ -286,19 +275,27 @@ public class DownloadPage extends Control implements DecoratorPage {
if (control.version.getProfile() != null && control.version.getVersion() != null) {
HMCLGameRepository repository = control.version.getProfile().getRepository();
Version game = repository.getResolvedPreservingPatchesVersion(control.version.getVersion());
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(game, repository.getGameVersion(game).orElse(null));
libraryAnalyzer.getVersion(LibraryAnalyzer.LibraryType.MINECRAFT).ifPresent(currentGameVersion -> {
Set<ModLoaderType> currentGameModLoaders = libraryAnalyzer.getModLoaders();
if (control.versions.containsKey(currentGameVersion)) {
control.versions.get(currentGameVersion).stream()
.filter(version1 -> version1.getLoaders().isEmpty() || version1.getLoaders().stream().anyMatch(currentGameModLoaders::contains))
.findFirst()
.ifPresent(value -> list.getContent().addAll(
ComponentList.createComponentListTitle(i18n("mods.download.recommend", currentGameVersion)),
new ModItem(value, control)
));
String gameVersion = repository.getGameVersion(game).orElse(null);
if (gameVersion != null) {
List<RemoteMod.Version> modVersions = control.versions.get(gameVersion);
if (modVersions != null && !modVersions.isEmpty()) {
Set<ModLoaderType> targetLoaders = LibraryAnalyzer.analyze(game, gameVersion).getModLoaders();
resolve:
for (RemoteMod.Version modVersion : modVersions) {
for (ModLoaderType loader : modVersion.getLoaders()) {
if (targetLoaders.contains(loader)) {
list.getContent().addAll(
ComponentList.createComponentListTitle(i18n("mods.download.recommend", gameVersion)),
new ModItem(modVersion, control)
);
break resolve;
}
}
}
}
});
}
}
for (String gameVersion : control.versions.keys().stream()
@@ -30,7 +30,6 @@ import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener;
import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -90,13 +89,15 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
for (InstallerItem installerItem : group.getLibraries()) {
String libraryId = installerItem.getLibraryId();
String libraryVersion = analyzer.getVersion(libraryId).orElse(null);
boolean libraryConfigurable = libraryVersion != null && analyzer.getLibraryStatus(libraryId) == LibraryAnalyzer.LibraryMark.LibraryStatus.CLEAR;
installerItem.libraryVersion.set(libraryVersion);
installerItem.upgradable.set(libraryVersion != null);
installerItem.upgradable.set(libraryConfigurable);
installerItem.installable.set(true);
installerItem.action.set(e -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
});
boolean removable = !LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(libraryId) && libraryVersion != null;
boolean removable = !LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(libraryId) && libraryConfigurable;
installerItem.removable.set(removable);
if (removable) {
Runnable action = removeAction.apply(libraryId);
@@ -119,17 +120,10 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
InstallerItem installerItem = new InstallerItem(libraryId);
installerItem.libraryVersion.set(libraryVersion);
installerItem.installable.set(false);
installerItem.upgradable.bind(installerItem.installable);
installerItem.upgradable.set(false);
installerItem.removable.set(true);
installerItem.removeAction.set(e -> action.run());
if (libraryVersion != null && Lang.test(() -> profile.getDependency().getVersionList(libraryId))) {
installerItem.installable.set(true);
installerItem.action.set(e -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
});
}
itemsProperty().add(installerItem);
}
}, Platform::runLater);
@@ -175,7 +169,8 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
@Override
protected List<Node> initializeToolbar(InstallerListPage skinnable) {
return Collections.singletonList(
createToolbarButton2(i18n("install.installer.install_offline"), SVG.PLUS, skinnable::installOffline));
createToolbarButton2(i18n("install.installer.install_offline"), SVG.PLUS, skinnable::installOffline)
);
}
}
}
@@ -33,6 +33,7 @@ import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.EventPriority;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
@@ -164,9 +165,11 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
}
private void clearAssets() {
File baseDirectory = getProfile().getRepository().getBaseDirectory();
FileUtils.deleteDirectoryQuietly(new File(baseDirectory, "assets"));
FileUtils.deleteDirectoryQuietly(new File(baseDirectory, "resources"));
HMCLGameRepository baseDirectory = getProfile().getRepository();
FileUtils.deleteDirectoryQuietly(new File(baseDirectory.getBaseDirectory(), "assets"));
if (version.get() != null) {
FileUtils.deleteDirectoryQuietly(new File(baseDirectory.getRunDirectory(version.get().getVersion()), "resources"));
}
}
private void clearJunkFiles() {
@@ -30,7 +30,9 @@ import javafx.scene.control.ScrollPane;
import javafx.scene.layout.*;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.game.GameDirectoryType;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.ProcessPriority;
import org.jackhuang.hmcl.setting.*;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
@@ -184,7 +186,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
gameDirSublist = new ComponentSublist();
gameDirSublist.getContent().add(gameDirItem);
gameDirSublist.setTitle(i18n("settings.game.working_directory"));
gameDirSublist.setHasSubtitle(true);
gameDirSublist.setHasSubtitle(versionId != null);
gameDirItem.disableProperty().bind(modpack);
gameDirCustomOption = new MultiFileItem.FileOption<>(i18n("settings.custom"), GameDirectoryType.CUSTOM)
.setChooserTitle(i18n("settings.game.working_directory.choose"))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

@@ -398,8 +398,6 @@ feedback.discord=Discord
feedback.discord.statement=Join our Discord community.
feedback.github=GitHub Issue
feedback.github.statement=Creating an issue on GitHub.
feedback.kookapp=KOOK
feedback.kookapp.statement=Join our KOOK community.
feedback.qq_group=HMCL User Group
feedback.qq_group.statement=Join HMCL user QQ group.
@@ -415,7 +413,6 @@ folder.saves=Saves
folder.screenshots=Screenshots
game=Game
#game.crash.feedback=Is it a common issue that almost everyone has had? Please leave feedback so that we can improve the crash analysis system!
game.crash.feedback=<b>Please do not share screenshots of this interface with others! </b> If you ask for help from others, please click to export the game crash information in the lower left corner and send the exported file to others for analysis.
game.crash.info=Crash Info
game.crash.reason=Crash Cause
@@ -640,6 +637,7 @@ install.installer.optifine=OptiFine
install.installer.quilt=Quilt
install.installer.quilt-api=QSL/QFAPI
install.installer.version=%s
install.installer.external_version=%s Installed by external process, which cannot be configured
install.modpack=Install a Modpack
install.new_game=Add a New Instance
install.new_game.already_exists=This instance already exists. Please use another name.
@@ -348,8 +348,6 @@ Por ejemplo, no podrá utilizar authlib-injector o cambiar el skin de su cuenta
feedback=Comentarios
feedback.discord=Discord
feedback.discord.statement=¡Únete a nuestra comunidad de Discord!
feedback.kookapp=KOOK
feedback.kookapp.statement=¡Únete a nuestra comunidad de KaiHeiLa!
file=Archivo
@@ -362,7 +360,6 @@ folder.saves=Guardados
folder.screenshots=Capturas de pantalla
game=Juego
#game.crash.feedback=¿Es un problema común que casi todo el mundo ha tenido? Por favor, deja tus comentarios para que podamos mejorar.
game.crash.feedback=<b>¡Por favor, no comparta capturas de pantalla de esta interfaz con otras personas! </b> Si solicita ayuda a otros, haga clic para exportar la información del bloqueo del juego en la esquina inferior izquierda y envíe el archivo exportado a otros para que lo analicen.
game.crash.info=Información sobre el fallo
game.crash.reason=Causa del fallo
@@ -320,8 +320,6 @@ fatal.illegal_char=ユーザーフォルダーのパスに不正な文字'='が
feedback=フィードバック
feedback.discord=Discord
feedback.discord.statement=チャットに参加しよう!
feedback.kookapp=KOOK
feedback.kookapp.statement=チャットに参加しよう!
file=ファイル
@@ -334,7 +332,6 @@ folder.saves=マップ
folder.screenshots=スクリーンショット
game=ゲーム
#game.crash.feedback=問題は非常に一般的ですが、理由がわかりませんでしたか?フィードバックページでフィードバックをお寄せください。
game.crash.feedback=<b>このインターフェースのスクリーンショットを他の人と共有しないでください。 </b> 他の人に助けを求める場合は、左下隅にあるゲーム クラッシュ情報をクリックしてエクスポートし、エクスポートされたファイルを分析のために他の人に送信してください。
game.crash.info=ゲームステータス
game.crash.reason=クラッシュアナライザー
@@ -871,7 +868,7 @@ update.channel.dev.title=ベータ版のヒント
update.channel.nightly=アルファ
update.channel.nightly.hint=アルファ版を使用しています。これには、ベータ版およびリリース版と比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n\
アルファ版は不安定です!\n\
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>で問題を報告します。
update.channel.nightly.title=アルファ版のヒント
update.channel.stable=リリース
update.checking=更新の確認
@@ -339,8 +339,6 @@ fatal.illegal_char=Недопустимый символ '=' в пути к па
feedback=Обратная связь
feedback.discord=Discord
feedback.discord.statement=Присоединиться!
feedback.kookapp=KOOK
feedback.kookapp.statement=Присоединиться!
file=Файл
@@ -353,7 +351,6 @@ folder.saves=Сохранения
folder.screenshots=Снимки экрана
game=Игра
#game.crash.feedback=Проблема настолько распространена, но мы не выяснили причину? Оставь нам отзыв на странице обратной связи!
game.crash.feedback=<b>Пожалуйста, не делитесь скриншотами этого интерфейса с другими! </b> Если вы попросите помощи у других, нажмите, чтобы экспортировать информацию о сбое игры в левом нижнем углу, и отправьте экспортированный файл другим для анализа.
game.crash.info=Статус игры
game.crash.reason=Анализатор сбоев
@@ -385,8 +385,6 @@ feedback.discord=Discord
feedback.discord.statement=歡迎加入 Discord 討論區,加入後請遵守討論區規定。
feedback.github=GitHub Issue
feedback.github.statement=打開一個 GitHub Issue。
feedback.kookapp=KOOK
feedback.kookapp.statement=歡迎加入 KOOK 討論區,加入後請遵守討論區規定。
feedback.qq_group=HMCL 用戶群
feedback.qq_group.statement=歡迎加入 HMCL 用戶群,加入後請遵守群規。
@@ -402,7 +400,6 @@ folder.saves=遊戲存檔資料夾
folder.screenshots=截圖資料夾
game=遊戲
#game.crash.feedback=查看了日誌發現問題很常見?歡迎在 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>提交回饋,幫助我們豐富崩潰分析系統!
game.crash.feedback=<b>請不要將本界面截圖給他人! </b>如果你要求助他人,請你點擊左下角 導出遊戲崩潰信息 後將導出的文件發送給他人以供分析。\n你可以點擊下方的 幫助 前往社區尋求幫助。
game.crash.info=遊戲訊息
game.crash.reason=崩潰原因
@@ -524,6 +521,7 @@ install.installer.optifine=OptiFine
install.installer.quilt=Quilt
install.installer.quilt-api=QSL/QFAPI
install.installer.version=%s
install.installer.external_version=%s 由外部安裝的版本,無法解除安裝或更換
install.modpack=安裝模組包
install.new_game=安裝新遊戲版本
install.new_game.already_exists=此版本已經存在,請重新命名
@@ -387,8 +387,6 @@ feedback.discord=Discord
feedback.discord.statement=欢迎加入 Discord 讨论区,加入后请遵守讨论区规定。
feedback.github=GitHub Issue
feedback.github.statement=打开一个 GitHub Issue。
feedback.kookapp=KOOK
feedback.kookapp.statement=欢迎加入 KOOK 讨论区,加入后请遵守讨论区规定。
feedback.qq_group=HMCL 用户群
feedback.qq_group.statement=欢迎加入 HMCL 用户群,加入后请遵守群规。
@@ -404,7 +402,6 @@ folder.saves=存档文件夹
folder.screenshots=截图文件夹
game=游戏
#game.crash.feedback=查看了日志发现该问题很常见?欢迎在下方的 “帮助” 中提交反馈,帮助我们丰富崩溃分析系统!
game.crash.feedback=<b>请不要将本界面截图给他人!</b>如果你要求助他人,请你点击左下角<b> 导出游戏崩溃信息 </b>后将导出的文件发送给他人以供分析。\n你可以点击下方的<b> 帮助 </b>前往交流群寻求帮助。
game.crash.info=游戏信息
game.crash.reason=崩溃原因
@@ -523,6 +520,7 @@ install.installer.optifine=OptiFine
install.installer.quilt=Quilt
install.installer.quilt-api=QSL/QFAPI
install.installer.version=%s
install.installer.external_version=%s 由外部安装的版本,无法卸载或更换
install.modpack=安装整合包
install.new_game=安装新游戏版本
install.new_game.already_exists=此版本已经存在,请换一个名字
@@ -539,6 +539,94 @@
}
}
},
"org.lwjgl:lwjgl:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3-natives-linux-arm64.jar",
"sha1": "f35d8b6ffe1ac1e3a5eb1d4e33de80f044ad5fd8",
"size": 91294
}
}
},
"org.lwjgl:lwjgl-jemalloc:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl-jemalloc:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3-natives-linux-arm64.jar",
"sha1": "eff8b86798191192fe2cba2dc2776109f30c239d",
"size": 209315
}
}
},
"org.lwjgl:lwjgl-openal:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl-openal:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3-natives-linux-arm64.jar",
"sha1": "ad8f302118a65bb8d615f8a2a680db58fb8f835e",
"size": 592963
}
}
},
"org.lwjgl:lwjgl-opengl:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl-opengl:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3-natives-linux-arm64.jar",
"sha1": "2096f6b94b2d68745d858fbfe53aacf5f0c8074c",
"size": 58625
}
}
},
"org.lwjgl:lwjgl-glfw:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl-glfw:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3-natives-linux-arm64.jar",
"sha1": "492a0f11f85b85899a6568f07511160c1b87cd38",
"size": 122159
}
}
},
"org.lwjgl:lwjgl-stb:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl-stb:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3-natives-linux-arm64.jar",
"sha1": "ddc177afc2be1ee8d93684b11363b80589a13fe1",
"size": 207418
}
}
},
"org.lwjgl:lwjgl-tinyfd:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl-tinyfd:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3-natives-linux-arm64.jar",
"sha1": "2823a8c955c758d0954d282888075019ef99cec7",
"size": 43864
}
}
},
"org.lwjgl:lwjgl-freetype:3.3.3:natives-linux": {
"name": "org.lwjgl:lwjgl-freetype:3.3.3:natives-linux-arm64",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3-natives-linux-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3-natives-linux-arm64.jar",
"sha1": "498965aac06c4a0d42df1fbef6bacd05bde7f974",
"size": 1093516
}
}
},
"org.lwjgl.lwjgl:lwjgl-platform:2.9.0:natives": {
"name": "org.glavo.hmcl:lwjgl2-natives:2.9.3",
"downloads": {
@@ -19,7 +19,10 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.mod.ModLoaderType;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.jackhuang.hmcl.util.versioning.VersionRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,6 +54,15 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
return Optional.ofNullable(libraries.get(type.getPatchId())).map(Pair::getKey);
}
/**
* If a library is provided in $.patches, it's structure is so clear that we can do any operation.
* Otherwise, we must guess how are these libraries mixed.
* Maybe a guessing implementation will be provided in the future. But by now, we simply set it to JUST_EXISTED.
*/
public LibraryMark.LibraryStatus getLibraryStatus(String type) {
return version.hasPatch(type) ? LibraryMark.LibraryStatus.CLEAR : LibraryMark.LibraryStatus.JUST_EXISTED;
}
@NotNull
@Override
public Iterator<LibraryMark> iterator() {
@@ -65,7 +77,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
@Override
public LibraryMark next() {
Map.Entry<String, Pair<Library, String>> entry = impl.next();
return new LibraryMark(entry.getKey(), entry.getValue().getValue());
return new LibraryMark(entry.getKey(), entry.getValue().getValue(), getLibraryStatus(entry.getKey()));
}
};
}
@@ -85,13 +97,9 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
}
public boolean hasModLauncher() {
final String modLauncher = "cpw.mods.modlauncher.Launcher";
return modLauncher.equals(version.getMainClass()) || version.getPatches().stream().anyMatch(patch -> modLauncher.equals(patch.getMainClass()));
}
public boolean hasBootstrapLauncher() {
final String bootstrapLauncher = "cpw.mods.bootstraplauncher.BootstrapLauncher";
return bootstrapLauncher.equals(version.getMainClass()) || version.getPatches().stream().anyMatch(patch -> bootstrapLauncher.equals(patch.getMainClass()));
return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(version.getMainClass()) || version.getPatches().stream().anyMatch(
patch -> LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(patch.getMainClass())
);
}
private Version removingMatchedLibrary(Version version, String libraryId) {
@@ -184,7 +192,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
private final Pattern FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?<forge>[0-9.]+)(-([0-9.]+))?$");
@Override
public String patchVersion(Version gameVersion, String libraryVersion) {
protected String patchVersion(Version gameVersion, String libraryVersion) {
Matcher matcher = FORGE_VERSION_MATCHER.matcher(libraryVersion);
if (matcher.find()) {
return matcher.group("forge");
@@ -193,7 +201,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
}
@Override
public boolean matchLibrary(Library library, List<Library> libraries) {
protected boolean matchLibrary(Library library, List<Library> libraries) {
for (Library l : libraries) {
if (NEO_FORGE.matchLibrary(l, libraries)) {
return false;
@@ -206,7 +214,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
private final Pattern NEO_FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?<forge>[0-9.]+)(-([0-9.]+))?$");
@Override
public String patchVersion(Version gameVersion, String libraryVersion) {
protected String patchVersion(Version gameVersion, String libraryVersion) {
Matcher matcher = NEO_FORGE_VERSION_MATCHER.matcher(libraryVersion);
if (matcher.find()) {
return matcher.group("forge");
@@ -289,22 +297,37 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
return null;
}
public boolean matchLibrary(Library library, List<Library> libraries) {
protected boolean matchLibrary(Library library, List<Library> libraries) {
return group.matcher(library.getGroupId()).matches() && artifact.matcher(library.getArtifactId()).matches();
}
public String patchVersion(Version gameVersion, String libraryVersion) {
protected String patchVersion(Version gameVersion, String libraryVersion) {
return libraryVersion;
}
}
public static class LibraryMark {
public final static class LibraryMark {
/**
* If a library is provided in $.patches, it's structure is so clear that we can do any operation.
* Otherwise, we must guess how are these libraries mixed.
* Maybe a guessing implementation will be provided in the future. But by now, we simply set it to JUST_EXISTED.
*/
public enum LibraryStatus {
CLEAR, UNSURE, JUST_EXISTED
}
private final String libraryId;
private final String libraryVersion;
/**
* If this version is installed by HMCL, instead of external process,
* which means $.patches contains this library, structureClear is true.
*/
private final LibraryStatus status;
public LibraryMark(@NotNull String libraryId, @Nullable String libraryVersion) {
private LibraryMark(@NotNull String libraryId, @Nullable String libraryVersion, LibraryStatus status) {
this.libraryId = libraryId;
this.libraryVersion = libraryVersion;
this.status = status;
}
@NotNull
@@ -316,12 +339,27 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
public String getLibraryVersion() {
return libraryVersion;
}
public LibraryStatus getStatus() {
return status;
}
}
public static final String VANILLA_MAIN = "net.minecraft.client.main.Main";
public static final String LAUNCH_WRAPPER_MAIN = "net.minecraft.launchwrapper.Launch";
public static final String MOD_LAUNCHER_MAIN = "cpw.mods.modlauncher.Launcher";
public static final String BOOTSTRAP_LAUNCHER_MAIN = "cpw.mods.bootstraplauncher.BootstrapLauncher";
public static final String FORGE_BOOTSTRAP_MAIN = "net.minecraftforge.bootstrap.ForgeBootstrap";
public static final Set<String> FORGE_OPTIFINE_MAIN = new HashSet<>(Lang.immutableListOf(
LibraryAnalyzer.VANILLA_MAIN,
LibraryAnalyzer.LAUNCH_WRAPPER_MAIN,
LibraryAnalyzer.MOD_LAUNCHER_MAIN,
LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN,
LibraryAnalyzer.FORGE_BOOTSTRAP_MAIN
));
public static final VersionRange<VersionNumber> FORGE_OPTIFINE_BROKEN_RANGE = VersionNumber.between("48.0.0", "49.0.50");
public static final String[] FORGE_TWEAKERS = new String[]{
"net.minecraftforge.legacy._1_5_2.LibraryFixerTweaker", // 1.5.2
@@ -76,7 +76,9 @@ public class MaintainTask extends Task<Version> {
}
List<Library> libraries = version.getLibraries();
if (libraries.size() > 0) {
if (!libraries.isEmpty()) {
// HMCL once use log4j-patch to prevent virus. But now, we only modify log4j2.xml.
// Therefore, we remove this library.
Library library = libraries.get(0);
if ("org.glavo".equals(library.getGroupId())
&& ("log4j-patch".equals(library.getArtifactId()) || "log4j-patch-beta9".equals(library.getArtifactId()))
@@ -161,8 +163,8 @@ public class MaintainTask extends Task<Version> {
Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath();
try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) {
Files.createDirectories(libraryPath.getParent());
Files.copy(input, libraryPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
Files.copy(Objects.requireNonNull(input, "Bundled HMCLTransformerDiscoveryService is missing."), libraryPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException | NullPointerException e) {
LOG.warning("Unable to unpack HMCLTransformerDiscoveryService", e);
}
});
@@ -282,13 +284,6 @@ public class MaintainTask extends Task<Version> {
return version.setLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList()));
}
public static boolean isPurePatched(Version version) {
if (!version.isResolvedPreservingPatches())
throw new IllegalArgumentException("isPurePatched requires a version resolved preserving patches");
return version.hasPatch("game");
}
public static Version unique(Version version) {
List<Library> libraries = new ArrayList<>();
@@ -102,18 +102,10 @@ public final class ForgeInstallTask extends Task<Version> {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (GameVersionNumber.compare("1.13", remote.getGameVersion()) <= 0) {
// Forge 1.13 is not compatible with fabric.
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass))
if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass))
throw new UnsupportedInstallationException(UNSUPPORTED_LAUNCH_WRAPPER);
} else {
// Forge 1.12 and older versions is compatible with vanilla and launchwrapper.
// if (!"net.minecraft.client.main.Main".equals(originalMainClass) && !"net.minecraft.launchwrapper.Launch".equals(originalMainClass))
// throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException();
}
if (detectForgeInstallerType(dependencyManager, version, installer))
dependency = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer);
else
@@ -18,15 +18,21 @@
package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -81,20 +87,23 @@ public final class GameLibrariesTask extends Task<Void> {
File file = gameRepository.getLibraryFile(version, library);
Path jar = file.toPath();
if (!file.isFile()) return true;
if (!integrityCheck) {
return false;
}
try {
if (integrityCheck && !library.getDownload().validateChecksum(jar, true)) return true;
if (integrityCheck &&
library.getChecksums() != null && !library.getChecksums().isEmpty() &&
!LibraryDownloadTask.checksumValid(file, library.getChecksums())) return true;
if (integrityCheck) {
String ext = FileUtils.getExtension(file);
if (ext.equals("jar")) {
try {
FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar);
} catch (IOException ignored) {
// the Jar file is malformed, so re-download it.
return true;
}
if (!library.getDownload().validateChecksum(jar, true)) {
return true;
}
if (library.getChecksums() != null && !library.getChecksums().isEmpty() && !LibraryDownloadTask.checksumValid(file, library.getChecksums())) {
return true;
}
if (FileUtils.getExtension(file).equals("jar")) {
try {
FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar);
} catch (IOException ignored) {
// the Jar file is malformed, so re-download it.
return true;
}
}
} catch (IOException e) {
@@ -105,16 +114,32 @@ public final class GameLibrariesTask extends Task<Void> {
}
@Override
public void execute() {
libraries.stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> {
File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
if (shouldDownloadLibrary(dependencyManager.getGameRepository(), version, library, integrityCheck)) {
if (library.hasDownloadURL() || !"optifine".equals(library.getGroupId()))
dependencies.add(new LibraryDownloadTask(dependencyManager, file, library));
public void execute() throws IOException {
GameRepository gameRepository = dependencyManager.getGameRepository();
for (Library library : libraries) {
if (!library.appliesToCurrentEnvironment()) {
continue;
}
File file = gameRepository.getLibraryFile(version, library);
if ("optifine".equals(library.getGroupId()) && file.exists() && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(version)).compareTo("1.20.4") == 0) {
String forgeVersion = LibraryAnalyzer.analyze(version, "1.20.4")
.getVersion(LibraryAnalyzer.LibraryType.FORGE)
.orElse(null);
if (forgeVersion != null && LibraryAnalyzer.FORGE_OPTIFINE_BROKEN_RANGE.contains(VersionNumber.asVersion(forgeVersion))) {
try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(file.toPath())) {
Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml"));
} catch (IOException e) {
throw new IOException("Cannot fix optifine", e);
}
}
}
if (shouldDownloadLibrary(gameRepository, version, library, integrityCheck) && library.hasDownloadURL()) {
dependencies.add(new LibraryDownloadTask(dependencyManager, file, library));
} else {
dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath());
}
});
}
}
}
@@ -22,10 +22,8 @@ import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jackhuang.hmcl.util.io.HttpRequest;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.util.Collections;
import java.util.List;
@@ -62,8 +60,7 @@ public final class NeoForgeBMCLVersionList extends VersionList<NeoForgeRemoteVer
@Override
public Optional<NeoForgeRemoteVersion> getVersion(String gameVersion, String remoteVersion) {
if (gameVersion.equals("1.20.1")) {
remoteVersion = NeoForgeRemoteVersion.fixInvalidVersion(remoteVersion);
remoteVersion = VersionNumber.compare(remoteVersion, "47.1.85") >= 0 ? "1.20.1-" + remoteVersion : remoteVersion;
remoteVersion = NeoForgeRemoteVersion.normalize(remoteVersion);
}
return super.getVersion(gameVersion, remoteVersion);
}
@@ -79,16 +76,10 @@ public final class NeoForgeBMCLVersionList extends VersionList<NeoForgeRemoteVer
try {
versions.clear(gameVersion);
for (NeoForgeVersion neoForgeVersion : neoForgeVersions) {
String nf = StringUtils.removePrefix(
neoForgeVersion.version,
"1.20.1".equals(gameVersion) ? "1.20.1-forge-" : "neoforge-" // Som of the version numbers for 1.20.1 are like forge.
);
versions.put(gameVersion, new NeoForgeRemoteVersion(
neoForgeVersion.mcVersion,
nf,
Collections.singletonList(
apiRoot + "/neoforge/version/" + neoForgeVersion.version + "/download/installer.jar"
)
NeoForgeRemoteVersion.normalize(neoForgeVersion.version),
Collections.singletonList(apiRoot + "/neoforge/version/" + neoForgeVersion.version + "/download/installer.jar")
));
}
} finally {
@@ -2,7 +2,6 @@ package org.jackhuang.hmcl.download.neoforge;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.HttpRequest;
import java.util.Collections;
@@ -31,10 +30,7 @@ public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemot
@Override
public Optional<NeoForgeRemoteVersion> getVersion(String gameVersion, String remoteVersion) {
if (gameVersion.equals("1.20.1")) {
remoteVersion = NeoForgeRemoteVersion.fixInvalidVersion(remoteVersion);
if (!remoteVersion.equals("47.1.82")) {
remoteVersion = "1.20.1-" + remoteVersion;
}
remoteVersion = NeoForgeRemoteVersion.normalize(remoteVersion);
}
return super.getVersion(gameVersion, remoteVersion);
}
@@ -52,7 +48,7 @@ public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemot
for (String version : results[0].versions) {
versions.put("1.20.1", new NeoForgeRemoteVersion(
"1.20.1", StringUtils.removePrefix(version, "1.20.1-"),
"1.20.1", NeoForgeRemoteVersion.normalize(version),
Collections.singletonList(
"https://maven.neoforged.net/releases/net/neoforged/forge/" + version + "/forge-" + version + "-installer.jar"
)
@@ -62,7 +58,7 @@ public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemot
for (String version : results[1].versions) {
String mcVersion = "1." + version.substring(0, version.indexOf('.', version.indexOf('.') + 1));
versions.put(mcVersion, new NeoForgeRemoteVersion(
mcVersion, version,
mcVersion, NeoForgeRemoteVersion.normalize(version),
Collections.singletonList(
"https://maven.neoforged.net/releases/net/neoforged/neoforge/" + version + "/neoforge-" + version + "-installer.jar"
)
@@ -18,7 +18,7 @@ public class NeoForgeRemoteVersion extends RemoteVersion {
return new NeoForgeInstallTask(dependencyManager, baseVersion, this);
}
public static String fixInvalidVersion(String version) {
public static String normalize(String version) {
if (version.startsWith("1.20.1-")) {
if (version.startsWith("forge-", "1.20.1-".length())) {
return version.substring("1.20.1-forge-".length());
@@ -125,20 +125,23 @@ public final class OptiFineInstallTask extends Task<Version> {
@Override
public void execute() throws Exception {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) &&
!LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) &&
!LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) &&
!LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass))
if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass))
throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER);
List<Library> libraries = new ArrayList<>(4);
libraries.add(optiFineLibrary);
FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath());
Path optiFineInstallerLibraryPath = gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath();
FileUtils.copyFile(dest, optiFineInstallerLibraryPath);
try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(optiFineInstallerLibraryPath)) {
Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml"));
}
// Install launch wrapper modified by OptiFine
boolean hasLaunchWrapper = false;
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) {
Path optiFineLibraryPath = gameRepository.getLibraryFile(version, optiFineLibrary).toPath();
if (Files.exists(fs.getPath("optifine/Patcher.class"))) {
String[] command = {
JavaVersion.fromCurrentEnvironment().getBinary().toString(),
@@ -147,13 +150,17 @@ public final class OptiFineInstallTask extends Task<Version> {
"optifine.Patcher",
gameRepository.getVersionJar(version).getAbsolutePath(),
dest.toString(),
gameRepository.getLibraryFile(version, optiFineLibrary).toString()
optiFineLibraryPath.toString()
};
int exitCode = SystemUtils.callExternalProcess(command);
if (exitCode != 0)
throw new IOException("OptiFine patcher failed, command: " + new CommandBuilder().addAll(Arrays.asList(command)));
} else {
FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineLibrary).toPath());
FileUtils.copyFile(dest, optiFineLibraryPath);
}
try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(optiFineLibraryPath)) {
Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml"));
}
Path launchWrapper2 = fs.getPath("launchwrapper-2.0.jar");
@@ -21,14 +21,7 @@ import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.MaintainTask;
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
import org.jackhuang.hmcl.event.Event;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.GameJsonParseFailedEvent;
import org.jackhuang.hmcl.event.LoadedOneVersionEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.event.RemoveVersionEvent;
import org.jackhuang.hmcl.event.RenameVersionEvent;
import org.jackhuang.hmcl.event.*;
import org.jackhuang.hmcl.game.tlauncher.TLauncherVersion;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.mod.ModpackConfiguration;
@@ -45,13 +38,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
@@ -126,9 +113,12 @@ public class DefaultGameRepository implements GameRepository {
@Override
public File getRunDirectory(String id) {
switch (getGameDirectoryType(id)) {
case VERSION_FOLDER: return getVersionRoot(id);
case ROOT_FOLDER: return getBaseDirectory();
default: throw new IllegalStateException();
case VERSION_FOLDER:
return getVersionRoot(id);
case ROOT_FOLDER:
return getBaseDirectory();
default:
throw new IllegalStateException();
}
}
@@ -458,7 +448,7 @@ public class DefaultGameRepository implements GameRepository {
return assetsDir;
if (index.isVirtual()) {
Path resourcesDir = getBaseDirectory().toPath().resolve("resources");
Path resourcesDir = getRunDirectory(version).toPath().resolve("resources");
int cnt = 0;
int tot = index.getObjects().entrySet().size();
@@ -507,18 +497,20 @@ public class DefaultGameRepository implements GameRepository {
/**
* read modpack configuration for a version.
*
* @param version version installed as modpack
* @param <M> manifest type of ModpackConfiguration
* @param <M> manifest type of ModpackConfiguration
* @return modpack configuration object, or null if this version is not a modpack.
* @throws VersionNotFoundException if version does not exist.
* @throws IOException if an i/o error occurs.
* @throws IOException if an i/o error occurs.
*/
@Nullable
public <M> ModpackConfiguration<M> readModpackConfiguration(String version) throws IOException, VersionNotFoundException {
if (!hasVersion(version)) throw new VersionNotFoundException(version);
File file = getModpackConfiguration(version);
if (!file.exists()) return null;
return JsonUtils.GSON.fromJson(FileUtils.readText(file), new TypeToken<ModpackConfiguration<M>>(){}.getType());
return JsonUtils.GSON.fromJson(FileUtils.readText(file), new TypeToken<ModpackConfiguration<M>>() {
}.getType());
}
public boolean isModpack(String version) {
@@ -295,7 +295,10 @@ public class Version implements Comparable<Version>, Validation {
}
}
if (patches != null && !patches.isEmpty()) {
if (patches == null) {
// This is a version from external launcher.
thisVersion = thisVersion.merge(this, true);
} else if (!patches.isEmpty()) {
// Assume patches themselves do not have patches recursively.
List<Version> sortedPatches = patches.stream()
.sorted(Comparator.comparing(Version::getPriority))
@@ -111,13 +111,14 @@ public final class Logger {
}
}
private void onShutdown() {
private void onExit() {
shutdown();
try {
loggerThread.join();
} catch (InterruptedException ignored) {
}
String caller = CLASS_NAME + ".onShutdown";
String caller = CLASS_NAME + ".onExit";
if (logRetention > 0 && logFile != null) {
List<Pair<Path, int[]>> list = new ArrayList<>();
@@ -258,7 +259,7 @@ public final class Logger {
loggerThread.setName("HMCL Logger Thread");
loggerThread.start();
Thread cleanerThread = new Thread(this::onShutdown);
Thread cleanerThread = new Thread(this::onExit);
cleanerThread.setName("HMCL Logger Shutdown Hook");
Runtime.getRuntime().addShutdownHook(cleanerThread);
}
+1 -1
View File
@@ -6,7 +6,7 @@ English | [简体中文](PLATFORM_cn.md)
|----------------------------|:--------------------------------------------------|:---------------------------|:------------------------------------------------------------------------|:---------------------------|
| x86-64 | ✅️ | ✅️ | ✅️ | 👌 (Minecraft 1.13~1.20.4) |
| x86 | ✅️ | ✅️ | / | / |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.20.4) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (use Rosetta 2) | ❔ |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.20.6) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (use Rosetta 2) | ❔ |
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.20.4) | / | / |
+1 -1
View File
@@ -6,7 +6,7 @@
|------------------|:---------------------------------------------------|:---------------------------|:-----------------------------------------------------------------------|:--------------------------|
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.20.4) |
| x86 | ✅️ | ✅️ | / | / |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.20.4) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.20.6) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
| ARM32 | /️ | 👌 (Minecraft 1.6~1.20.1) | / | / |
| MIPS64el | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.20.4) | / | / |
-1
View File
@@ -4,7 +4,6 @@
![Downloads](https://img.shields.io/github/downloads/huanghongxun/HMCL/total)
![Stars](https://img.shields.io/github/stars/huanghongxun/HMCL)
[![Discord](https://img.shields.io/discord/995291757799538688.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/jVvC7HfM6U)
[![KOOK](https://img.shields.io/badge/KOOK-HMCL-brightgreen)](https://kook.top/Kx7n3t)
[![QQ Group](https://img.shields.io/badge/QQ-HMCL-brightgreen)](https://docs.hmcl.net/groups.html)
English | [中文](README_cn.md)
-1
View File
@@ -4,7 +4,6 @@
![Downloads](https://img.shields.io/github/downloads/huanghongxun/HMCL/total)
![Stars](https://img.shields.io/github/stars/huanghongxun/HMCL)
[![Discord](https://img.shields.io/discord/995291757799538688.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/jVvC7HfM6U)
[![KOOK](https://img.shields.io/badge/KOOK-HMCL-brightgreen)](https://kook.top/Kx7n3t)
[![QQ Group](https://img.shields.io/badge/QQ-HMCL-brightgreen)](https://docs.hmcl.net/groups.html)
[English](README.md) | 中文