Merge e1e9cf930be0d47ac48f6fbc9dcd897ab2e86ef5 into 9969dc60c5278340b6b9a4d7facdde620e99d1f5

This commit is contained in:
Zkitefly 2025-08-02 23:00:10 +08:00 committed by GitHub
commit 46ecd12eb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 339 additions and 13 deletions

View File

@ -25,6 +25,7 @@ import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask;
import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration; import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration;
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask;
import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; import org.jackhuang.hmcl.mod.server.ServerModpackExportTask;
import org.jackhuang.hmcl.mod.modrinth.ModrinthModpackExportTask;
import org.jackhuang.hmcl.setting.Config; import org.jackhuang.hmcl.setting.Config;
import org.jackhuang.hmcl.setting.FontManager; import org.jackhuang.hmcl.setting.FontManager;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
@ -100,6 +101,9 @@ public final class ExportWizardProvider implements WizardProvider {
case ModpackTypeSelectionPage.MODPACK_TYPE_SERVER: case ModpackTypeSelectionPage.MODPACK_TYPE_SERVER:
exportTask = exportAsServer(exportInfo, dest); exportTask = exportAsServer(exportInfo, dest);
break; break;
case ModpackTypeSelectionPage.MODPACK_TYPE_MODRINTH:
exportTask = exportAsModrinth(exportInfo, dest);
break;
default: default:
throw new IllegalStateException("Unrecognized modpack type " + modpackType); throw new IllegalStateException("Unrecognized modpack type " + modpackType);
} }
@ -233,6 +237,27 @@ public final class ExportWizardProvider implements WizardProvider {
}; };
} }
private Task<?> exportAsModrinth(ModpackExportInfo exportInfo, File modpackFile) {
return new Task<Void>() {
Task<?> dependency;
@Override
public void execute() {
dependency = new ModrinthModpackExportTask(
profile.getRepository(),
version,
exportInfo,
modpackFile
);
}
@Override
public Collection<Task<?>> getDependencies() {
return Collections.singleton(dependency);
}
};
}
@Override @Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) { public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
switch (step) { switch (step) {

View File

@ -53,6 +53,7 @@ import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory; import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory;
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE; import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE;
import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_MODRINTH;
import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_SERVER; import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.MODPACK_TYPE_SERVER;
import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES; import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES;
import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.Lang.tryCast;
@ -80,6 +81,8 @@ public final class ModpackInfoPage extends Control implements WizardPage {
private final SimpleStringProperty launchArguments = new SimpleStringProperty(""); private final SimpleStringProperty launchArguments = new SimpleStringProperty("");
private final SimpleStringProperty javaArguments = new SimpleStringProperty(""); private final SimpleStringProperty javaArguments = new SimpleStringProperty("");
private final SimpleStringProperty mcbbsThreadId = new SimpleStringProperty(""); private final SimpleStringProperty mcbbsThreadId = new SimpleStringProperty("");
private final SimpleBooleanProperty noCreateRemoteFiles = new SimpleBooleanProperty();
private final SimpleBooleanProperty skipCurseForgeRemoteFiles = new SimpleBooleanProperty();
public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) { public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) {
this.controller = controller; this.controller = controller;
@ -102,7 +105,13 @@ public final class ModpackInfoPage extends Control implements WizardPage {
private void onNext() { private void onNext() {
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(i18n("modpack.wizard.step.initialization.save")); fileChooser.setTitle(i18n("modpack.wizard.step.initialization.save"));
if (controller.getSettings().get(MODPACK_TYPE) == ModpackTypeSelectionPage.MODPACK_TYPE_MODRINTH) {
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.mrpack"));
fileChooser.setInitialFileName(name.get() + ".mrpack");
} else {
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip"));
fileChooser.setInitialFileName(name.get() + ".zip");
}
File file = fileChooser.showSaveDialog(Controllers.getStage()); File file = fileChooser.showSaveDialog(Controllers.getStage());
if (file == null) { if (file == null) {
controller.onEnd(); controller.onEnd();
@ -122,6 +131,8 @@ public final class ModpackInfoPage extends Control implements WizardPage {
exportInfo.setLaunchArguments(launchArguments.get()); exportInfo.setLaunchArguments(launchArguments.get());
exportInfo.setJavaArguments(javaArguments.get()); exportInfo.setJavaArguments(javaArguments.get());
exportInfo.setAuthlibInjectorServer(authlibInjectorServer.get()); exportInfo.setAuthlibInjectorServer(authlibInjectorServer.get());
exportInfo.setNoCreateRemoteFiles(noCreateRemoteFiles.get());
exportInfo.setSkipCurseForgeRemoteFiles(skipCurseForgeRemoteFiles.get());
if (StringUtils.isNotBlank(mcbbsThreadId.get())) { if (StringUtils.isNotBlank(mcbbsThreadId.get())) {
exportInfo.setOrigins(Collections.singletonList(new McbbsModpackManifest.Origin( exportInfo.setOrigins(Collections.singletonList(new McbbsModpackManifest.Origin(
@ -178,6 +189,10 @@ public final class ModpackInfoPage extends Control implements WizardPage {
Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server")); Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server"));
hyperlink.setOnAction(e -> FXUtils.openLink(Metadata.DOCS_URL + "/modpack/serverpack.html")); hyperlink.setOnAction(e -> FXUtils.openLink(Metadata.DOCS_URL + "/modpack/serverpack.html"));
borderPane.setTop(hyperlink); borderPane.setTop(hyperlink);
} if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_MODRINTH) {
HintPane pane = new HintPane(MessageDialogPane.MessageType.INFO);
pane.setText(i18n("modpack.wizard.step.initialization.modrinth.info"));
borderPane.setTop(pane);
} else { } else {
HintPane pane = new HintPane(MessageDialogPane.MessageType.INFO); HintPane pane = new HintPane(MessageDialogPane.MessageType.INFO);
pane.setText(i18n("modpack.wizard.step.initialization.warning")); pane.setText(i18n("modpack.wizard.step.initialization.warning"));
@ -366,6 +381,32 @@ public final class ModpackInfoPage extends Control implements WizardPage {
button.setMaxHeight(16); button.setMaxHeight(16);
pane.setRight(button); pane.setRight(button);
} }
if (skinnable.options.isRequireNoCreateRemoteFiles()) {
BorderPane noCreateRemoteFiles = new BorderPane();
noCreateRemoteFiles.setLeft(new Label(i18n("modpack.wizard.step.initialization.no_create_remote_files")));
list.getContent().add(noCreateRemoteFiles);
JFXToggleButton noCreateRemoteFilesButton = new JFXToggleButton();
noCreateRemoteFilesButton.selectedProperty().bindBidirectional(skinnable.noCreateRemoteFiles);
noCreateRemoteFilesButton.setSize(8);
noCreateRemoteFilesButton.setMinHeight(16);
noCreateRemoteFilesButton.setMaxHeight(16);
noCreateRemoteFiles.setRight(noCreateRemoteFilesButton);
}
if (skinnable.options.isRequireSkipCurseForgeRemoteFiles()) {
BorderPane skipCurseForgeRemoteFiles = new BorderPane();
skipCurseForgeRemoteFiles.setLeft(new Label(i18n("modpack.wizard.step.initialization.skip_curseforge_remote_files")));
list.getContent().add(skipCurseForgeRemoteFiles);
JFXToggleButton skipCurseForgeRemoteFilesButton = new JFXToggleButton();
skipCurseForgeRemoteFilesButton.selectedProperty().bindBidirectional(skinnable.skipCurseForgeRemoteFiles);
skipCurseForgeRemoteFilesButton.setSize(8);
skipCurseForgeRemoteFilesButton.setMinHeight(16);
skipCurseForgeRemoteFilesButton.setMaxHeight(16);
skipCurseForgeRemoteFiles.setRight(skipCurseForgeRemoteFilesButton);
}
} }
{ {

View File

@ -28,6 +28,7 @@ import org.jackhuang.hmcl.mod.ModpackExportInfo;
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask; import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask;
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask;
import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; import org.jackhuang.hmcl.mod.server.ServerModpackExportTask;
import org.jackhuang.hmcl.mod.modrinth.ModrinthModpackExportTask;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
@ -56,7 +57,8 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
title, title,
createButton(MODPACK_TYPE_MCBBS, McbbsModpackExportTask.OPTION), createButton(MODPACK_TYPE_MCBBS, McbbsModpackExportTask.OPTION),
createButton(MODPACK_TYPE_MULTIMC, MultiMCModpackExportTask.OPTION), createButton(MODPACK_TYPE_MULTIMC, MultiMCModpackExportTask.OPTION),
createButton(MODPACK_TYPE_SERVER, ServerModpackExportTask.OPTION) createButton(MODPACK_TYPE_SERVER, ServerModpackExportTask.OPTION),
createButton(MODPACK_TYPE_MODRINTH, ModrinthModpackExportTask.OPTION)
); );
} }
@ -100,4 +102,5 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
public static final String MODPACK_TYPE_MCBBS = "mcbbs"; public static final String MODPACK_TYPE_MCBBS = "mcbbs";
public static final String MODPACK_TYPE_MULTIMC = "multimc"; public static final String MODPACK_TYPE_MULTIMC = "multimc";
public static final String MODPACK_TYPE_SERVER = "server"; public static final String MODPACK_TYPE_SERVER = "server";
public static final String MODPACK_TYPE_MODRINTH = "modrinth";
} }

View File

@ -904,6 +904,7 @@ modpack.type.manual.warning=The modpack is manually packaged by the publisher, w
modpack.type.mcbbs=MCBBS modpack.type.mcbbs=MCBBS
modpack.type.mcbbs.export=Can be imported by Hello Minecraft! Launcher modpack.type.mcbbs.export=Can be imported by Hello Minecraft! Launcher
modpack.type.modrinth=Modrinth modpack.type.modrinth=Modrinth
modpack.type.modrinth.export=Can be imported by popular third-party launchers
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.multimc.export=Can be imported by Hello Minecraft! Launcher and MultiMC modpack.type.multimc.export=Can be imported by Hello Minecraft! Launcher and MultiMC
modpack.type.server=Auto-Update Modpack from Server modpack.type.server=Auto-Update Modpack from Server
@ -921,7 +922,10 @@ modpack.wizard.step.3.title=Choose modpack type you wanted to export as.
modpack.wizard.step.initialization.exported_version=Game Instance to Export modpack.wizard.step.initialization.exported_version=Game Instance to Export
modpack.wizard.step.initialization.force_update=Force updating the modpack to the latest version (you will need a file-hosting server) modpack.wizard.step.initialization.force_update=Force updating the modpack to the latest version (you will need a file-hosting server)
modpack.wizard.step.initialization.include_launcher=Include the launcher modpack.wizard.step.initialization.include_launcher=Include the launcher
modpack.wizard.step.initialization.modrinth.info=The launcher will match CurseForge/Modrinth remote resources instead of local files (including mods, resource packs, and shader packs) during modpack creation to reduce the modpack size, and mark files with ".disabled" extension as optional for installation.
modpack.wizard.step.initialization.no_create_remote_files=Do not match remote files
modpack.wizard.step.initialization.save=Export to... modpack.wizard.step.initialization.save=Export to...
modpack.wizard.step.initialization.skip_curseforge_remote_files=Do not match CurseForge remote resources
modpack.wizard.step.initialization.warning=Before making a modpack, please make sure the game can be launched normally and Minecraft is a release version instead of a snapshot. The launcher will save your download settings.\n\ modpack.wizard.step.initialization.warning=Before making a modpack, please make sure the game can be launched normally and Minecraft is a release version instead of a snapshot. The launcher will save your download settings.\n\
\n\ \n\
Keep in mind that you are not allowed to add mods and resource packs that explicitly state they could not to be distributed or put in a modpack. Keep in mind that you are not allowed to add mods and resource packs that explicitly state they could not to be distributed or put in a modpack.

View File

@ -909,6 +909,7 @@ modpack.type.manual.warning=Este archivo contiene una copia completa de una inst
modpack.type.mcbbs=Tipo MCBBS modpack.type.mcbbs=Tipo MCBBS
modpack.type.mcbbs.export=Puede ser importado por Hello Minecraft! Launcher modpack.type.mcbbs.export=Puede ser importado por Hello Minecraft! Launcher
modpack.type.modrinth=Modrinth modpack.type.modrinth=Modrinth
modpack.type.modrinth.export=Puede ser importado por los principales lanzadores de terceros
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.multimc.export=Puede ser importado por Hello Minecraft! Launcher y MultiMC modpack.type.multimc.export=Puede ser importado por Hello Minecraft! Launcher y MultiMC
modpack.type.server=Actualización automática del modpack desde el servidor modpack.type.server=Actualización automática del modpack desde el servidor
@ -926,7 +927,10 @@ modpack.wizard.step.3.title=Elija el tipo de modpack que desea exportar.
modpack.wizard.step.initialization.exported_version=Instancia del juego a exportar modpack.wizard.step.initialization.exported_version=Instancia del juego a exportar
modpack.wizard.step.initialization.force_update=Forzar la actualización del modpack a la última versión (necesitarás un servicio de alojamiento de archivos) modpack.wizard.step.initialization.force_update=Forzar la actualización del modpack a la última versión (necesitarás un servicio de alojamiento de archivos)
modpack.wizard.step.initialization.include_launcher=Incluir el launcher modpack.wizard.step.initialization.include_launcher=Incluir el launcher
modpack.wizard.step.initialization.modrinth.info=El lanzador comparará los recursos remotos de CurseForge/Modrinth en lugar de los archivos locales (incluidos mods, paquetes de recursos y paquetes de shaders) durante la creación del modpack para reducir su tamaño, y marcará los archivos con extensión «.disabled» como opcionales para la instalación.
modpack.wizard.step.initialization.no_create_remote_files=No utilice recursos remotos para sustituir archivos locales
modpack.wizard.step.initialization.save=Exportar a... modpack.wizard.step.initialization.save=Exportar a...
modpack.wizard.step.initialization.skip_curseforge_remote_files=No utilice los recursos remotos de CurseForge para sustituir archivos locales
modpack.wizard.step.initialization.warning=Antes de hacer un modpack, por favor asegúrate de que el juego puede ser lanzado normalmente y Minecraft es una versión de lanzamiento en lugar de una snapshot. El launcher guardará tu configuración de descarga.\n\ modpack.wizard.step.initialization.warning=Antes de hacer un modpack, por favor asegúrate de que el juego puede ser lanzado normalmente y Minecraft es una versión de lanzamiento en lugar de una snapshot. El launcher guardará tu configuración de descarga.\n\
\n\ \n\
Ten en cuenta que no se te permite añadir mods y paquetes de recursos que se digan explícitamente que no se pueden distribuir o poner en un modpack. Ten en cuenta que no se te permite añadir mods y paquetes de recursos que se digan explícitamente que no se pueden distribuir o poner en un modpack.

View File

@ -624,6 +624,8 @@ modpack.type.curse.not_found=必要なリソースの一部が欠落している
modpack.type.manual.warning=このmodpackをインポートする代わりに、おそらくこのmodpackファイルを直接解凍する必要があります。そして、バンドルされたランチャーを使用してゲームを起動します。このmodpackは、ランチャーによってエクスポートされるのではなく、.minecraftディレクトリを圧縮することによって手動で作成されます。HMCLはこのmodpackのインポートを試みることができます、続行しますか modpack.type.manual.warning=このmodpackをインポートする代わりに、おそらくこのmodpackファイルを直接解凍する必要があります。そして、バンドルされたランチャーを使用してゲームを起動します。このmodpackは、ランチャーによってエクスポートされるのではなく、.minecraftディレクトリを圧縮することによって手動で作成されます。HMCLはこのmodpackのインポートを試みることができます、続行しますか
modpack.type.mcbbs=MCBBS標準 modpack.type.mcbbs=MCBBS標準
modpack.type.mcbbs.export=Hello Minecraftでインポートできますランチャー modpack.type.mcbbs.export=Hello Minecraftでインポートできますランチャー
modpack.type.modrinth=Modrinth
modpack.type.modrinth.export=メインストリームのサードパーティ製イニシエータでインポート可能
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.multimc.export=Hello MinecraftでインポートできますランチャーとMultiMC modpack.type.multimc.export=Hello MinecraftでインポートできますランチャーとMultiMC
modpack.type.server=サーバー自動更新Modpack modpack.type.server=サーバー自動更新Modpack
@ -641,7 +643,10 @@ modpack.wizard.step.3.title=modpackの形式を選択します。
modpack.wizard.step.initialization.exported_version=エクスポートされたゲームバージョン modpack.wizard.step.initialization.exported_version=エクスポートされたゲームバージョン
modpack.wizard.step.initialization.force_update=可能であればmodpackを強制的に更新します modpack.wizard.step.initialization.force_update=可能であればmodpackを強制的に更新します
modpack.wizard.step.initialization.include_launcher=ランチャーを含める modpack.wizard.step.initialization.include_launcher=ランチャーを含める
modpack.wizard.step.initialization.modrinth.info=モッドパック作成プロセスにおいて、CurseForge/Modrinth のリモートリソースと一致するローカルファイルを置換して容量を削減し、.disabledサフィックス付きファイルをインストール時オプションとしてマーク
modpack.wizard.step.initialization.no_create_remote_files=リモートファイルとの一致を行わない
modpack.wizard.step.initialization.save=エクスポート先... modpack.wizard.step.initialization.save=エクスポート先...
modpack.wizard.step.initialization.skip_curseforge_remote_files=CurseForge リモートリソースのマッチングをスキップ
modpack.wizard.step.initialization.warning=modpackを作成する前に、ゲームが正常に起動できること、および\nMinecraftがリリースバージョンであることを確認する必要があります。\n再配布できないmodを追加しないでください。 modpack.wizard.step.initialization.warning=modpackを作成する前に、ゲームが正常に起動できること、および\nMinecraftがリリースバージョンであることを確認する必要があります。\n再配布できないmodを追加しないでください。
modpack.wizard.step.initialization.server=サーバーの自動更新modpackの詳細については、ここをクリックしてください modpack.wizard.step.initialization.server=サーバーの自動更新modpackの詳細については、ここをクリックしてください

View File

@ -908,6 +908,7 @@ modpack.type.manual.warning=Возможно, вам нужно напрямую
modpack.type.mcbbs=MCBBS modpack.type.mcbbs=MCBBS
modpack.type.mcbbs.export=Может быть импортирован Hello Minecraft! Launcher modpack.type.mcbbs.export=Может быть импортирован Hello Minecraft! Launcher
modpack.type.modrinth=Modrinth modpack.type.modrinth=Modrinth
modpack.type.modrinth.export=Может быть импортирован популярными сторонними лаунчеры
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.multimc.export=Может быть импортирован Hello Minecraft! Launcher и MultiMC modpack.type.multimc.export=Может быть импортирован Hello Minecraft! Launcher и MultiMC
modpack.type.server=Сервер автообновления модпака modpack.type.server=Сервер автообновления модпака
@ -925,7 +926,10 @@ modpack.wizard.step.3.title=Выберите тип модпака, которы
modpack.wizard.step.initialization.exported_version=Сборка игры для экспорта modpack.wizard.step.initialization.exported_version=Сборка игры для экспорта
modpack.wizard.step.initialization.force_update=Принудительное обновление модпака до последней версии (вам понадобится служба хостинга файлов) modpack.wizard.step.initialization.force_update=Принудительное обновление модпака до последней версии (вам понадобится служба хостинга файлов)
modpack.wizard.step.initialization.include_launcher=Включать лаунчер modpack.wizard.step.initialization.include_launcher=Включать лаунчер
modpack.wizard.step.initialization.modrinth.info=Лаунчер будет использовать удаленные ресурсы CurseForge/Modrinth вместо локальных файлов (включая моды, пакеты ресурсов и пакеты шейдеров) при создании модпака, чтобы уменьшить размер модпака, и помечать файлы с расширением «.disabled» как необязательные для установки.
modpack.wizard.step.initialization.no_create_remote_files=Не используйте удаленные файлы
modpack.wizard.step.initialization.save=Экспортировать в... modpack.wizard.step.initialization.save=Экспортировать в...
modpack.wizard.step.initialization.skip_curseforge_remote_files=Не используйте удаленные файлы CurseForge
modpack.wizard.step.initialization.warning=Перед созданием модпака убедитесь, что игра запускается нормально и Minecraft является релизной версией, а не снапшотом. Пусковая установка сохранит ваши настройки скачивания.\n\ modpack.wizard.step.initialization.warning=Перед созданием модпака убедитесь, что игра запускается нормально и Minecraft является релизной версией, а не снапшотом. Пусковая установка сохранит ваши настройки скачивания.\n\
\n\ \n\
Помните, что вам не разрешается добавлять моды и пакет ресурсов, в которых явно указано, что они не подлежат распространению или помещению в модпак. Помните, что вам не разрешается добавлять моды и пакет ресурсов, в которых явно указано, что они не подлежат распространению или помещению в модпак.

View File

@ -720,6 +720,7 @@ modpack.type.manual.warning=該模組包由發佈者手動打包,其中可能
modpack.type.mcbbs=MCBBS 模組包 modpack.type.mcbbs=MCBBS 模組包
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 匯入 modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 匯入
modpack.type.modrinth=Modrinth modpack.type.modrinth=Modrinth
modpack.type.modrinth.export=可以被主流第三方啟動器匯入
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher 和 MultiMC 匯入 modpack.type.multimc.export=可以被 Hello Minecraft! Launcher 和 MultiMC 匯入
modpack.type.server=伺服器自動更新模組包 modpack.type.server=伺服器自動更新模組包
@ -737,7 +738,10 @@ modpack.wizard.step.3.title=選取模組包匯出類型
modpack.wizard.step.initialization.exported_version=要匯出的遊戲實例 modpack.wizard.step.initialization.exported_version=要匯出的遊戲實例
modpack.wizard.step.initialization.force_update=強制升級模組包至最新版本 (需要自建伺服器) modpack.wizard.step.initialization.force_update=強制升級模組包至最新版本 (需要自建伺服器)
modpack.wizard.step.initialization.include_launcher=包含啟動器 modpack.wizard.step.initialization.include_launcher=包含啟動器
modpack.wizard.step.initialization.modrinth.info=在模組包建立過程中,啟動器將匹配 CurseForge/Modrinth 遠端資源替代本機檔案(包括模組、資源包和光影包)以縮減模組包大小,並將副檔名為「.disabled」的檔案標註為「安裝時可選項」。
modpack.wizard.step.initialization.no_create_remote_files=不匹配遠端檔案
modpack.wizard.step.initialization.save=選取要匯出到的遊戲模組包位置 modpack.wizard.step.initialization.save=選取要匯出到的遊戲模組包位置
modpack.wizard.step.initialization.skip_curseforge_remote_files=不匹配 CurseForge 遠端資源
modpack.wizard.step.initialization.warning=在製作模組包前,請你確認你選取的實例可以正常啟動,\n並保證你的 Minecraft 是正式版而非快照,\n而且不應將不允許非官方途徑傳播的模組、資源 (紋理) 包等納入模組包。\n模組包會儲存你目前的下載來源設定。 modpack.wizard.step.initialization.warning=在製作模組包前,請你確認你選取的實例可以正常啟動,\n並保證你的 Minecraft 是正式版而非快照,\n而且不應將不允許非官方途徑傳播的模組、資源 (紋理) 包等納入模組包。\n模組包會儲存你目前的下載來源設定。
modpack.wizard.step.initialization.server=點選此處查看有關伺服器自動更新模組包的製作教學 modpack.wizard.step.initialization.server=點選此處查看有關伺服器自動更新模組包的製作教學

View File

@ -730,6 +730,7 @@ modpack.type.manual.warning=该整合包由发布者手动打包,其中可能
modpack.type.mcbbs=MCBBS 整合包 (推荐) modpack.type.mcbbs=MCBBS 整合包 (推荐)
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 导入 modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 导入
modpack.type.modrinth=Modrinth modpack.type.modrinth=Modrinth
modpack.type.modrinth.export=可以被主流第三方启动器导入
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher 和 MultiMC 导入 modpack.type.multimc.export=可以被 Hello Minecraft! Launcher 和 MultiMC 导入
modpack.type.server=服务器自动更新整合包 modpack.type.server=服务器自动更新整合包
@ -747,7 +748,10 @@ modpack.wizard.step.3.title=选择整合包导出类型
modpack.wizard.step.initialization.exported_version=要导出的游戏版本 modpack.wizard.step.initialization.exported_version=要导出的游戏版本
modpack.wizard.step.initialization.force_update=强制升级整合包至最新版本 (需要自建服务器) modpack.wizard.step.initialization.force_update=强制升级整合包至最新版本 (需要自建服务器)
modpack.wizard.step.initialization.include_launcher=包含启动器 modpack.wizard.step.initialization.include_launcher=包含启动器
modpack.wizard.step.initialization.modrinth.info=在整合包创建过程中,启动器将匹配 CurseForge/Modrinth 远程资源替代本地文件(包括模组、资源包和光影包)以缩减整合包体积,并将扩展名为“.disabled”的文件标注为“安装时可选项”。
modpack.wizard.step.initialization.no_create_remote_files=不匹配远程文件
modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位置 modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位置
modpack.wizard.step.initialization.skip_curseforge_remote_files=不匹配 CurseForge 远程资源
modpack.wizard.step.initialization.warning=在制作整合包前,请你确认你选择的版本可以正常启动,\n并保证你的 Minecraft 是正式版而非快照,\n而且不应当将不允许非官方途径传播的模组、资源 (纹理) 包等纳入整合包。\n整合包会保存你目前的下载源设置。\n如遇到问题你可以点击右上角帮助按钮进行求助。 modpack.wizard.step.initialization.warning=在制作整合包前,请你确认你选择的版本可以正常启动,\n并保证你的 Minecraft 是正式版而非快照,\n而且不应当将不允许非官方途径传播的模组、资源 (纹理) 包等纳入整合包。\n整合包会保存你目前的下载源设置。\n如遇到问题你可以点击右上角帮助按钮进行求助。
modpack.wizard.step.initialization.server=点击此处查看有关服务器自动更新整合包的制作教程 modpack.wizard.step.initialization.server=点击此处查看有关服务器自动更新整合包的制作教程

View File

@ -43,16 +43,28 @@ public interface ModAdviser {
List<String> MODPACK_BLACK_LIST = Lang.immutableListOf( List<String> MODPACK_BLACK_LIST = Lang.immutableListOf(
"regex:(.*?)\\.log", "regex:(.*?)\\.log",
"regex:.*\\.dat_old$", "regex:.*\\.old$", // Backup files
"regex:.*\\.BakaCoreInfo$", // BakaXL
"regex:.*-natives",
"usernamecache.json", "usercache.json", // Minecraft "usernamecache.json", "usercache.json", // Minecraft
"launcher_profiles.json", "launcher.pack.lzma", // Old Minecraft Launcher "launcher_profiles.json", "launcher.pack.lzma", // Old Minecraft Launcher
"launcher_accounts.json", "launcher_cef_log.txt", "launcher_log.txt", "launcher_msa_credentials.bin", "launcher_settings.json", "launcher_ui_state.json", "realms_persistence.json", "webcache2", "treatment_tags.json", // New Minecraft Launcher "launcher_accounts.json", "launcher_cef_log.txt", "launcher_log.txt", "launcher_msa_credentials.bin", "launcher_settings.json", "launcher_ui_state.json", "realms_persistence.json", "webcache2", "treatment_tags.json", // New Minecraft Launcher
"clientId.txt", "PCL.ini", // Plain Craft Launcher "clientId.txt", "PCL.ini", // Plain Craft Launcher
"backup", "pack.json", "launcher.jar", "cache", "modpack.cfg", // HMCL "backup", "pack.json", "launcher.jar", "cache", "modpack.cfg", "log4j2.xml", "hmclversion.cfg", // HMCL
"manifest.json", "minecraftinstance.json", ".curseclient", // Curse "manifest.json", "minecraftinstance.json", ".curseclient", // Curse
".fabric", ".mixin.out", // Fabric "modrinth.index.json", // Modrinth
"jars", "logs", "versions", "assets", "libraries", "crash-reports", "NVIDIA", "AMD", "screenshots", "natives", "native", "$native", "server-resource-packs", // Minecraft ".fabric", ".mixin.out", ".optifine", // Fabric/OptiFine
"downloads", // Curse "jars", "logs", "versions", "assets", "libraries", "crash-reports", "NVIDIA", "AMD", "screenshots", "natives", "native", "$native", "$natives", "server-resource-packs", "command_history.txt", // Minecraft
"asm", "backups", "TCNodeTracker", "CustomDISkins", "data", "CustomSkinLoader/caches" // Mods "downloads", "essential", // Downloads and Essential
"asm", "backups", "TCNodeTracker", "CustomDISkins", "data", "CustomSkinLoader/caches", // Mods
"debug", // Debug files
".replay_cache", "replay_recordings", "replay_videos", // ReplayMod
"irisUpdateInfo.json", // Iris
"modernfix", // ModernFix
"modtranslations", // Mod translations
"schematics", // Schematics mod
"journeymap/data", // JourneyMap
"mods/.connector" // Sinytra Connector
); );
List<String> MODPACK_SUGGESTED_BLACK_LIST = Lang.immutableListOf( List<String> MODPACK_SUGGESTED_BLACK_LIST = Lang.immutableListOf(

View File

@ -29,6 +29,9 @@ public class ModpackExportInfo {
private List<McbbsModpackManifest.Origin> origins = new ArrayList<>(); private List<McbbsModpackManifest.Origin> origins = new ArrayList<>();
private boolean noCreateRemoteFiles;
private boolean skipCurseForgeRemoteFiles;
public ModpackExportInfo() {} public ModpackExportInfo() {}
public List<String> getWhitelist() { public List<String> getWhitelist() {
@ -186,6 +189,22 @@ public class ModpackExportInfo {
return this; return this;
} }
public boolean isNoCreateRemoteFiles() {
return noCreateRemoteFiles;
}
public void setNoCreateRemoteFiles(boolean noCreateRemoteFiles) {
this.noCreateRemoteFiles = noCreateRemoteFiles;
}
public boolean isSkipCurseForgeRemoteFiles() {
return skipCurseForgeRemoteFiles;
}
public void setSkipCurseForgeRemoteFiles(boolean skipCurseForgeRemoteFiles) {
this.skipCurseForgeRemoteFiles = skipCurseForgeRemoteFiles;
}
public ModpackExportInfo validate() throws NullPointerException { public ModpackExportInfo validate() throws NullPointerException {
return this; return this;
} }
@ -200,6 +219,8 @@ public class ModpackExportInfo {
private boolean requireLaunchArguments; private boolean requireLaunchArguments;
private boolean requireJavaArguments; private boolean requireJavaArguments;
private boolean requireOrigins; private boolean requireOrigins;
private boolean requireNoCreateRemoteFiles;
private boolean requireSkipCurseForgeRemoteFiles;
public Options() { public Options() {
} }
@ -240,6 +261,14 @@ public class ModpackExportInfo {
return requireOrigins; return requireOrigins;
} }
public boolean isRequireNoCreateRemoteFiles() {
return requireNoCreateRemoteFiles;
}
public boolean isRequireSkipCurseForgeRemoteFiles() {
return requireSkipCurseForgeRemoteFiles;
}
public Options requireUrl() { public Options requireUrl() {
requireUrl = true; requireUrl = true;
return this; return this;
@ -281,5 +310,14 @@ public class ModpackExportInfo {
return this; return this;
} }
public Options requireNoCreateRemoteFiles() {
requireNoCreateRemoteFiles = true;
return this;
}
public Options requireSkipCurseForgeRemoteFiles() {
requireSkipCurseForgeRemoteFiles = true;
return this;
}
} }
} }

View File

@ -0,0 +1,182 @@
package org.jackhuang.hmcl.mod.modrinth;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.mod.ModAdviser;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.ModpackExportInfo;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.DigestUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.Zipper;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public class ModrinthModpackExportTask extends Task<Void> {
private final DefaultGameRepository repository;
private final String version;
private final ModpackExportInfo info;
private final File modpackFile;
public ModrinthModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, File modpackFile) {
this.repository = repository;
this.version = version;
this.info = info.validate();
this.modpackFile = modpackFile;
onDone().register(event -> {
if (event.isFailed()) modpackFile.delete();
});
}
private ModrinthManifest.File tryGetRemoteFile(Path file, String relativePath) throws IOException {
if (info.isNoCreateRemoteFiles()) {
return null;
}
boolean isDisabled = repository.getModManager(version).isDisabled(file);
if (isDisabled) {
relativePath = repository.getModManager(version).enableMod(Paths.get(relativePath)).toString();
}
LocalModFile localModFile = null;
Optional<RemoteMod.Version> modrinthVersion = Optional.empty();
Optional<RemoteMod.Version> curseForgeVersion = Optional.empty();
try {
modrinthVersion = ModrinthRemoteModRepository.MODS.getRemoteVersionByLocalFile(localModFile, file);
} catch (IOException e) {
LOG.warning("Failed to get remote file from Modrinth for: " + file, e);
}
if (!info.isSkipCurseForgeRemoteFiles() && CurseForgeRemoteModRepository.isAvailable()) {
try {
curseForgeVersion = CurseForgeRemoteModRepository.MODS.getRemoteVersionByLocalFile(localModFile, file);
} catch (IOException e) {
LOG.warning("Failed to get remote file from CurseForge for: " + file, e);
}
}
if (!modrinthVersion.isPresent() && !curseForgeVersion.isPresent()) {
return null;
}
Map<String, String> hashes = new HashMap<>();
hashes.put("sha1", DigestUtils.digestToString("SHA-1", file));
hashes.put("sha512", DigestUtils.digestToString("SHA-512", file));
Map<String, String> env = null;
if (isDisabled) {
env = new HashMap<>();
env.put("client", "optional");
}
List<URL> downloads = new ArrayList<>();
if (modrinthVersion.isPresent())
downloads.add(new URL(modrinthVersion.get().getFile().getUrl()));
if (curseForgeVersion.isPresent())
downloads.add(new URL(curseForgeVersion.get().getFile().getUrl()));
long fileSize = Files.size(file);
if (fileSize > Integer.MAX_VALUE) {
LOG.warning("File " + relativePath + " is too large (size: " + fileSize + " bytes), precision may be lost when converting to int");
}
return new ModrinthManifest.File(
relativePath,
hashes,
env,
downloads,
(int) fileSize
);
}
@Override
public void execute() throws Exception {
ArrayList<String> blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST);
blackList.add(version + ".jar");
blackList.add(version + ".json");
LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
try (Zipper zip = new Zipper(modpackFile.toPath())) {
Path runDirectory = repository.getRunDirectory(version).toPath();
List<ModrinthManifest.File> files = new ArrayList<>();
Set<String> filesInManifest = new HashSet<>();
String[] resourceDirs = {"resourcepacks", "shaderpacks", "mods"};
for (String dir : resourceDirs) {
Path dirPath = runDirectory.resolve(dir);
if (Files.exists(dirPath)) {
Files.walk(dirPath)
.filter(Files::isRegularFile)
.forEach(file -> {
try {
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
if (!info.getWhitelist().contains(relativePath)) {
return;
}
ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath);
if (fileEntry != null) {
files.add(fileEntry);
filesInManifest.add(relativePath);
}
} catch (IOException e) {
LOG.warning("Failed to process file: " + file, e);
}
});
}
}
zip.putDirectory(runDirectory, "client-overrides", path -> {
String relativePath = path.toString().replace(File.separatorChar, '/');
if (filesInManifest.contains(relativePath)) {
return false;
}
return Modpack.acceptFile(path, blackList, info.getWhitelist());
});
String gameVersion = repository.getGameVersion(version)
.orElseThrow(() -> new IOException("Cannot parse the version of " + version));
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion);
Map<String, String> dependencies = new HashMap<>();
dependencies.put("minecraft", gameVersion);
analyzer.getVersion(FORGE).ifPresent(forgeVersion ->
dependencies.put("forge", forgeVersion));
analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion ->
dependencies.put("neoforge", neoForgeVersion));
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
dependencies.put("fabric-loader", fabricVersion));
analyzer.getVersion(QUILT).ifPresent(quiltVersion ->
dependencies.put("quilt-loader", quiltVersion));
ModrinthManifest manifest = new ModrinthManifest(
"minecraft",
1,
info.getVersion(),
info.getName(),
info.getDescription(),
files,
dependencies
);
zip.putTextFile(JsonUtils.GSON.toJson(manifest), "modrinth.index.json");
}
}
public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options()
.requireNoCreateRemoteFiles()
.requireSkipCurseForgeRemoteFiles();
}