mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-18 08:16:58 -04:00
Support Server Auto-Update Task. Closes #624
This commit is contained in:
parent
177935241f
commit
4e0ffc8d1e
@ -62,17 +62,7 @@ public class HMCLModpackExportTask extends Task<Void> {
|
|||||||
blackList.add(version + ".json");
|
blackList.add(version + ".json");
|
||||||
Logging.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");
|
Logging.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(output.toPath())) {
|
try (Zipper zip = new Zipper(output.toPath())) {
|
||||||
zip.putDirectory(repository.getRunDirectory(version).toPath(), "minecraft", path -> {
|
zip.putDirectory(repository.getRunDirectory(version).toPath(), "minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
|
||||||
if (path.isEmpty())
|
|
||||||
return true;
|
|
||||||
for (String s : blackList)
|
|
||||||
if (path.equals(s))
|
|
||||||
return false;
|
|
||||||
for (String s : whitelist)
|
|
||||||
if (path.equals(s))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
Version mv = repository.getResolvedPreservingPatchesVersion(version);
|
Version mv = repository.getResolvedPreservingPatchesVersion(version);
|
||||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version))
|
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version))
|
||||||
|
@ -31,9 +31,10 @@ import org.jackhuang.hmcl.launch.NotDecompressingNativesException;
|
|||||||
import org.jackhuang.hmcl.launch.PermissionException;
|
import org.jackhuang.hmcl.launch.PermissionException;
|
||||||
import org.jackhuang.hmcl.launch.ProcessCreationException;
|
import org.jackhuang.hmcl.launch.ProcessCreationException;
|
||||||
import org.jackhuang.hmcl.launch.ProcessListener;
|
import org.jackhuang.hmcl.launch.ProcessListener;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionException;
|
import org.jackhuang.hmcl.mod.curse.CurseCompletionException;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
|
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
|
||||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
import org.jackhuang.hmcl.mod.server.ServerModpackCompletionTask;
|
||||||
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||||
@ -62,13 +63,7 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -147,6 +142,8 @@ public final class LauncherHelper {
|
|||||||
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
||||||
if ("Curse".equals(configuration.getType()))
|
if ("Curse".equals(configuration.getType()))
|
||||||
return new CurseCompletionTask(dependencyManager, selectedVersion);
|
return new CurseCompletionTask(dependencyManager, selectedVersion);
|
||||||
|
else if ("Server".equals(configuration.getType()))
|
||||||
|
return new ServerModpackCompletionTask(dependencyManager, selectedVersion);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -25,6 +25,8 @@ import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
|
|||||||
import org.jackhuang.hmcl.mod.curse.CurseManifest;
|
import org.jackhuang.hmcl.mod.curse.CurseManifest;
|
||||||
import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration;
|
import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration;
|
||||||
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
|
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
|
||||||
|
import org.jackhuang.hmcl.mod.server.ServerModpackInstallTask;
|
||||||
|
import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
|
||||||
import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||||
@ -65,6 +67,12 @@ public final class ModpackHelper {
|
|||||||
// ignore it, not a valid MultiMC modpack.
|
// ignore it, not a valid MultiMC modpack.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ServerModpackManifest.readManifest(file, charset);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore it, not a valid Server modpack.
|
||||||
|
}
|
||||||
|
|
||||||
throw new UnsupportedModpackException(file.toString());
|
throw new UnsupportedModpackException(file.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +128,10 @@ public final class ModpackHelper {
|
|||||||
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
|
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
|
||||||
.whenComplete(Schedulers.defaultScheduler(), success, failure)
|
.whenComplete(Schedulers.defaultScheduler(), success, failure)
|
||||||
.thenComposeAsync(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
|
.thenComposeAsync(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
|
||||||
else throw new IllegalArgumentException("Unrecognized modpack: " + modpack);
|
else if (modpack.getManifest() instanceof ServerModpackManifest)
|
||||||
|
return new ServerModpackInstallTask(profile.getDependency(), zipFile, modpack, ((ServerModpackManifest) modpack.getManifest()), name)
|
||||||
|
.whenComplete(Schedulers.defaultScheduler(), success, failure);
|
||||||
|
else throw new IllegalArgumentException("Unrecognized modpack: " + modpack.getManifest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<Void> getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
|
public static Task<Void> getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
|
||||||
|
@ -151,7 +151,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
|
|||||||
if (manifest != null) {
|
if (manifest != null) {
|
||||||
WebStage stage = new WebStage();
|
WebStage stage = new WebStage();
|
||||||
stage.getWebView().getEngine().loadContent(manifest.getDescription());
|
stage.getWebView().getEngine().loadContent(manifest.getDescription());
|
||||||
stage.setTitle(i18n("modpack.wizard.step.3"));
|
stage.setTitle(i18n("modpack.description"));
|
||||||
stage.showAndWait();
|
stage.showAndWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ public final class ModpackSelectionPage extends StackPane implements WizardPage
|
|||||||
Optional<File> filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class);
|
Optional<File> filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class);
|
||||||
if (filePath.isPresent()) {
|
if (filePath.isPresent()) {
|
||||||
controller.getSettings().put(MODPACK_FILE, filePath.get());
|
controller.getSettings().put(MODPACK_FILE, filePath.get());
|
||||||
controller.onNext();
|
Platform.runLater(controller::onNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it)), modpacks -> {
|
FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it)), modpacks -> {
|
||||||
|
@ -24,6 +24,7 @@ import org.jackhuang.hmcl.mod.ModAdviser;
|
|||||||
import org.jackhuang.hmcl.mod.Modpack;
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
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.setting.Config;
|
import org.jackhuang.hmcl.setting.Config;
|
||||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
@ -74,6 +75,8 @@ public final class ExportWizardProvider implements WizardProvider {
|
|||||||
return exportAsHMCL(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, includeLauncher);
|
return exportAsHMCL(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, includeLauncher);
|
||||||
case ModpackTypeSelectionPage.MODPACK_TYPE_MULTIMC:
|
case ModpackTypeSelectionPage.MODPACK_TYPE_MULTIMC:
|
||||||
return exportAsMultiMC(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription);
|
return exportAsMultiMC(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription);
|
||||||
|
case ModpackTypeSelectionPage.MODPACK_TYPE_SERVER:
|
||||||
|
return exportAsServer(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription);
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unrecognized modpack type " + modpackType);
|
throw new IllegalStateException("Unrecognized modpack type " + modpackType);
|
||||||
}
|
}
|
||||||
@ -177,6 +180,21 @@ public final class ExportWizardProvider implements WizardProvider {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task<?> exportAsServer(List<String> whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription) {
|
||||||
|
return new Task<Void>() {
|
||||||
|
Task<?> dependency;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
dependency = new ServerModpackExportTask(profile.getRepository(), version, whitelist, modpackName, modpackAuthor, modpackVersion, modpackDescription, 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) {
|
||||||
|
@ -156,6 +156,7 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
|
|||||||
|
|
||||||
public static final String MODPACK_FILE_SELECTION = "modpack.accepted";
|
public static final String MODPACK_FILE_SELECTION = "modpack.accepted";
|
||||||
private static final Map<String, String> TRANSLATION = mapOf(
|
private static final Map<String, String> TRANSLATION = mapOf(
|
||||||
|
pair("minecraft/hmclversion.cfg", i18n("modpack.files.hmclversion_cfg")),
|
||||||
pair("minecraft/servers.dat", i18n("modpack.files.servers_dat")),
|
pair("minecraft/servers.dat", i18n("modpack.files.servers_dat")),
|
||||||
pair("minecraft/saves", i18n("modpack.files.saves")),
|
pair("minecraft/saves", i18n("modpack.files.saves")),
|
||||||
pair("minecraft/mods", i18n("modpack.files.mods")),
|
pair("minecraft/mods", i18n("modpack.files.mods")),
|
||||||
|
@ -34,13 +34,15 @@ public final class ModpackTypeSelectionPage extends StackPane implements WizardP
|
|||||||
private JFXButton btnHMCL;
|
private JFXButton btnHMCL;
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnMultiMC;
|
private JFXButton btnMultiMC;
|
||||||
|
@FXML
|
||||||
|
private JFXButton btnServer;
|
||||||
|
|
||||||
public ModpackTypeSelectionPage(WizardController controller) {
|
public ModpackTypeSelectionPage(WizardController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
FXUtils.loadFXML(this, "/assets/fxml/modpack/type.fxml");
|
FXUtils.loadFXML(this, "/assets/fxml/modpack/type.fxml");
|
||||||
|
|
||||||
JFXButton[] buttons = new JFXButton[]{btnHMCL, btnMultiMC};
|
JFXButton[] buttons = new JFXButton[]{btnHMCL, btnMultiMC, btnServer};
|
||||||
String[] types = new String[]{MODPACK_TYPE_HMCL, MODPACK_TYPE_MULTIMC};
|
String[] types = new String[]{MODPACK_TYPE_HMCL, MODPACK_TYPE_MULTIMC, MODPACK_TYPE_SERVER};
|
||||||
for (int i = 0; i < types.length; ++i) {
|
for (int i = 0; i < types.length; ++i) {
|
||||||
String type = types[i];
|
String type = types[i];
|
||||||
buttons[i].setOnMouseClicked(e -> {
|
buttons[i].setOnMouseClicked(e -> {
|
||||||
@ -61,6 +63,7 @@ public final class ModpackTypeSelectionPage extends StackPane implements WizardP
|
|||||||
|
|
||||||
public static final String MODPACK_TYPE = "modpack.type";
|
public static final String MODPACK_TYPE = "modpack.type";
|
||||||
|
|
||||||
public static final String MODPACK_TYPE_MULTIMC = "multimc";
|
|
||||||
public static final String MODPACK_TYPE_HMCL = "hmcl";
|
public static final String MODPACK_TYPE_HMCL = "hmcl";
|
||||||
|
public static final String MODPACK_TYPE_MULTIMC = "multimc";
|
||||||
|
public static final String MODPACK_TYPE_SERVER = "server";
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<BorderPane><left><Label text="%archive.version"/></left><right><Label fx:id="lblVersion" /></right></BorderPane>
|
<BorderPane><left><Label text="%archive.version"/></left><right><Label fx:id="lblVersion" /></right></BorderPane>
|
||||||
<BorderPane><left><Label text="%archive.author"/></left><right><Label fx:id="lblAuthor" /></right></BorderPane>
|
<BorderPane><left><Label text="%archive.author"/></left><right><Label fx:id="lblAuthor" /></right></BorderPane>
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.wizard.step.3" styleClass="jfx-button-border" /></left>
|
<left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.description" styleClass="jfx-button-border" /></left>
|
||||||
<right><JFXButton buttonType="RAISED" fx:id="btnInstall" onMouseClicked="#onInstall" text="%button.install" styleClass="jfx-button-raised" /></right>
|
<right><JFXButton buttonType="RAISED" fx:id="btnInstall" onMouseClicked="#onInstall" text="%button.install" styleClass="jfx-button-raised" /></right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</ComponentList>
|
</ComponentList>
|
||||||
|
@ -37,5 +37,17 @@
|
|||||||
</BorderPane>
|
</BorderPane>
|
||||||
</graphic>
|
</graphic>
|
||||||
</JFXButton>
|
</JFXButton>
|
||||||
|
<JFXButton fx:id="btnServer" prefWidth="${list.width}">
|
||||||
|
<graphic>
|
||||||
|
<BorderPane mouseTransparent="true">
|
||||||
|
<left>
|
||||||
|
<TwoLineListItem title="%modpack.type.server" subtitle="%modpack.type.server.export" />
|
||||||
|
</left>
|
||||||
|
<right>
|
||||||
|
<fx:include BorderPane.alignment="CENTER" source="/assets/svg/arrow-right.fxml"/>
|
||||||
|
</right>
|
||||||
|
</BorderPane>
|
||||||
|
</graphic>
|
||||||
|
</JFXButton>
|
||||||
</VBox>
|
</VBox>
|
||||||
</fx:root>
|
</fx:root>
|
||||||
|
@ -224,12 +224,14 @@ modpack.choose.remote=Download modpack from Internet
|
|||||||
modpack.choose.remote.detail=Requires a direct download link to the remote modpack file
|
modpack.choose.remote.detail=Requires a direct download link to the remote modpack file
|
||||||
modpack.choose.remote.tooltip=A direct download link to the remote modpack file
|
modpack.choose.remote.tooltip=A direct download link to the remote modpack file
|
||||||
modpack.desc=Describe your modpack, including precautions and changelog. Markdown and online pictures are supported.
|
modpack.desc=Describe your modpack, including precautions and changelog. Markdown and online pictures are supported.
|
||||||
|
modpack.description=Description
|
||||||
modpack.enter_name=Enter a name for this modpack.
|
modpack.enter_name=Enter a name for this modpack.
|
||||||
modpack.export=Export Modpack
|
modpack.export=Export Modpack
|
||||||
modpack.export.as=Export Modpack As...
|
modpack.export.as=Export Modpack As...
|
||||||
modpack.files.blueprints=BuildCraft blueprints
|
modpack.files.blueprints=BuildCraft blueprints
|
||||||
modpack.files.config=Mod configs
|
modpack.files.config=Mod configs
|
||||||
modpack.files.dumps=NEI debug output
|
modpack.files.dumps=NEI debug output
|
||||||
|
modpack.files.hmclversion_cfg=Launcher configuration file
|
||||||
modpack.files.liteconfig=Mod configurations
|
modpack.files.liteconfig=Mod configurations
|
||||||
modpack.files.mods=Mods
|
modpack.files.mods=Mods
|
||||||
modpack.files.mods.voxelmods=VoxelMods (including VoxelMap) options
|
modpack.files.mods.voxelmods=VoxelMods (including VoxelMap) options
|
||||||
@ -259,6 +261,8 @@ modpack.type.hmcl=Hello Minecraft! Launcher
|
|||||||
modpack.type.hmcl.export=Can be imported by Hello Minecraft! Launcher
|
modpack.type.hmcl.export=Can be imported by Hello Minecraft! Launcher
|
||||||
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=Server Auto-Update Modpack
|
||||||
|
modpack.type.server.export=Allow server manager updating game client remotely
|
||||||
modpack.unsupported=Unsupported modpack, only HMCL, MultiMC, and Curse modpacks are supported.
|
modpack.unsupported=Unsupported modpack, only HMCL, MultiMC, and Curse modpacks are supported.
|
||||||
modpack.update=Game Update
|
modpack.update=Game Update
|
||||||
modpack.wizard=Exporting modpack wizard
|
modpack.wizard=Exporting modpack wizard
|
||||||
|
@ -218,6 +218,7 @@ message.warning=Advertencia
|
|||||||
modpack=Modpack
|
modpack=Modpack
|
||||||
modpack.choose=Escoja el modpack en archivo zip que quieras instalar.
|
modpack.choose=Escoja el modpack en archivo zip que quieras instalar.
|
||||||
modpack.desc=Describa su modpack, incluyendo precauciones, changelog, soportando Markdown (también soportando imágenes en línea).
|
modpack.desc=Describa su modpack, incluyendo precauciones, changelog, soportando Markdown (también soportando imágenes en línea).
|
||||||
|
modpack.description=Descripción
|
||||||
modpack.enter_name=Entre su nombre deseado para este juego.
|
modpack.enter_name=Entre su nombre deseado para este juego.
|
||||||
modpack.export=Exportar modpack
|
modpack.export=Exportar modpack
|
||||||
modpack.export.as=Exportar modpack como...
|
modpack.export.as=Exportar modpack como...
|
||||||
|
@ -218,6 +218,7 @@ message.warning=Предупреждение
|
|||||||
modpack=Модпак
|
modpack=Модпак
|
||||||
modpack.choose=Выберите zip-файл модпака, который вы хотите установить.
|
modpack.choose=Выберите zip-файл модпака, который вы хотите установить.
|
||||||
modpack.desc=Опишите свой модпак, включая меры предосторожности, журнал изменений, поддерживая Markdown (также поддерживая онлайн-изображения).
|
modpack.desc=Опишите свой модпак, включая меры предосторожности, журнал изменений, поддерживая Markdown (также поддерживая онлайн-изображения).
|
||||||
|
modpack.description=описание
|
||||||
modpack.enter_name=Введите желаемое имя для этой версии.
|
modpack.enter_name=Введите желаемое имя для этой версии.
|
||||||
modpack.export=Экспортировать модпак
|
modpack.export=Экспортировать модпак
|
||||||
modpack.export.as=Экспорт модпака как...
|
modpack.export.as=Экспорт модпака как...
|
||||||
|
@ -221,12 +221,14 @@ modpack.choose.remote=從網路下載整合包
|
|||||||
modpack.choose.remote.detail=需要提供整合包的下載連結
|
modpack.choose.remote.detail=需要提供整合包的下載連結
|
||||||
modpack.choose.remote.tooltip=要下載的整合包的連結
|
modpack.choose.remote.tooltip=要下載的整合包的連結
|
||||||
modpack.desc=描述你要製作的整合包,比如整合包注意事項和更新記錄,支援 Markdown(圖片請上傳至網路)。
|
modpack.desc=描述你要製作的整合包,比如整合包注意事項和更新記錄,支援 Markdown(圖片請上傳至網路)。
|
||||||
|
modpack.description=整合包描述
|
||||||
modpack.enter_name=給遊戲取個你喜歡的名字
|
modpack.enter_name=給遊戲取個你喜歡的名字
|
||||||
modpack.export=匯出整合包
|
modpack.export=匯出整合包
|
||||||
modpack.export.as=請選擇整合包類型。若你無法決定,請選擇 HMCL 類型。
|
modpack.export.as=請選擇整合包類型。若你無法決定,請選擇 HMCL 類型。
|
||||||
modpack.files.blueprints=BuildCraft 藍圖
|
modpack.files.blueprints=BuildCraft 藍圖
|
||||||
modpack.files.config=Mod 模組設定檔案
|
modpack.files.config=Mod 模組設定檔案
|
||||||
modpack.files.dumps=NEI 調校輸出
|
modpack.files.dumps=NEI 調校輸出
|
||||||
|
modpack.files.hmclversion_cfg=啟動器設定檔案
|
||||||
modpack.files.liteconfig=Mod 模組設定檔案
|
modpack.files.liteconfig=Mod 模組設定檔案
|
||||||
modpack.files.mods=Mod 模組
|
modpack.files.mods=Mod 模組
|
||||||
modpack.files.mods.voxelmods=VoxelMods 設定,如小地圖
|
modpack.files.mods.voxelmods=VoxelMods 設定,如小地圖
|
||||||
@ -256,6 +258,8 @@ modpack.type.hmcl=HMCL
|
|||||||
modpack.type.hmcl.export=可以被 Hello Minecraft! Launcher (HMCL) 導入
|
modpack.type.hmcl.export=可以被 Hello Minecraft! Launcher (HMCL) 導入
|
||||||
modpack.type.multimc=MultiMC
|
modpack.type.multimc=MultiMC
|
||||||
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 導入
|
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 導入
|
||||||
|
modpack.type.server=伺服器自動更新整合包
|
||||||
|
modpack.type.server.export=允許伺服器管理員遠程更新遊戲用戶端
|
||||||
modpack.unsupported=不支援該整合包。僅 HMCL、MultiMC、Curse 整合包受支援。
|
modpack.unsupported=不支援該整合包。僅 HMCL、MultiMC、Curse 整合包受支援。
|
||||||
modpack.update=正在升級整合包
|
modpack.update=正在升級整合包
|
||||||
modpack.wizard=匯出整合包引導
|
modpack.wizard=匯出整合包引導
|
||||||
|
@ -222,12 +222,14 @@ modpack.choose.remote=从互联网下载整合包
|
|||||||
modpack.choose.remote.detail=需要提供整合包的下载链接
|
modpack.choose.remote.detail=需要提供整合包的下载链接
|
||||||
modpack.choose.remote.tooltip=要下载的整合包的链接
|
modpack.choose.remote.tooltip=要下载的整合包的链接
|
||||||
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录,支持 Markdown(图片请用网络图)。
|
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录,支持 Markdown(图片请用网络图)。
|
||||||
|
modpack.description=整合包描述
|
||||||
modpack.enter_name=给游戏起个你喜欢的名字
|
modpack.enter_name=给游戏起个你喜欢的名字
|
||||||
modpack.export=导出整合包
|
modpack.export=导出整合包
|
||||||
modpack.export.as=请选择整合包类型。若你无法决定,请选择 HMCL 类型。
|
modpack.export.as=请选择整合包类型。若你无法决定,请选择 HMCL 类型。
|
||||||
modpack.files.blueprints=BuildCraft 蓝图
|
modpack.files.blueprints=BuildCraft 蓝图
|
||||||
modpack.files.config=Mod 配置文件
|
modpack.files.config=Mod 配置文件
|
||||||
modpack.files.dumps=NEI 调试输出
|
modpack.files.dumps=NEI 调试输出
|
||||||
|
modpack.files.hmclversion_cfg=启动器配置文件
|
||||||
modpack.files.liteconfig=Mod 配置文件
|
modpack.files.liteconfig=Mod 配置文件
|
||||||
modpack.files.mods=Mod
|
modpack.files.mods=Mod
|
||||||
modpack.files.mods.voxelmods=VoxelMods 配置,如小地图
|
modpack.files.mods.voxelmods=VoxelMods 配置,如小地图
|
||||||
@ -257,6 +259,8 @@ modpack.type.hmcl=HMCL
|
|||||||
modpack.type.hmcl.export=可以被 Hello Minecraft! Launcher (HMCL) 导入
|
modpack.type.hmcl.export=可以被 Hello Minecraft! Launcher (HMCL) 导入
|
||||||
modpack.type.multimc=MultiMC
|
modpack.type.multimc=MultiMC
|
||||||
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 导入
|
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 导入
|
||||||
|
modpack.type.server=服务器自动更新整合包
|
||||||
|
modpack.type.server.export=允许服务器管理员远程更新游戏客户端
|
||||||
modpack.unsupported=该整合包不被支持。仅 HMCL、MultiMC、Curse 整合包受支持。
|
modpack.unsupported=该整合包不被支持。仅 HMCL、MultiMC、Curse 整合包受支持。
|
||||||
modpack.update=正在升级整合包
|
modpack.update=正在升级整合包
|
||||||
modpack.wizard=导出整合包向导
|
modpack.wizard=导出整合包向导
|
||||||
|
@ -62,10 +62,10 @@ public final class MinecraftInstanceTask<T> extends Task<Void> {
|
|||||||
Path root = fs.getPath(subDirectory);
|
Path root = fs.getPath(subDirectory);
|
||||||
|
|
||||||
if (Files.exists(root))
|
if (Files.exists(root))
|
||||||
Files.walkFileTree(fs.getPath(subDirectory), new SimpleFileVisitor<Path>() {
|
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
String relativePath = root.relativize(file).normalize().toString();
|
String relativePath = root.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||||
overrides.add(new ModpackConfiguration.FileInformation(relativePath, encodeHex(digest("SHA-1", file))));
|
overrides.add(new ModpackConfiguration.FileInformation(relativePath, encodeHex(digest("SHA-1", file))));
|
||||||
return FileVisitResult.CONTINUE;
|
return FileVisitResult.CONTINUE;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public interface ModAdviser {
|
|||||||
List<String> MODPACK_BLACK_LIST = Lang.immutableListOf(
|
List<String> MODPACK_BLACK_LIST = Lang.immutableListOf(
|
||||||
"usernamecache.json", "usercache.json", // Minecraft
|
"usernamecache.json", "usercache.json", // Minecraft
|
||||||
"launcher_profiles.json", "launcher.pack.lzma", // Minecraft Launcher
|
"launcher_profiles.json", "launcher.pack.lzma", // Minecraft Launcher
|
||||||
"pack.json", "launcher.jar", "hmclmc.log", "cache", // HMCL
|
"pack.json", "launcher.jar", "hmclmc.log", "cache", "modpack.cfg", // HMCL
|
||||||
"manifest.json", "minecraftinstance.json", ".curseclient", // Curse
|
"manifest.json", "minecraftinstance.json", ".curseclient", // Curse
|
||||||
"minetweaker.log", // Mods
|
"minetweaker.log", // Mods
|
||||||
".fabric", ".mixin.out", // Fabric
|
".fabric", ".mixin.out", // Fabric
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.mod;
|
package org.jackhuang.hmcl.mod;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -85,4 +86,16 @@ public final class Modpack {
|
|||||||
public Modpack setManifest(Object manifest) {
|
public Modpack setManifest(Object manifest) {
|
||||||
return new Modpack(name, author, version, gameVersion, description, encoding, manifest);
|
return new Modpack(name, author, version, gameVersion, description, encoding, manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean acceptFile(String path, List<String> blackList, List<String> whiteList) {
|
||||||
|
if (path.isEmpty())
|
||||||
|
return true;
|
||||||
|
for (String s : blackList)
|
||||||
|
if (path.equals(s))
|
||||||
|
return false;
|
||||||
|
for (String s : whiteList)
|
||||||
|
if (path.equals(s))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,15 @@ public class ModpackInstallTask<T> extends Task<Void> {
|
|||||||
private final List<ModpackConfiguration.FileInformation> overrides;
|
private final List<ModpackConfiguration.FileInformation> overrides;
|
||||||
private final Predicate<String> callback;
|
private final Predicate<String> callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param modpackFile a zip file
|
||||||
|
* @param dest destination to store unpacked files
|
||||||
|
* @param charset charset of the zip file
|
||||||
|
* @param subDirectory the subdirectory of zip file to unpack
|
||||||
|
* @param callback test whether the file (given full path) in zip file should be unpacked or not
|
||||||
|
* @param oldConfiguration old modpack information if upgrade
|
||||||
|
*/
|
||||||
public ModpackInstallTask(File modpackFile, File dest, Charset charset, String subDirectory, Predicate<String> callback, ModpackConfiguration<T> oldConfiguration) {
|
public ModpackInstallTask(File modpackFile, File dest, Charset charset, String subDirectory, Predicate<String> callback, ModpackConfiguration<T> oldConfiguration) {
|
||||||
this.modpackFile = modpackFile;
|
this.modpackFile = modpackFile;
|
||||||
this.dest = dest;
|
this.dest = dest;
|
||||||
|
@ -52,7 +52,6 @@ public final class CurseCompletionTask extends Task<Void> {
|
|||||||
private final ModManager modManager;
|
private final ModManager modManager;
|
||||||
private final String version;
|
private final String version;
|
||||||
private CurseManifest manifest;
|
private CurseManifest manifest;
|
||||||
private final List<Task<?>> dependents = new LinkedList<>();
|
|
||||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,11 +93,6 @@ public final class CurseCompletionTask extends Task<Void> {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Task<?>> getDependents() {
|
|
||||||
return dependents;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
if (manifest == null)
|
if (manifest == null)
|
||||||
|
@ -113,7 +113,7 @@ public final class CurseManifest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param f the CurseForge modpack file.
|
* @param zip the CurseForge modpack file.
|
||||||
* @throws IOException if the file is not a valid zip file.
|
* @throws IOException if the file is not a valid zip file.
|
||||||
* @throws JsonParseException if the manifest.json is missing or malformed.
|
* @throws JsonParseException if the manifest.json is missing or malformed.
|
||||||
* @return the manifest.
|
* @return the manifest.
|
||||||
|
@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
|||||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
import org.jackhuang.hmcl.game.GameVersion;
|
import org.jackhuang.hmcl.game.GameVersion;
|
||||||
import org.jackhuang.hmcl.mod.ModAdviser;
|
import org.jackhuang.hmcl.mod.ModAdviser;
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
@ -67,17 +68,7 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
|||||||
blackList.add(versionId + ".json");
|
blackList.add(versionId + ".json");
|
||||||
Logging.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");
|
Logging.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(output.toPath())) {
|
try (Zipper zip = new Zipper(output.toPath())) {
|
||||||
zip.putDirectory(repository.getRunDirectory(versionId).toPath(), ".minecraft", path -> {
|
zip.putDirectory(repository.getRunDirectory(versionId).toPath(), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
|
||||||
if (path.isEmpty())
|
|
||||||
return true;
|
|
||||||
for (String s : blackList)
|
|
||||||
if (path.equals(s))
|
|
||||||
return false;
|
|
||||||
for (String s : whitelist)
|
|
||||||
if (path.equals(s))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
|
||||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
|
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
|
||||||
|
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.mod.server;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.GetTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.DigestUtils.digest;
|
||||||
|
import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
||||||
|
|
||||||
|
public class ServerModpackCompletionTask extends Task<Void> {
|
||||||
|
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final String version;
|
||||||
|
private ModpackConfiguration<ServerModpackManifest> manifest;
|
||||||
|
private GetTask dependent;
|
||||||
|
private ServerModpackManifest remoteManifest;
|
||||||
|
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) {
|
||||||
|
this.repository = dependencyManager.getGameRepository();
|
||||||
|
this.version = version;
|
||||||
|
|
||||||
|
try {
|
||||||
|
File manifestFile = repository.getModpackConfiguration(version);
|
||||||
|
if (manifestFile.exists())
|
||||||
|
this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), new TypeToken<ModpackConfiguration<ServerModpackManifest>>() {
|
||||||
|
}.getType());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logging.LOG.log(Level.WARNING, "Unable to read CurseForge modpack manifest.json", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doPreExecute() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preExecute() throws Exception {
|
||||||
|
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||||
|
dependent = new GetTask(new URL(manifest.getManifest().getFileApi() + "/server-manifest.json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task<?>> getDependents() {
|
||||||
|
return dependent == null ? Collections.emptySet() : Collections.singleton(dependent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
remoteManifest = JsonUtils.fromNonNullJson(dependent.getResult(), ServerModpackManifest.class);
|
||||||
|
} catch (JsonParseException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Path rootPath = repository.getVersionRoot(version).toPath();
|
||||||
|
Map<String, ModpackConfiguration.FileInformation> files = manifest.getManifest().getFiles().stream()
|
||||||
|
.collect(Collectors.toMap(ModpackConfiguration.FileInformation::getPath,
|
||||||
|
Function.identity()));
|
||||||
|
|
||||||
|
Set<String> remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// for files in new modpack
|
||||||
|
for (ModpackConfiguration.FileInformation file : remoteManifest.getFiles()) {
|
||||||
|
Path actualPath = rootPath.resolve(file.getPath());
|
||||||
|
boolean download;
|
||||||
|
if (!files.containsKey(file.getPath())) {
|
||||||
|
// If old modpack does not have this entry, download it
|
||||||
|
download = true;
|
||||||
|
} else if (!Files.exists(actualPath)) {
|
||||||
|
// If both old and new modpacks have this entry, but the file is deleted by user, leave it missing.
|
||||||
|
download = false;
|
||||||
|
} else {
|
||||||
|
// If user modified this entry file, we will not replace this file since this modified file is that user expects.
|
||||||
|
String fileHash = encodeHex(digest("SHA-1", Files.newInputStream(actualPath)));
|
||||||
|
String oldHash = files.get(file.getPath()).getHash();
|
||||||
|
download = !Objects.equals(oldHash, file.getHash()) && Objects.equals(oldHash, fileHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (download) {
|
||||||
|
dependencies.add(new FileDownloadTask(
|
||||||
|
new URL(remoteManifest.getFileApi() + "/overrides/" + file.getPath()),
|
||||||
|
actualPath.toFile(),
|
||||||
|
new FileDownloadTask.IntegrityCheck("SHA-1", file.getHash())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If old modpack have this entry, and new modpack deleted it. Delete this file.
|
||||||
|
for (ModpackConfiguration.FileInformation file : manifest.getManifest().getFiles()) {
|
||||||
|
Path actualPath = rootPath.resolve(file.getPath());
|
||||||
|
if (Files.exists(actualPath) && !remoteFiles.contains(file.getPath()))
|
||||||
|
Files.deleteIfExists(actualPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doPostExecute() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postExecute() throws Exception {
|
||||||
|
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||||
|
File manifestFile = repository.getModpackConfiguration(version);
|
||||||
|
FileUtils.writeText(manifestFile, JsonUtils.GSON.toJson(new ModpackConfiguration<>(remoteManifest, this.manifest.getType(), remoteManifest.getFiles())));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.mod.server;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
|
import org.jackhuang.hmcl.game.GameVersion;
|
||||||
|
import org.jackhuang.hmcl.mod.ModAdviser;
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.Zipper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||||
|
import static org.jackhuang.hmcl.util.DigestUtils.digest;
|
||||||
|
import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
||||||
|
|
||||||
|
public class ServerModpackExportTask extends Task<Void> {
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final String versionId;
|
||||||
|
private final List<String> whitelist;
|
||||||
|
private final File output;
|
||||||
|
private final String modpackName;
|
||||||
|
private final String modpackAuthor;
|
||||||
|
private final String modpackVersion;
|
||||||
|
private final String modpackDescription;
|
||||||
|
|
||||||
|
public ServerModpackExportTask(DefaultGameRepository repository, String versionId, List<String> whitelist, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, File output) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.versionId = versionId;
|
||||||
|
this.whitelist = whitelist;
|
||||||
|
this.output = output;
|
||||||
|
this.modpackName = modpackName;
|
||||||
|
this.modpackAuthor = modpackAuthor;
|
||||||
|
this.modpackVersion = modpackVersion;
|
||||||
|
this.modpackDescription = modpackDescription;
|
||||||
|
|
||||||
|
onDone().register(event -> {
|
||||||
|
if (event.isFailed()) output.delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
ArrayList<String> blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST);
|
||||||
|
blackList.add(versionId + ".jar");
|
||||||
|
blackList.add(versionId + ".json");
|
||||||
|
Logging.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(output.toPath())) {
|
||||||
|
Path runDirectory = repository.getRunDirectory(versionId).toPath();
|
||||||
|
List<ModpackConfiguration.FileInformation> files = new ArrayList<>();
|
||||||
|
zip.putDirectory(runDirectory, ".minecraft", path -> {
|
||||||
|
if (Modpack.acceptFile(path, blackList, whitelist)) {
|
||||||
|
Path file = runDirectory.resolve(path);
|
||||||
|
if (Files.isRegularFile(file)) {
|
||||||
|
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||||
|
files.add(new ModpackConfiguration.FileInformation(relativePath, encodeHex(digest("SHA-1", file))));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
|
||||||
|
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
|
||||||
|
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
|
||||||
|
List<ServerModpackManifest.Addon> addons = new ArrayList<>();
|
||||||
|
addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion));
|
||||||
|
analyzer.getVersion(FORGE).ifPresent(forgeVersion ->
|
||||||
|
addons.add(new ServerModpackManifest.Addon(FORGE.getPatchId(), forgeVersion)));
|
||||||
|
analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion ->
|
||||||
|
addons.add(new ServerModpackManifest.Addon(LITELOADER.getPatchId(), liteLoaderVersion)));
|
||||||
|
analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion ->
|
||||||
|
addons.add(new ServerModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion)));
|
||||||
|
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
|
||||||
|
addons.add(new ServerModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion)));
|
||||||
|
ServerModpackManifest manifest = new ServerModpackManifest(modpackName, modpackAuthor, modpackVersion, modpackDescription, "", files, addons);
|
||||||
|
zip.putTextFile(JsonUtils.GSON.toJson(manifest), "server-manifest.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.mod.server;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.download.GameBuilder;
|
||||||
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
|
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ServerModpackInstallTask extends Task<Void> {
|
||||||
|
|
||||||
|
private final File zipFile;
|
||||||
|
private final Modpack modpack;
|
||||||
|
private final ServerModpackManifest manifest;
|
||||||
|
private final String name;
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||||
|
private final List<Task<?>> dependents = new LinkedList<>();
|
||||||
|
|
||||||
|
public ServerModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, ServerModpackManifest manifest, String name) {
|
||||||
|
this.zipFile = zipFile;
|
||||||
|
this.modpack = modpack;
|
||||||
|
this.manifest = manifest;
|
||||||
|
this.name = name;
|
||||||
|
this.repository = dependencyManager.getGameRepository();
|
||||||
|
File run = repository.getRunDirectory(name);
|
||||||
|
|
||||||
|
File json = repository.getModpackConfiguration(name);
|
||||||
|
if (repository.hasVersion(name) && !json.exists())
|
||||||
|
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||||
|
|
||||||
|
GameBuilder builder = dependencyManager.gameBuilder().name(name);
|
||||||
|
for (ServerModpackManifest.Addon addon : manifest.getAddons()) {
|
||||||
|
builder.version(addon.getId(), addon.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
dependents.add(builder.buildAsync());
|
||||||
|
onDone().register(event -> {
|
||||||
|
if (event.isFailed())
|
||||||
|
repository.removeVersionFromDisk(name);
|
||||||
|
});
|
||||||
|
|
||||||
|
ModpackConfiguration<ServerModpackManifest> config = null;
|
||||||
|
try {
|
||||||
|
if (json.exists()) {
|
||||||
|
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<ServerModpackManifest>>() {
|
||||||
|
}.getType());
|
||||||
|
|
||||||
|
if (!MODPACK_TYPE.equals(config.getType()))
|
||||||
|
throw new IllegalArgumentException("Version " + name + " is not a Curse modpack. Cannot update this version.");
|
||||||
|
}
|
||||||
|
} catch (JsonParseException | IOException ignore) {
|
||||||
|
}
|
||||||
|
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/overrides", any -> true, config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task<?>> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/overrides", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String MODPACK_TYPE = "Server";
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.mod.server;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||||
|
import org.jackhuang.hmcl.util.gson.Validation;
|
||||||
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
|
||||||
|
|
||||||
|
public class ServerModpackManifest implements Validation {
|
||||||
|
private final String name;
|
||||||
|
private final String author;
|
||||||
|
private final String version;
|
||||||
|
private final String description;
|
||||||
|
private final String fileApi;
|
||||||
|
private final List<ModpackConfiguration.FileInformation> files;
|
||||||
|
private final List<Addon> addons;
|
||||||
|
|
||||||
|
public ServerModpackManifest() {
|
||||||
|
this("", "", "", "", "", Collections.emptyList(), Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerModpackManifest(String name, String author, String version, String description, String fileApi, List<ModpackConfiguration.FileInformation> files, List<Addon> addons) {
|
||||||
|
this.name = name;
|
||||||
|
this.author = author;
|
||||||
|
this.version = version;
|
||||||
|
this.description = description;
|
||||||
|
this.fileApi = fileApi;
|
||||||
|
this.files = files;
|
||||||
|
this.addons = addons;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileApi() {
|
||||||
|
return fileApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ModpackConfiguration.FileInformation> getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Addon> getAddons() {
|
||||||
|
return addons;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException, TolerableValidationException {
|
||||||
|
if (fileApi == null)
|
||||||
|
throw new JsonParseException("ServerModpackManifest.fileApi cannot be blank");
|
||||||
|
if (files == null)
|
||||||
|
throw new JsonParseException("ServerModpackManifest.files cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Addon {
|
||||||
|
private final String id;
|
||||||
|
private final String version;
|
||||||
|
|
||||||
|
public Addon() {
|
||||||
|
this("", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Addon(String id, String version) {
|
||||||
|
this.id = id;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param zip the CurseForge modpack file.
|
||||||
|
* @throws IOException if the file is not a valid zip file.
|
||||||
|
* @throws JsonParseException if the server-manifest.json is missing or malformed.
|
||||||
|
* @return the manifest.
|
||||||
|
*/
|
||||||
|
public static Modpack readManifest(Path zip, Charset encoding) throws IOException, JsonParseException {
|
||||||
|
String json = CompressingUtils.readTextZipEntry(zip, "server-manifest.json", encoding);
|
||||||
|
ServerModpackManifest manifest = JsonUtils.fromNonNullJson(json, ServerModpackManifest.class);
|
||||||
|
String gameVersion = manifest.getAddons().stream().filter(x -> MINECRAFT.getPatchId().equals(x.getId())).findAny().orElseThrow(() -> new IOException("Cannot find game version")).getVersion();
|
||||||
|
return new Modpack(manifest.getName(), manifest.getAuthor(), manifest.getVersion(), gameVersion, manifest.getDescription(), encoding, manifest);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.util.function;
|
||||||
|
|
||||||
|
public interface ExceptionalPredicate<T, E extends Exception> {
|
||||||
|
boolean test(T t) throws E;
|
||||||
|
}
|
@ -18,6 +18,8 @@
|
|||||||
package org.jackhuang.hmcl.util.io;
|
package org.jackhuang.hmcl.util.io;
|
||||||
|
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.function.ExceptionalPredicate;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -25,7 +27,6 @@ import java.io.InputStream;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Non thread-safe
|
* Non thread-safe
|
||||||
@ -67,7 +68,7 @@ public final class Zipper implements Closeable {
|
|||||||
* @param targetDir the path of the directory in this zip file.
|
* @param targetDir the path of the directory in this zip file.
|
||||||
* @param filter returns false if you do not want that file or directory
|
* @param filter returns false if you do not want that file or directory
|
||||||
*/
|
*/
|
||||||
public void putDirectory(Path source, String targetDir, Predicate<String> filter) throws IOException {
|
public void putDirectory(Path source, String targetDir, ExceptionalPredicate<String, IOException> filter) throws IOException {
|
||||||
Path root = fs.getPath(targetDir);
|
Path root = fs.getPath(targetDir);
|
||||||
Files.createDirectories(root);
|
Files.createDirectories(root);
|
||||||
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
|
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user