Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15c1042991 | ||
|
|
1275e38995 | ||
|
|
312a2e0cde | ||
|
|
35ce95857a | ||
|
|
6b78f56298 | ||
|
|
b43c8ceae5 | ||
|
|
704d4e9cc0 | ||
|
|
96471e3a92 |
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-12
@@ -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 {
|
||||
|
||||
+3
-7
@@ -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"
|
||||
)
|
||||
|
||||
+1
-1
@@ -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());
|
||||
|
||||
+14
-7
@@ -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
@@ -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
@@ -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) | / | / |
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://kook.top/Kx7n3t)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
English | [中文](README_cn.md)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://kook.top/Kx7n3t)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
Reference in new issue
Block a user