feat: MCBBS modpack
This commit is contained in:
@@ -19,13 +19,24 @@ package org.jackhuang.hmcl.game;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
import org.jackhuang.hmcl.Metadata;
|
||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.mod.Modpack;
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
|
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackLocalInstallTask;
|
||||||
|
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
|
import org.jackhuang.hmcl.setting.ProxyManager;
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
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 org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -33,6 +44,7 @@ import java.nio.file.Files;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.newImage;
|
import static org.jackhuang.hmcl.ui.FXUtils.newImage;
|
||||||
|
|
||||||
public class HMCLGameRepository extends DefaultGameRepository {
|
public class HMCLGameRepository extends DefaultGameRepository {
|
||||||
@@ -273,6 +285,54 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
|||||||
return FORBIDDEN.contains(id);
|
return FORBIDDEN.contains(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LaunchOptions getLaunchOptions(String version, File gameDir, boolean checkJava) throws InterruptedException {
|
||||||
|
VersionSetting vs = getVersionSetting(version);
|
||||||
|
|
||||||
|
JavaVersion javaVersion = Optional.ofNullable(vs.getJavaVersion(checkJava)).orElse(JavaVersion.fromCurrentEnvironment());
|
||||||
|
LaunchOptions.Builder builder = new LaunchOptions.Builder()
|
||||||
|
.setGameDir(gameDir)
|
||||||
|
.setJava(javaVersion)
|
||||||
|
.setVersionName(Metadata.TITLE)
|
||||||
|
.setVersionType(Metadata.TITLE)
|
||||||
|
.setProfileName(Metadata.TITLE)
|
||||||
|
.setGameArguments(StringUtils.tokenize(vs.getMinecraftArgs()))
|
||||||
|
.setJavaArguments(StringUtils.tokenize(vs.getJavaArgs()))
|
||||||
|
.setMaxMemory(vs.getMaxMemory())
|
||||||
|
.setMinMemory(vs.getMinMemory())
|
||||||
|
.setMetaspace(Lang.toIntOrNull(vs.getPermSize()))
|
||||||
|
.setWidth(vs.getWidth())
|
||||||
|
.setHeight(vs.getHeight())
|
||||||
|
.setFullscreen(vs.isFullscreen())
|
||||||
|
.setServerIp(vs.getServerIp())
|
||||||
|
.setWrapper(vs.getWrapper())
|
||||||
|
.setPrecalledCommand(vs.getPreLaunchCommand())
|
||||||
|
.setNoGeneratedJVMArgs(vs.isNoJVMArgs());
|
||||||
|
if (config().hasProxy()) {
|
||||||
|
builder.setProxy(ProxyManager.getProxy());
|
||||||
|
if (config().hasProxyAuth()) {
|
||||||
|
builder.setProxyUser(config().getProxyUser());
|
||||||
|
builder.setProxyPass(config().getProxyPass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File json = getModpackConfiguration(version);
|
||||||
|
if (json.exists()) {
|
||||||
|
try {
|
||||||
|
String jsonText = FileUtils.readText(json);
|
||||||
|
ModpackConfiguration<?> modpackConfiguration = JsonUtils.GSON.fromJson(jsonText, ModpackConfiguration.class);
|
||||||
|
if (McbbsModpackLocalInstallTask.MODPACK_TYPE.equals(modpackConfiguration.getType())) {
|
||||||
|
ModpackConfiguration<McbbsModpackManifest> config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<McbbsModpackManifest>>() {
|
||||||
|
}.getType());
|
||||||
|
config.getManifest().injectLaunchOptions(builder);
|
||||||
|
}
|
||||||
|
} catch (IOException | JsonParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getModpackConfiguration(String version) {
|
public File getModpackConfiguration(String version) {
|
||||||
return new File(getVersionRoot(version), "modpack.cfg");
|
return new File(getVersionRoot(version), "modpack.cfg");
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher
|
|
||||||
* Copyright (C) 2020 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.game;
|
|
||||||
|
|
||||||
import org.jackhuang.hmcl.mod.ModAdviser;
|
|
||||||
import org.jackhuang.hmcl.mod.Modpack;
|
|
||||||
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.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the game to a mod pack file.
|
|
||||||
*/
|
|
||||||
public class HMCLModpackExportTask extends Task<Void> {
|
|
||||||
private final DefaultGameRepository repository;
|
|
||||||
private final String version;
|
|
||||||
private final List<String> whitelist;
|
|
||||||
private final Modpack modpack;
|
|
||||||
private final File output;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param output mod pack file.
|
|
||||||
* @param version to locate version.json
|
|
||||||
*/
|
|
||||||
public HMCLModpackExportTask(DefaultGameRepository repository, String version, List<String> whitelist, Modpack modpack, File output) {
|
|
||||||
this.repository = repository;
|
|
||||||
this.version = version;
|
|
||||||
this.whitelist = whitelist;
|
|
||||||
this.modpack = modpack;
|
|
||||||
this.output = output;
|
|
||||||
|
|
||||||
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(version + ".jar");
|
|
||||||
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");
|
|
||||||
try (Zipper zip = new Zipper(output.toPath())) {
|
|
||||||
zip.putDirectory(repository.getRunDirectory(version).toPath(), "minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
|
|
||||||
|
|
||||||
Version mv = repository.getResolvedPreservingPatchesVersion(version);
|
|
||||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version))
|
|
||||||
.orElseThrow(() -> new IOException("Cannot parse the version of " + version));
|
|
||||||
zip.putTextFile(JsonUtils.GSON.toJson(mv.setJar(gameVersion)), "minecraft/pack.json"); // Making "jar" to gameVersion is to be compatible with old HMCL.
|
|
||||||
zip.putTextFile(JsonUtils.GSON.toJson(modpack.setGameVersion(gameVersion)), "modpack.json"); // Newer HMCL only reads 'gameVersion' field.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -169,7 +169,7 @@ public final class LauncherHelper {
|
|||||||
repository,
|
repository,
|
||||||
version.getPatches().isEmpty() ? repository.getResolvedVersion(selectedVersion) : version,
|
version.getPatches().isEmpty() ? repository.getResolvedVersion(selectedVersion) : version,
|
||||||
authInfo,
|
authInfo,
|
||||||
setting.toLaunchOptions(profile.getGameDir(), !setting.isNotCheckJVM()),
|
repository.getLaunchOptions(selectedVersion, profile.getGameDir(), !setting.isNotCheckJVM()),
|
||||||
launcherVisibility == LauncherVisibility.CLOSE
|
launcherVisibility == LauncherVisibility.CLOSE
|
||||||
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
|
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
|
||||||
: new HMCLProcessListener(repository, selectedVersion, authInfo, launchingLatch, gameVersion.isPresent())
|
: new HMCLProcessListener(repository, selectedVersion, authInfo, launchingLatch, gameVersion.isPresent())
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import org.jackhuang.hmcl.mod.*;
|
|||||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionException;
|
import org.jackhuang.hmcl.mod.curse.CurseCompletionException;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
|
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.mcbbs.McbbsModpackLocalInstallTask;
|
||||||
|
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest;
|
||||||
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.ServerModpackLocalInstallTask;
|
import org.jackhuang.hmcl.mod.server.ServerModpackLocalInstallTask;
|
||||||
@@ -43,12 +45,20 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class ModpackHelper {
|
public final class ModpackHelper {
|
||||||
private ModpackHelper() {}
|
private ModpackHelper() {}
|
||||||
|
|
||||||
public static Modpack readModpackManifest(Path file, Charset charset) throws UnsupportedModpackException {
|
public static Modpack readModpackManifest(Path file, Charset charset) throws UnsupportedModpackException {
|
||||||
|
try {
|
||||||
|
return McbbsModpackManifest.readManifest(file, charset);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
ignored.printStackTrace();
|
||||||
|
// ignore it, not a valid MCBBS modpack.
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return CurseManifest.readCurseForgeModpackManifest(file, charset);
|
return CurseManifest.readCurseForgeModpackManifest(file, charset);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -148,12 +158,16 @@ public final class ModpackHelper {
|
|||||||
return new HMCLModpackInstallTask(profile, zipFile, modpack, name)
|
return new HMCLModpackInstallTask(profile, zipFile, modpack, name)
|
||||||
.whenComplete(Schedulers.defaultScheduler(), success, failure);
|
.whenComplete(Schedulers.defaultScheduler(), success, failure);
|
||||||
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
|
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
|
||||||
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(createMultiMCPostInstallTask(profile, (MultiMCInstanceConfiguration) modpack.getManifest(), name));
|
||||||
else if (modpack.getManifest() instanceof ServerModpackManifest)
|
else if (modpack.getManifest() instanceof ServerModpackManifest)
|
||||||
return new ServerModpackLocalInstallTask(profile.getDependency(), zipFile, modpack, ((ServerModpackManifest) modpack.getManifest()), name)
|
return new ServerModpackLocalInstallTask(profile.getDependency(), zipFile, modpack, (ServerModpackManifest) modpack.getManifest(), name)
|
||||||
.whenComplete(Schedulers.defaultScheduler(), success, failure);
|
.whenComplete(Schedulers.defaultScheduler(), success, failure);
|
||||||
|
else if (modpack.getManifest() instanceof McbbsModpackManifest)
|
||||||
|
return new McbbsModpackLocalInstallTask(profile.getDependency(), zipFile, modpack, (McbbsModpackManifest) modpack.getManifest(), name)
|
||||||
|
.whenComplete(Schedulers.defaultScheduler(), success, failure)
|
||||||
|
.thenComposeAsync(createMcbbsPostInstallTask(profile, (McbbsModpackManifest) modpack.getManifest(), name));
|
||||||
else throw new IllegalArgumentException("Unrecognized modpack: " + modpack.getManifest());
|
else throw new IllegalArgumentException("Unrecognized modpack: " + modpack.getManifest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,5 +246,18 @@ public final class ModpackHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Task<Void> createMultiMCPostInstallTask(Profile profile, MultiMCInstanceConfiguration manifest, String version) {
|
||||||
|
return Task.runAsync(Schedulers.javafx(), () -> {
|
||||||
|
VersionSetting vs = Objects.requireNonNull(profile.getRepository().specializeVersionSetting(version));
|
||||||
|
ModpackHelper.toVersionSetting(manifest, vs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<Void> createMcbbsPostInstallTask(Profile profile, McbbsModpackManifest manifest, String version) {
|
||||||
|
return Task.runAsync(Schedulers.javafx(), () -> {
|
||||||
|
VersionSetting vs = Objects.requireNonNull(profile.getRepository().specializeVersionSetting(version));
|
||||||
|
if (manifest.getLaunchInfo().getMinMemory() > vs.getMaxMemory())
|
||||||
|
vs.setMaxMemory(manifest.getLaunchInfo().getMinMemory());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher
|
|
||||||
* Copyright (C) 2020 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.game;
|
|
||||||
|
|
||||||
import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration;
|
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
|
||||||
import org.jackhuang.hmcl.task.Task;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public final class MultiMCInstallVersionSettingTask extends Task<Void> {
|
|
||||||
private final Profile profile;
|
|
||||||
private final MultiMCInstanceConfiguration manifest;
|
|
||||||
private final String version;
|
|
||||||
|
|
||||||
public MultiMCInstallVersionSettingTask(Profile profile, MultiMCInstanceConfiguration manifest, String version) {
|
|
||||||
this.profile = profile;
|
|
||||||
this.manifest = manifest;
|
|
||||||
this.version = version;
|
|
||||||
|
|
||||||
setExecutor(Schedulers.javafx());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute() {
|
|
||||||
VersionSetting vs = Objects.requireNonNull(profile.getRepository().specializeVersionSetting(version));
|
|
||||||
ModpackHelper.toVersionSetting(manifest, vs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,16 +21,13 @@ import com.google.gson.*;
|
|||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import org.jackhuang.hmcl.Metadata;
|
|
||||||
import org.jackhuang.hmcl.game.GameDirectoryType;
|
import org.jackhuang.hmcl.game.GameDirectoryType;
|
||||||
import org.jackhuang.hmcl.game.LaunchOptions;
|
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||||
import org.jackhuang.hmcl.util.platform.Platform;
|
import org.jackhuang.hmcl.util.platform.Platform;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.nio.file.InvalidPathException;
|
import java.nio.file.InvalidPathException;
|
||||||
@@ -39,8 +36,6 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
@@ -533,36 +528,6 @@ public final class VersionSetting implements Cloneable {
|
|||||||
defaultJavaPathProperty.addListener(listener);
|
defaultJavaPathProperty.addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LaunchOptions toLaunchOptions(File gameDir, boolean checkJava) throws InterruptedException {
|
|
||||||
JavaVersion javaVersion = Optional.ofNullable(getJavaVersion(checkJava)).orElse(JavaVersion.fromCurrentEnvironment());
|
|
||||||
LaunchOptions.Builder builder = new LaunchOptions.Builder()
|
|
||||||
.setGameDir(gameDir)
|
|
||||||
.setJava(javaVersion)
|
|
||||||
.setVersionName(Metadata.TITLE)
|
|
||||||
.setVersionType(Metadata.TITLE)
|
|
||||||
.setProfileName(Metadata.TITLE)
|
|
||||||
.setMinecraftArgs(getMinecraftArgs())
|
|
||||||
.setJavaArgs(getJavaArgs())
|
|
||||||
.setMaxMemory(getMaxMemory())
|
|
||||||
.setMinMemory(getMinMemory())
|
|
||||||
.setMetaspace(Lang.toIntOrNull(getPermSize()))
|
|
||||||
.setWidth(getWidth())
|
|
||||||
.setHeight(getHeight())
|
|
||||||
.setFullscreen(isFullscreen())
|
|
||||||
.setServerIp(getServerIp())
|
|
||||||
.setWrapper(getWrapper())
|
|
||||||
.setPrecalledCommand(getPreLaunchCommand())
|
|
||||||
.setNoGeneratedJVMArgs(isNoJVMArgs());
|
|
||||||
if (config().hasProxy()) {
|
|
||||||
builder.setProxy(ProxyManager.getProxy());
|
|
||||||
if (config().hasProxyAuth()) {
|
|
||||||
builder.setProxyUser(config().getProxyUser());
|
|
||||||
builder.setProxyPass(config().getProxyPass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VersionSetting clone() {
|
public VersionSetting clone() {
|
||||||
VersionSetting versionSetting = new VersionSetting();
|
VersionSetting versionSetting = new VersionSetting();
|
||||||
|
|||||||
@@ -17,9 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui;
|
package org.jackhuang.hmcl.ui;
|
||||||
|
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.concurrent.Worker;
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.web.WebEngine;
|
import javafx.scene.web.WebEngine;
|
||||||
import javafx.scene.web.WebView;
|
import javafx.scene.web.WebView;
|
||||||
@@ -41,7 +38,6 @@ public class WebStage extends Stage {
|
|||||||
getScene().getStylesheets().addAll(config().getTheme().getStylesheets());
|
getScene().getStylesheets().addAll(config().getTheme().getStylesheets());
|
||||||
getIcons().add(newImage("/assets/img/icon.png"));
|
getIcons().add(newImage("/assets/img/icon.png"));
|
||||||
webView.setContextMenuEnabled(false);
|
webView.setContextMenuEnabled(false);
|
||||||
titleProperty().bind(webEngine.titleProperty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebView getWebView() {
|
public WebView getWebView() {
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class MicrosoftAccountLoginStage extends WebStage implements MicrosoftSer
|
|||||||
super(600, 600);
|
super(600, 600);
|
||||||
initModality(Modality.APPLICATION_MODAL);
|
initModality(Modality.APPLICATION_MODAL);
|
||||||
|
|
||||||
|
titleProperty().bind(webEngine.titleProperty());
|
||||||
|
|
||||||
webEngine.locationProperty().addListener((observable, oldValue, newValue) -> {
|
webEngine.locationProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
if (urlTester != null && urlTester.test(newValue)) {
|
if (urlTester != null && urlTester.test(newValue)) {
|
||||||
future.complete(newValue);
|
future.complete(newValue);
|
||||||
|
|||||||
@@ -34,14 +34,16 @@ import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
|||||||
import org.jackhuang.hmcl.download.game.GameInstallTask;
|
import org.jackhuang.hmcl.download.game.GameInstallTask;
|
||||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
|
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
|
||||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||||
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
|
|
||||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
|
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
|
||||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||||
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||||
import org.jackhuang.hmcl.mod.ModpackUpdateTask;
|
import org.jackhuang.hmcl.mod.ModpackUpdateTask;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
|
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
|
import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
|
||||||
|
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask;
|
||||||
|
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask;
|
||||||
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
|
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
|
||||||
|
import org.jackhuang.hmcl.mod.server.ServerModpackExportTask;
|
||||||
import org.jackhuang.hmcl.setting.Theme;
|
import org.jackhuang.hmcl.setting.Theme;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||||
@@ -128,7 +130,7 @@ public final class TaskListPane extends StackPane {
|
|||||||
task.setName(i18n("modpack.install", i18n("modpack.type.multimc")));
|
task.setName(i18n("modpack.install", i18n("modpack.type.multimc")));
|
||||||
} else if (task instanceof HMCLModpackInstallTask) {
|
} else if (task instanceof HMCLModpackInstallTask) {
|
||||||
task.setName(i18n("modpack.install", i18n("modpack.type.hmcl")));
|
task.setName(i18n("modpack.install", i18n("modpack.type.hmcl")));
|
||||||
} else if (task instanceof HMCLModpackExportTask) {
|
} else if (task instanceof McbbsModpackExportTask || task instanceof MultiMCModpackExportTask || task instanceof ServerModpackExportTask) {
|
||||||
task.setName(i18n("modpack.export"));
|
task.setName(i18n("modpack.export"));
|
||||||
} else if (task instanceof MinecraftInstanceTask) {
|
} else if (task instanceof MinecraftInstanceTask) {
|
||||||
task.setName(i18n("modpack.scan"));
|
task.setName(i18n("modpack.scan"));
|
||||||
|
|||||||
@@ -19,32 +19,25 @@ package org.jackhuang.hmcl.ui.export;
|
|||||||
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import org.jackhuang.hmcl.Launcher;
|
import org.jackhuang.hmcl.Launcher;
|
||||||
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
|
|
||||||
import org.jackhuang.hmcl.mod.ModAdviser;
|
import org.jackhuang.hmcl.mod.ModAdviser;
|
||||||
import org.jackhuang.hmcl.mod.Modpack;
|
import org.jackhuang.hmcl.mod.ModpackExportInfo;
|
||||||
|
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.setting.Config;
|
|
||||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.io.Zipper;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
|
||||||
|
|
||||||
public final class ExportWizardProvider implements WizardProvider {
|
public final class ExportWizardProvider implements WizardProvider {
|
||||||
private final Profile profile;
|
private final Profile profile;
|
||||||
private final String version;
|
private final String version;
|
||||||
@@ -62,73 +55,31 @@ public final class ExportWizardProvider implements WizardProvider {
|
|||||||
public Object finish(Map<String, Object> settings) {
|
public Object finish(Map<String, Object> settings) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<String> whitelist = (List<String>) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION);
|
List<String> whitelist = (List<String>) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION);
|
||||||
File modpackFile = (File) settings.get(ModpackInfoPage.MODPACK_FILE);
|
ModpackExportInfo exportInfo = (ModpackExportInfo) settings.get(ModpackInfoPage.MODPACK_INFO);
|
||||||
String modpackName = (String) settings.get(ModpackInfoPage.MODPACK_NAME);
|
exportInfo.setWhitelist(whitelist);
|
||||||
String modpackAuthor = (String) settings.get(ModpackInfoPage.MODPACK_AUTHOR);
|
|
||||||
String modpackFileApi = (String) settings.get(ModpackInfoPage.MODPACK_FILE_API);
|
|
||||||
String modpackVersion = (String) settings.get(ModpackInfoPage.MODPACK_VERSION);
|
|
||||||
String modpackDescription = (String) settings.get(ModpackInfoPage.MODPACK_DESCRIPTION);
|
|
||||||
String modpackType = (String) settings.get(ModpackTypeSelectionPage.MODPACK_TYPE);
|
String modpackType = (String) settings.get(ModpackTypeSelectionPage.MODPACK_TYPE);
|
||||||
boolean includeLauncher = (Boolean) settings.get(ModpackInfoPage.MODPACK_INCLUDE_LAUNCHER);
|
|
||||||
|
|
||||||
switch (modpackType) {
|
switch (modpackType) {
|
||||||
case ModpackTypeSelectionPage.MODPACK_TYPE_HMCL:
|
case ModpackTypeSelectionPage.MODPACK_TYPE_MCBBS:
|
||||||
return exportAsHMCL(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, includeLauncher);
|
return exportAsMcbbs(exportInfo);
|
||||||
case ModpackTypeSelectionPage.MODPACK_TYPE_MULTIMC:
|
case ModpackTypeSelectionPage.MODPACK_TYPE_MULTIMC:
|
||||||
return exportAsMultiMC(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription);
|
return exportAsMultiMC(exportInfo);
|
||||||
case ModpackTypeSelectionPage.MODPACK_TYPE_SERVER:
|
case ModpackTypeSelectionPage.MODPACK_TYPE_SERVER:
|
||||||
return exportAsServer(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, modpackFileApi);
|
return exportAsServer(exportInfo);
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unrecognized modpack type " + modpackType);
|
throw new IllegalStateException("Unrecognized modpack type " + modpackType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<?> exportAsHMCL(List<String> whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, boolean includeLauncherRaw) {
|
private Task<?> exportAsMcbbs(ModpackExportInfo exportInfo) {
|
||||||
List<File> launcherJar = Launcher.getCurrentJarFiles();
|
List<File> launcherJar = Launcher.getCurrentJarFiles();
|
||||||
boolean includeLauncher = includeLauncherRaw && launcherJar != null;
|
|
||||||
|
|
||||||
return new Task<Void>() {
|
return new Task<Void>() {
|
||||||
Task<?> dependency = null;
|
Task<?> dependency = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() {
|
||||||
File tempModpack = includeLauncher ? Files.createTempFile("hmcl", ".zip").toFile() : modpackFile;
|
dependency = new McbbsModpackExportTask(profile.getRepository(), version, exportInfo);
|
||||||
|
|
||||||
dependency = new HMCLModpackExportTask(profile.getRepository(), version, whitelist,
|
|
||||||
new Modpack(modpackName, modpackAuthor, modpackVersion, null, modpackDescription, StandardCharsets.UTF_8, null), tempModpack);
|
|
||||||
|
|
||||||
if (includeLauncher) {
|
|
||||||
dependency = dependency.thenRunAsync(() -> {
|
|
||||||
try (Zipper zip = new Zipper(modpackFile.toPath())) {
|
|
||||||
Config exported = new Config();
|
|
||||||
|
|
||||||
exported.setBackgroundImageType(config().getBackgroundImageType());
|
|
||||||
exported.setBackgroundImage(config().getBackgroundImage());
|
|
||||||
exported.setTheme(config().getTheme());
|
|
||||||
exported.setDownloadType(config().getDownloadType());
|
|
||||||
exported.setPreferredLoginType(config().getPreferredLoginType());
|
|
||||||
exported.getAuthlibInjectorServers().setAll(config().getAuthlibInjectorServers());
|
|
||||||
|
|
||||||
zip.putTextFile(exported.toJson(), ConfigHolder.CONFIG_FILENAME);
|
|
||||||
zip.putFile(tempModpack, "modpack.zip");
|
|
||||||
|
|
||||||
File bg = new File("bg").getAbsoluteFile();
|
|
||||||
if (bg.isDirectory())
|
|
||||||
zip.putDirectory(bg.toPath(), "bg");
|
|
||||||
|
|
||||||
File background_png = new File("background.png").getAbsoluteFile();
|
|
||||||
if (background_png.isFile())
|
|
||||||
zip.putFile(background_png, "background.png");
|
|
||||||
|
|
||||||
File background_jpg = new File("background.jpg").getAbsoluteFile();
|
|
||||||
if (background_jpg.isFile())
|
|
||||||
zip.putFile(background_jpg, "background.jpg");
|
|
||||||
|
|
||||||
for (File jar : launcherJar)
|
|
||||||
zip.putFile(jar, jar.getName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -138,30 +89,30 @@ public final class ExportWizardProvider implements WizardProvider {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<?> exportAsMultiMC(List<String> whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription) {
|
private Task<?> exportAsMultiMC(ModpackExportInfo exportInfo) {
|
||||||
return new Task<Void>() {
|
return new Task<Void>() {
|
||||||
Task<?> dependency;
|
Task<?> dependency;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
VersionSetting vs = profile.getVersionSetting(version);
|
VersionSetting vs = profile.getVersionSetting(version);
|
||||||
dependency = new MultiMCModpackExportTask(profile.getRepository(), version, whitelist,
|
dependency = new MultiMCModpackExportTask(profile.getRepository(), version, exportInfo.getWhitelist(),
|
||||||
new MultiMCInstanceConfiguration(
|
new MultiMCInstanceConfiguration(
|
||||||
"OneSix",
|
"OneSix",
|
||||||
modpackName + "-" + modpackVersion,
|
exportInfo.getName() + "-" + exportInfo.getVersion(),
|
||||||
null,
|
null,
|
||||||
Lang.toIntOrNull(vs.getPermSize()),
|
Lang.toIntOrNull(vs.getPermSize()),
|
||||||
vs.getWrapper(),
|
vs.getWrapper(),
|
||||||
vs.getPreLaunchCommand(),
|
vs.getPreLaunchCommand(),
|
||||||
null,
|
null,
|
||||||
modpackDescription,
|
exportInfo.getDescription(),
|
||||||
null,
|
null,
|
||||||
vs.getJavaArgs(),
|
exportInfo.getJavaArguments(),
|
||||||
vs.isFullscreen(),
|
vs.isFullscreen(),
|
||||||
vs.getWidth(),
|
vs.getWidth(),
|
||||||
vs.getHeight(),
|
vs.getHeight(),
|
||||||
vs.getMaxMemory(),
|
vs.getMaxMemory(),
|
||||||
vs.getMinMemory(),
|
exportInfo.getMinMemory(),
|
||||||
vs.isShowLogs(),
|
vs.isShowLogs(),
|
||||||
/* showConsoleOnError */ true,
|
/* showConsoleOnError */ true,
|
||||||
/* autoCloseConsole */ false,
|
/* autoCloseConsole */ false,
|
||||||
@@ -171,7 +122,7 @@ public final class ExportWizardProvider implements WizardProvider {
|
|||||||
/* overrideConsole */ true,
|
/* overrideConsole */ true,
|
||||||
/* overrideCommands */ true,
|
/* overrideCommands */ true,
|
||||||
/* overrideWindow */ true
|
/* overrideWindow */ true
|
||||||
), modpackFile);
|
), exportInfo.getOutput().toFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -181,13 +132,13 @@ public final class ExportWizardProvider implements WizardProvider {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<?> exportAsServer(List<String> whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, String modpackFileApi) {
|
private Task<?> exportAsServer(ModpackExportInfo exportInfo) {
|
||||||
return new Task<Void>() {
|
return new Task<Void>() {
|
||||||
Task<?> dependency;
|
Task<?> dependency;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
dependency = new ServerModpackExportTask(profile.getRepository(), version, whitelist, modpackName, modpackAuthor, modpackVersion, modpackDescription, modpackFileApi, modpackFile);
|
dependency = new ServerModpackExportTask(profile.getRepository(), version, exportInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -203,7 +154,7 @@ public final class ExportWizardProvider implements WizardProvider {
|
|||||||
case 0:
|
case 0:
|
||||||
return new ModpackTypeSelectionPage(controller);
|
return new ModpackTypeSelectionPage(controller);
|
||||||
case 1:
|
case 1:
|
||||||
return new ModpackInfoPage(controller, version);
|
return new ModpackInfoPage(controller, profile.getRepository(), version);
|
||||||
case 2:
|
case 2:
|
||||||
return new ModpackFileSelectionPage(controller, profile, version, ModAdviser::suggestMod);
|
return new ModpackFileSelectionPage(controller, profile, version, ModAdviser::suggestMod);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -17,76 +17,100 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.export;
|
package org.jackhuang.hmcl.ui.export;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.*;
|
||||||
import com.jfoenix.controls.JFXTextArea;
|
|
||||||
import com.jfoenix.controls.JFXTextField;
|
|
||||||
import com.jfoenix.controls.JFXToggleButton;
|
|
||||||
import com.jfoenix.validation.RequiredFieldValidator;
|
import com.jfoenix.validation.RequiredFieldValidator;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.TextAlignment;
|
import javafx.scene.text.TextAlignment;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.Launcher;
|
import org.jackhuang.hmcl.Launcher;
|
||||||
import org.jackhuang.hmcl.auth.Account;
|
import org.jackhuang.hmcl.auth.Account;
|
||||||
|
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||||
|
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackExportInfo;
|
||||||
|
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest;
|
||||||
import org.jackhuang.hmcl.setting.Accounts;
|
import org.jackhuang.hmcl.setting.Accounts;
|
||||||
|
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.NumberValidator;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.*;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
|
import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory;
|
||||||
|
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_SERVER;
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public final class ModpackInfoPage extends Control implements WizardPage {
|
public final class ModpackInfoPage extends Control implements WizardPage {
|
||||||
private final WizardController controller;
|
private final WizardController controller;
|
||||||
|
private final HMCLGameRepository gameRepository;
|
||||||
|
private final ModpackExportInfo.Options options;
|
||||||
|
private final String versionName;
|
||||||
private final boolean canIncludeLauncher;
|
private final boolean canIncludeLauncher;
|
||||||
private final boolean showFileApi;
|
|
||||||
|
|
||||||
private SimpleStringProperty versionName = new SimpleStringProperty();
|
private final ModpackExportInfo exportInfo = new ModpackExportInfo();
|
||||||
private SimpleStringProperty modpackName = new SimpleStringProperty();
|
|
||||||
private SimpleStringProperty modpackFileApi = new SimpleStringProperty();
|
|
||||||
private SimpleStringProperty modpackAuthor = new SimpleStringProperty();
|
|
||||||
private SimpleStringProperty modpackVersion = new SimpleStringProperty("1.0");
|
|
||||||
private SimpleStringProperty modpackDescription = new SimpleStringProperty();
|
|
||||||
private SimpleBooleanProperty includingLauncher = new SimpleBooleanProperty();
|
|
||||||
private ObjectProperty<EventHandler<? super MouseEvent>> next = new SimpleObjectProperty<>();
|
|
||||||
|
|
||||||
public ModpackInfoPage(WizardController controller, String version) {
|
private final SimpleStringProperty name = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty author = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty version = new SimpleStringProperty("1.0");
|
||||||
|
private final SimpleStringProperty description = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty url = new SimpleStringProperty("");
|
||||||
|
private final SimpleBooleanProperty forceUpdate = new SimpleBooleanProperty();
|
||||||
|
private final SimpleBooleanProperty packWithLauncher = new SimpleBooleanProperty();
|
||||||
|
private final SimpleStringProperty fileApi = new SimpleStringProperty();
|
||||||
|
private final SimpleIntegerProperty minMemory = new SimpleIntegerProperty(0);
|
||||||
|
private final SimpleStringProperty authlibInjectorServer = new SimpleStringProperty();
|
||||||
|
private final SimpleStringProperty launchArguments = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty javaArguments = new SimpleStringProperty("");
|
||||||
|
private final ObjectProperty<EventHandler<? super MouseEvent>> next = new SimpleObjectProperty<>();
|
||||||
|
private final SimpleStringProperty mcbbsThreadId = new SimpleStringProperty();
|
||||||
|
|
||||||
|
public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
modpackName.set(version);
|
this.gameRepository = gameRepository;
|
||||||
modpackAuthor.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse(""));
|
this.options = tryCast(controller.getSettings().get(MODPACK_INFO_OPTION), ModpackExportInfo.Options.class)
|
||||||
versionName.set(version);
|
.orElseThrow(() -> new IllegalArgumentException("Settings.MODPACK_INFO_OPTION is required"));
|
||||||
|
this.versionName = version;
|
||||||
|
|
||||||
|
name.set(version);
|
||||||
|
author.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse(""));
|
||||||
|
|
||||||
|
VersionSetting versionSetting = gameRepository.getVersionSetting(versionName);
|
||||||
|
minMemory.set(Optional.ofNullable(versionSetting.getMinMemory()).orElse(0));
|
||||||
|
launchArguments.set(versionSetting.getMinecraftArgs());
|
||||||
|
javaArguments.set(versionSetting.getJavaArgs());
|
||||||
|
|
||||||
List<File> launcherJar = Launcher.getCurrentJarFiles();
|
List<File> launcherJar = Launcher.getCurrentJarFiles();
|
||||||
canIncludeLauncher = launcherJar != null;
|
canIncludeLauncher = launcherJar != null;
|
||||||
showFileApi = controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_SERVER;
|
|
||||||
|
|
||||||
next.set(e -> onNext());
|
next.set(e -> onNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
|
||||||
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"));
|
||||||
@@ -96,24 +120,35 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
controller.onEnd();
|
controller.onEnd();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
controller.getSettings().put(MODPACK_NAME, modpackName.get());
|
|
||||||
controller.getSettings().put(MODPACK_FILE_API, modpackFileApi.get());
|
exportInfo.setName(name.get());
|
||||||
controller.getSettings().put(MODPACK_VERSION, modpackVersion.get());
|
exportInfo.setFileApi(fileApi.get());
|
||||||
controller.getSettings().put(MODPACK_AUTHOR, modpackAuthor.get());
|
exportInfo.setVersion(version.get());
|
||||||
controller.getSettings().put(MODPACK_FILE, file);
|
exportInfo.setAuthor(author.get());
|
||||||
controller.getSettings().put(MODPACK_DESCRIPTION, modpackDescription.get());
|
exportInfo.setOutput(file.toPath());
|
||||||
controller.getSettings().put(MODPACK_INCLUDE_LAUNCHER, includingLauncher.get());
|
exportInfo.setDescription(description.get());
|
||||||
|
exportInfo.setPackWithLauncher(packWithLauncher.get());
|
||||||
|
exportInfo.setUrl(url.get());
|
||||||
|
exportInfo.setForceUpdate(forceUpdate.get());
|
||||||
|
exportInfo.setPackWithLauncher(packWithLauncher.get());
|
||||||
|
exportInfo.setMinMemory(minMemory.get());
|
||||||
|
exportInfo.setLaunchArguments(launchArguments.get());
|
||||||
|
exportInfo.setJavaArguments(javaArguments.get());
|
||||||
|
exportInfo.setAuthlibInjectorServer(authlibInjectorServer.get());
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(mcbbsThreadId.get())) {
|
||||||
|
exportInfo.setOrigins(Collections.singletonList(new McbbsModpackManifest.Origin(
|
||||||
|
"mcbbs", Integer.parseInt(mcbbsThreadId.get())
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.getSettings().put(MODPACK_INFO, exportInfo);
|
||||||
controller.onNext();
|
controller.onNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanup(Map<String, Object> settings) {
|
public void cleanup(Map<String, Object> settings) {
|
||||||
controller.getSettings().remove(MODPACK_NAME);
|
controller.getSettings().remove(MODPACK_INFO);
|
||||||
controller.getSettings().remove(MODPACK_VERSION);
|
|
||||||
controller.getSettings().remove(MODPACK_AUTHOR);
|
|
||||||
controller.getSettings().remove(MODPACK_DESCRIPTION);
|
|
||||||
controller.getSettings().remove(MODPACK_INCLUDE_LAUNCHER);
|
|
||||||
controller.getSettings().remove(MODPACK_FILE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,19 +161,11 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
return new ModpackInfoPageSkin(this);
|
return new ModpackInfoPageSkin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String MODPACK_NAME = "modpack.name";
|
public static final String MODPACK_INFO = "modpack.info";
|
||||||
public static final String MODPACK_FILE_API = "modpack.file_api";
|
public static final String MODPACK_INFO_OPTION = "modpack.info.option";
|
||||||
public static final String MODPACK_VERSION = "modpack.version";
|
|
||||||
public static final String MODPACK_AUTHOR = "archive.author";
|
|
||||||
public static final String MODPACK_DESCRIPTION = "modpack.description";
|
|
||||||
public static final String MODPACK_INCLUDE_LAUNCHER = "modpack.include_launcher";
|
|
||||||
public static final String MODPACK_FILE = "modpack.file";
|
|
||||||
|
|
||||||
public static class ModpackInfoPageSkin extends SkinBase<ModpackInfoPage> {
|
public static class ModpackInfoPageSkin extends SkinBase<ModpackInfoPage> {
|
||||||
private final JFXTextField txtModpackName;
|
private ObservableList<Node> originList;
|
||||||
private final JFXTextField txtModpackFileApi;
|
|
||||||
private final JFXTextField txtModpackAuthor;
|
|
||||||
private final JFXTextField txtModpackVersion;
|
|
||||||
|
|
||||||
public ModpackInfoPageSkin(ModpackInfoPage skinnable) {
|
public ModpackInfoPageSkin(ModpackInfoPage skinnable) {
|
||||||
super(skinnable);
|
super(skinnable);
|
||||||
@@ -151,6 +178,8 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
scroll.setFitToHeight(true);
|
scroll.setFitToHeight(true);
|
||||||
getChildren().setAll(scroll);
|
getChildren().setAll(scroll);
|
||||||
|
|
||||||
|
List<JFXTextField> validatingFields = new ArrayList<>();
|
||||||
|
|
||||||
{
|
{
|
||||||
BorderPane borderPane = new BorderPane();
|
BorderPane borderPane = new BorderPane();
|
||||||
borderPane.setStyle("-fx-padding: 16;");
|
borderPane.setStyle("-fx-padding: 16;");
|
||||||
@@ -179,14 +208,14 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
borderPane1.setLeft(new Label(i18n("modpack.wizard.step.initialization.exported_version")));
|
borderPane1.setLeft(new Label(i18n("modpack.wizard.step.initialization.exported_version")));
|
||||||
|
|
||||||
Label versionNameLabel = new Label();
|
Label versionNameLabel = new Label();
|
||||||
versionNameLabel.textProperty().bind(skinnable.versionName);
|
versionNameLabel.setText(skinnable.versionName);
|
||||||
borderPane1.setRight(versionNameLabel);
|
borderPane1.setRight(versionNameLabel);
|
||||||
list.getContent().add(borderPane1);
|
list.getContent().add(borderPane1);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
txtModpackName = new JFXTextField();
|
JFXTextField txtModpackName = new JFXTextField();
|
||||||
txtModpackName.textProperty().bindBidirectional(skinnable.modpackName);
|
txtModpackName.textProperty().bindBidirectional(skinnable.name);
|
||||||
txtModpackName.setLabelFloat(true);
|
txtModpackName.setLabelFloat(true);
|
||||||
txtModpackName.setPromptText(i18n("modpack.name"));
|
txtModpackName.setPromptText(i18n("modpack.name"));
|
||||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||||
@@ -194,16 +223,25 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
txtModpackName.getValidators().add(validator);
|
txtModpackName.getValidators().add(validator);
|
||||||
StackPane.setMargin(txtModpackName, insets);
|
StackPane.setMargin(txtModpackName, insets);
|
||||||
list.getContent().add(txtModpackName);
|
list.getContent().add(txtModpackName);
|
||||||
|
|
||||||
|
validatingFields.add(txtModpackName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skinnable.showFileApi) {
|
if (skinnable.options.isRequireFileApi()) {
|
||||||
txtModpackFileApi = new JFXTextField();
|
JFXTextField txtModpackFileApi = new JFXTextField();
|
||||||
txtModpackFileApi.textProperty().bindBidirectional(skinnable.modpackFileApi);
|
txtModpackFileApi.textProperty().bindBidirectional(skinnable.fileApi);
|
||||||
txtModpackFileApi.setLabelFloat(true);
|
txtModpackFileApi.setLabelFloat(true);
|
||||||
txtModpackFileApi.setPromptText(i18n("modpack.file_api"));
|
txtModpackFileApi.setPromptText(i18n("modpack.file_api"));
|
||||||
|
|
||||||
|
if (skinnable.options.isValidateFileApi()) {
|
||||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||||
txtModpackFileApi.getValidators().add(validator);
|
txtModpackFileApi.getValidators().add(validator);
|
||||||
|
}
|
||||||
|
|
||||||
txtModpackFileApi.getValidators().add(new Validator(s -> {
|
txtModpackFileApi.getValidators().add(new Validator(s -> {
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
new URL(s).toURI();
|
new URL(s).toURI();
|
||||||
return true;
|
return true;
|
||||||
@@ -213,35 +251,39 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
}));
|
}));
|
||||||
StackPane.setMargin(txtModpackFileApi, insets);
|
StackPane.setMargin(txtModpackFileApi, insets);
|
||||||
list.getContent().add(txtModpackFileApi);
|
list.getContent().add(txtModpackFileApi);
|
||||||
} else {
|
|
||||||
txtModpackFileApi = null;
|
validatingFields.add(txtModpackFileApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
txtModpackAuthor = new JFXTextField();
|
JFXTextField txtModpackAuthor = new JFXTextField();
|
||||||
txtModpackAuthor.textProperty().bindBidirectional(skinnable.modpackAuthor);
|
txtModpackAuthor.textProperty().bindBidirectional(skinnable.author);
|
||||||
txtModpackAuthor.setLabelFloat(true);
|
txtModpackAuthor.setLabelFloat(true);
|
||||||
txtModpackAuthor.setPromptText(i18n("archive.author"));
|
txtModpackAuthor.setPromptText(i18n("archive.author"));
|
||||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||||
txtModpackAuthor.getValidators().add(validator);
|
txtModpackAuthor.getValidators().add(validator);
|
||||||
StackPane.setMargin(txtModpackAuthor, insets);
|
StackPane.setMargin(txtModpackAuthor, insets);
|
||||||
list.getContent().add(txtModpackAuthor);
|
list.getContent().add(txtModpackAuthor);
|
||||||
|
|
||||||
|
validatingFields.add(txtModpackAuthor);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
txtModpackVersion = new JFXTextField();
|
JFXTextField txtModpackVersion = new JFXTextField();
|
||||||
txtModpackVersion.textProperty().bindBidirectional(skinnable.modpackVersion);
|
txtModpackVersion.textProperty().bindBidirectional(skinnable.version);
|
||||||
txtModpackVersion.setLabelFloat(true);
|
txtModpackVersion.setLabelFloat(true);
|
||||||
txtModpackVersion.setPromptText(i18n("archive.version"));
|
txtModpackVersion.setPromptText(i18n("archive.version"));
|
||||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||||
txtModpackVersion.getValidators().add(validator);
|
txtModpackVersion.getValidators().add(validator);
|
||||||
StackPane.setMargin(txtModpackVersion, insets);
|
StackPane.setMargin(txtModpackVersion, insets);
|
||||||
list.getContent().add(txtModpackVersion);
|
list.getContent().add(txtModpackVersion);
|
||||||
|
|
||||||
|
validatingFields.add(txtModpackVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
JFXTextArea area = new JFXTextArea();
|
JFXTextArea area = new JFXTextArea();
|
||||||
area.textProperty().bindBidirectional(skinnable.modpackDescription);
|
area.textProperty().bindBidirectional(skinnable.description);
|
||||||
area.setLabelFloat(true);
|
area.setLabelFloat(true);
|
||||||
area.setPromptText(i18n("modpack.desc"));
|
area.setPromptText(i18n("modpack.desc"));
|
||||||
area.setMinHeight(400);
|
area.setMinHeight(400);
|
||||||
@@ -249,18 +291,110 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
list.getContent().add(area);
|
list.getContent().add(area);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_HMCL) {
|
if (skinnable.options.isRequireForceUpdate()) {
|
||||||
BorderPane borderPane1 = new BorderPane();
|
BorderPane pane = new BorderPane();
|
||||||
borderPane1.setLeft(new Label(i18n("modpack.wizard.step.initialization.include_launcher")));
|
pane.setLeft(new Label(i18n("modpack.wizard.step.initialization.force_update")));
|
||||||
list.getContent().add(borderPane1);
|
list.getContent().add(pane);
|
||||||
|
|
||||||
JFXToggleButton button = new JFXToggleButton();
|
JFXToggleButton button = new JFXToggleButton();
|
||||||
button.setDisable(!skinnable.canIncludeLauncher);
|
button.setDisable(!skinnable.canIncludeLauncher);
|
||||||
button.selectedProperty().bindBidirectional(skinnable.includingLauncher);
|
button.selectedProperty().bindBidirectional(skinnable.packWithLauncher);
|
||||||
button.setSize(8);
|
button.setSize(8);
|
||||||
button.setMinHeight(16);
|
button.setMinHeight(16);
|
||||||
button.setMaxHeight(16);
|
button.setMaxHeight(16);
|
||||||
borderPane1.setRight(button);
|
pane.setRight(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skinnable.options.isRequireAuthlibInjectorServer()) {
|
||||||
|
JFXComboBox<AuthlibInjectorServer> cboServers = new JFXComboBox<>();
|
||||||
|
cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl())));
|
||||||
|
cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName));
|
||||||
|
Bindings.bindContent(cboServers.getItems(), config().getAuthlibInjectorServers());
|
||||||
|
|
||||||
|
skinnable.authlibInjectorServer.bind(Bindings.createStringBinding(() ->
|
||||||
|
Optional.ofNullable(cboServers.getSelectionModel().getSelectedItem())
|
||||||
|
.map(AuthlibInjectorServer::getUrl)
|
||||||
|
.orElse(null)));
|
||||||
|
|
||||||
|
BorderPane pane = new BorderPane();
|
||||||
|
|
||||||
|
Label left = new Label(i18n("account.injector.server"));
|
||||||
|
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
|
||||||
|
pane.setLeft(left);
|
||||||
|
pane.setRight(cboServers);
|
||||||
|
|
||||||
|
list.getContent().add(pane);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skinnable.options.isRequireMinMemory()) {
|
||||||
|
JFXTextField txtMinMemory = new JFXTextField();
|
||||||
|
FXUtils.bindInt(txtMinMemory, skinnable.minMemory);
|
||||||
|
txtMinMemory.getValidators().add(new NumberValidator(i18n("input.number"), false));
|
||||||
|
FXUtils.setLimitWidth(txtMinMemory, 300);
|
||||||
|
validatingFields.add(txtMinMemory);
|
||||||
|
|
||||||
|
BorderPane pane = new BorderPane();
|
||||||
|
Label label = new Label(i18n("settings.min_memory"));
|
||||||
|
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
|
||||||
|
pane.setLeft(label);
|
||||||
|
BorderPane.setAlignment(txtMinMemory, Pos.CENTER_RIGHT);
|
||||||
|
pane.setRight(txtMinMemory);
|
||||||
|
|
||||||
|
list.getContent().add(pane);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skinnable.options.isRequireLaunchArguments()) {
|
||||||
|
JFXTextField txtLaunchArguments = new JFXTextField();
|
||||||
|
txtLaunchArguments.textProperty().bindBidirectional(skinnable.launchArguments);
|
||||||
|
txtLaunchArguments.setLabelFloat(true);
|
||||||
|
txtLaunchArguments.setPromptText(i18n("settings.advanced.minecraft_arguments"));
|
||||||
|
StackPane.setMargin(txtLaunchArguments, insets);
|
||||||
|
list.getContent().add(txtLaunchArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skinnable.options.isRequireJavaArguments()) {
|
||||||
|
JFXTextField txtJavaArguments = new JFXTextField();
|
||||||
|
txtJavaArguments.textProperty().bindBidirectional(skinnable.javaArguments);
|
||||||
|
txtJavaArguments.setLabelFloat(true);
|
||||||
|
txtJavaArguments.setPromptText(i18n("settings.advanced.jvm_args"));
|
||||||
|
StackPane.setMargin(txtJavaArguments, insets);
|
||||||
|
list.getContent().add(txtJavaArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skinnable.options.isRequireOrigins() || skinnable.options.isRequireUrl()) {
|
||||||
|
BorderPane originPane = new BorderPane();
|
||||||
|
Label title = new Label(i18n("modpack.origin"));
|
||||||
|
originPane.setTop(title);
|
||||||
|
VBox container = new VBox();
|
||||||
|
BorderPane.setMargin(container, new Insets(0, 0, 0, 16));
|
||||||
|
originPane.setCenter(container);
|
||||||
|
list.getContent().add(originPane);
|
||||||
|
|
||||||
|
if (skinnable.options.isRequireUrl()) {
|
||||||
|
BorderPane pane = new BorderPane();
|
||||||
|
pane.setPadding(new Insets(8, 0, 8, 0));
|
||||||
|
Label left = new Label(i18n("modpack.origin.url"));
|
||||||
|
pane.setLeft(left);
|
||||||
|
JFXTextField txtModpackUrl = new JFXTextField();
|
||||||
|
txtModpackUrl.textProperty().bindBidirectional(skinnable.url);
|
||||||
|
pane.setRight(txtModpackUrl);
|
||||||
|
container.getChildren().add(pane);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skinnable.options.isRequireOrigins()) {
|
||||||
|
BorderPane pane = new BorderPane();
|
||||||
|
pane.setPadding(new Insets(8, 0, 8, 0));
|
||||||
|
Label left = new Label(i18n("modpack.origin.mcbbs"));
|
||||||
|
pane.setLeft(left);
|
||||||
|
JFXTextField txtMcbbs = new JFXTextField();
|
||||||
|
FXUtils.setValidateWhileTextChanged(txtMcbbs, true);
|
||||||
|
txtMcbbs.getValidators().add(new NumberValidator(i18n("input.number"), true));
|
||||||
|
txtMcbbs.textProperty().bindBidirectional(skinnable.mcbbsThreadId);
|
||||||
|
pane.setRight(txtMcbbs);
|
||||||
|
container.getChildren().add(pane);
|
||||||
|
|
||||||
|
validatingFields.add(txtMcbbs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,15 +410,13 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
|||||||
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
|
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
|
||||||
nextButton.setText(i18n("wizard.next"));
|
nextButton.setText(i18n("wizard.next"));
|
||||||
nextButton.getStyleClass().add("jfx-button-raised");
|
nextButton.getStyleClass().add("jfx-button-raised");
|
||||||
if (skinnable.showFileApi) {
|
nextButton.disableProperty().bind(
|
||||||
nextButton.disableProperty().bind(Bindings.createBooleanBinding(() ->
|
// Disable nextButton if any text of JFXTextFields in validatingFields does not fulfill
|
||||||
!txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate() || !txtModpackFileApi.validate(),
|
// our requirement.
|
||||||
txtModpackName.textProperty(), txtModpackAuthor.textProperty(), txtModpackVersion.textProperty(), txtModpackFileApi.textProperty()));
|
Bindings.createBooleanBinding(() -> validatingFields.stream()
|
||||||
} else {
|
.map(field -> !field.validate())
|
||||||
nextButton.disableProperty().bind(Bindings.createBooleanBinding(() ->
|
.reduce(true, (left, right) -> left || right),
|
||||||
!txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate(),
|
validatingFields.stream().map(JFXTextField::textProperty).toArray(StringProperty[]::new)));
|
||||||
txtModpackName.textProperty(), txtModpackAuthor.textProperty(), txtModpackVersion.textProperty()));
|
|
||||||
}
|
|
||||||
hbox.getChildren().add(nextButton);
|
hbox.getChildren().add(nextButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,23 @@ package org.jackhuang.hmcl.ui.export;
|
|||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackExportInfo;
|
||||||
|
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask;
|
||||||
|
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask;
|
||||||
|
import org.jackhuang.hmcl.mod.server.ServerModpackExportTask;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.ui.export.ModpackInfoPage.MODPACK_INFO_OPTION;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public final class ModpackTypeSelectionPage extends StackPane implements WizardPage {
|
public final class ModpackTypeSelectionPage extends StackPane implements WizardPage {
|
||||||
private final WizardController controller;
|
private final WizardController controller;
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnHMCL;
|
private JFXButton btnMCBBS;
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnMultiMC;
|
private JFXButton btnMultiMC;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -41,12 +46,15 @@ public final class ModpackTypeSelectionPage extends StackPane implements WizardP
|
|||||||
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, btnServer};
|
JFXButton[] buttons = new JFXButton[]{btnMCBBS, btnMultiMC, btnServer};
|
||||||
String[] types = new String[]{MODPACK_TYPE_HMCL, MODPACK_TYPE_MULTIMC, MODPACK_TYPE_SERVER};
|
String[] types = new String[]{MODPACK_TYPE_MCBBS, MODPACK_TYPE_MULTIMC, MODPACK_TYPE_SERVER};
|
||||||
|
ModpackExportInfo.Options[] options = new ModpackExportInfo.Options[]{McbbsModpackExportTask.OPTION, MultiMCModpackExportTask.OPTION, ServerModpackExportTask.OPTION};
|
||||||
for (int i = 0; i < types.length; ++i) {
|
for (int i = 0; i < types.length; ++i) {
|
||||||
String type = types[i];
|
String type = types[i];
|
||||||
|
ModpackExportInfo.Options option = options[i];
|
||||||
buttons[i].setOnMouseClicked(e -> {
|
buttons[i].setOnMouseClicked(e -> {
|
||||||
controller.getSettings().put(MODPACK_TYPE, type);
|
controller.getSettings().put(MODPACK_TYPE, type);
|
||||||
|
controller.getSettings().put(MODPACK_INFO_OPTION, option);
|
||||||
controller.onNext();
|
controller.onNext();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -63,7 +71,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_HMCL = "hmcl";
|
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
</fx:define>
|
</fx:define>
|
||||||
<VBox fx:id="list" styleClass="jfx-list-view" maxHeight="150" maxWidth="300">
|
<VBox fx:id="list" styleClass="jfx-list-view" maxHeight="150" maxWidth="300">
|
||||||
<Label padding="${insets}" text="%modpack.export.as" />
|
<Label padding="${insets}" text="%modpack.export.as" />
|
||||||
<JFXButton fx:id="btnHMCL" prefWidth="${list.width}">
|
<JFXButton fx:id="btnMCBBS" prefWidth="${list.width}">
|
||||||
<graphic>
|
<graphic>
|
||||||
<BorderPane mouseTransparent="true">
|
<BorderPane mouseTransparent="true">
|
||||||
<left>
|
<left>
|
||||||
<TwoLineListItem title="%modpack.type.hmcl" subtitle="%modpack.type.hmcl.export" />
|
<TwoLineListItem title="%modpack.type.mcbbs" subtitle="%modpack.type.mcbbs.export" />
|
||||||
</left>
|
</left>
|
||||||
<right>
|
<right>
|
||||||
<fx:include BorderPane.alignment="CENTER" source="/assets/svg/arrow-right.fxml"/>
|
<fx:include BorderPane.alignment="CENTER" source="/assets/svg/arrow-right.fxml"/>
|
||||||
|
|||||||
@@ -264,6 +264,10 @@ modpack.invalid=Invalid modpack file.
|
|||||||
modpack.mismatched_type=Inappropriate modpack type, your current game is a %s modpack, but your update file is a %s modpack.
|
modpack.mismatched_type=Inappropriate modpack type, your current game is a %s modpack, but your update file is a %s modpack.
|
||||||
modpack.name=Modpack Name
|
modpack.name=Modpack Name
|
||||||
modpack.not_a_valid_name=Invalid modpack name
|
modpack.not_a_valid_name=Invalid modpack name
|
||||||
|
modpack.origin=Origin
|
||||||
|
modpack.origin.url=Official Website
|
||||||
|
modpack.origin.mcbbs=MCBBS
|
||||||
|
modpack.origin.mcbbs.prompt=Thread id
|
||||||
modpack.scan=Scanning this modpack
|
modpack.scan=Scanning this modpack
|
||||||
modpack.task.install=Import Modpack
|
modpack.task.install=Import Modpack
|
||||||
modpack.task.install.error=This modpack file cannot be recognized. Only Curse and MultiMC modpacks are supported.
|
modpack.task.install.error=This modpack file cannot be recognized. Only Curse and MultiMC modpacks are supported.
|
||||||
@@ -273,8 +277,8 @@ modpack.type.curse.completion=Install files related to Curse modpack
|
|||||||
modpack.type.curse.tolerable_error=We cannot complete the download of all files of this Curse modpack. You can retry the download when starting corresponding game version. You may retry for a couple of times due to network problems.
|
modpack.type.curse.tolerable_error=We cannot complete the download of all files of this Curse modpack. You can retry the download when starting corresponding game version. You may retry for a couple of times due to network problems.
|
||||||
modpack.type.curse.error=Unable to install this Curse modpack. Please retry.
|
modpack.type.curse.error=Unable to install this Curse modpack. Please retry.
|
||||||
modpack.type.curse.not_found=Some of required resources are missing and thus could not be downloaded. Please consider the latest version or other modpacks.
|
modpack.type.curse.not_found=Some of required resources are missing and thus could not be downloaded. Please consider the latest version or other modpacks.
|
||||||
modpack.type.hmcl=Hello Minecraft! Launcher
|
modpack.type.mcbbs=MCBBS Standard
|
||||||
modpack.type.hmcl.export=Can be imported by Hello Minecraft! Launcher
|
modpack.type.mcbbs.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=Server Auto-Update Modpack
|
||||||
@@ -290,6 +294,7 @@ modpack.wizard.step.2.title=Add files to the modpack.
|
|||||||
modpack.wizard.step.3=Modpack Type
|
modpack.wizard.step.3=Modpack Type
|
||||||
modpack.wizard.step.3.title=Choose the format of the modpack.
|
modpack.wizard.step.3.title=Choose the format of the modpack.
|
||||||
modpack.wizard.step.initialization.exported_version=Exported game version
|
modpack.wizard.step.initialization.exported_version=Exported game version
|
||||||
|
modpack.wizard.step.initialization.force_update=Force updating modpack if possible
|
||||||
modpack.wizard.step.initialization.include_launcher=Include the launcher
|
modpack.wizard.step.initialization.include_launcher=Include the launcher
|
||||||
modpack.wizard.step.initialization.save=Export to...
|
modpack.wizard.step.initialization.save=Export to...
|
||||||
modpack.wizard.step.initialization.warning=Before creating a modpack, you should ensure that the game can launch successfully,\nand that your Minecraft is a release version.\nDo NOT add mods which cannot be redistributed.
|
modpack.wizard.step.initialization.warning=Before creating a modpack, you should ensure that the game can launch successfully,\nand that your Minecraft is a release version.\nDo NOT add mods which cannot be redistributed.
|
||||||
@@ -405,6 +410,7 @@ settings.launcher.proxy.socks=Socks
|
|||||||
settings.launcher.proxy.username=Account
|
settings.launcher.proxy.username=Account
|
||||||
settings.launcher.theme=Theme
|
settings.launcher.theme=Theme
|
||||||
|
|
||||||
|
settings.min_memory=Min Memory/MB
|
||||||
settings.max_memory=Max Memory/MB
|
settings.max_memory=Max Memory/MB
|
||||||
settings.physical_memory=Physical Memory Size
|
settings.physical_memory=Physical Memory Size
|
||||||
settings.show_log=Show Logs
|
settings.show_log=Show Logs
|
||||||
|
|||||||
@@ -264,6 +264,10 @@ modpack.invalid=无效的整合包升级文件,可能是下载时出现问题
|
|||||||
modpack.mismatched_type=整合包类型不匹配,当前游戏是 %s 整合包,但是提供的整合包更新文件是 %s 整合包。
|
modpack.mismatched_type=整合包类型不匹配,当前游戏是 %s 整合包,但是提供的整合包更新文件是 %s 整合包。
|
||||||
modpack.name=整合包名称
|
modpack.name=整合包名称
|
||||||
modpack.not_a_valid_name=不是一个有效的整合包名称
|
modpack.not_a_valid_name=不是一个有效的整合包名称
|
||||||
|
modpack.origin=来源
|
||||||
|
modpack.origin.url=官方网站
|
||||||
|
modpack.origin.mcbbs=MCBBS
|
||||||
|
modpack.origin.mcbbs.prompt=贴子 id
|
||||||
modpack.scan=解析整合包
|
modpack.scan=解析整合包
|
||||||
modpack.task.install=导入整合包
|
modpack.task.install=导入整合包
|
||||||
modpack.task.install.error=无法识别该整合包,目前仅支持导入 Curse、MultiMC、HMCL 整合包。
|
modpack.task.install.error=无法识别该整合包,目前仅支持导入 Curse、MultiMC、HMCL 整合包。
|
||||||
@@ -273,7 +277,7 @@ modpack.type.curse.completion=下载 Curse 整合包相关文件
|
|||||||
modpack.type.curse.tolerable_error=但未能完成 Curse 整合包文件的下载,您可以在启动该游戏版本时继续 Curse 整合包文件的下载。由于网络问题,您可能需要重试多次。
|
modpack.type.curse.tolerable_error=但未能完成 Curse 整合包文件的下载,您可以在启动该游戏版本时继续 Curse 整合包文件的下载。由于网络问题,您可能需要重试多次。
|
||||||
modpack.type.curse.error=未能完成 Curse 整合包的下载,请多次重试或设置代理
|
modpack.type.curse.error=未能完成 Curse 整合包的下载,请多次重试或设置代理
|
||||||
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。
|
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。
|
||||||
modpack.type.hmcl=HMCL
|
modpack.type.mcbbs=我的世界中文论坛整合包标准
|
||||||
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 导入
|
||||||
@@ -290,6 +294,7 @@ modpack.wizard.step.2.title=选中你想加到整合包中的文件或文件夹
|
|||||||
modpack.wizard.step.3=整合包类型
|
modpack.wizard.step.3=整合包类型
|
||||||
modpack.wizard.step.3.title=选择整合包导出类型
|
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.include_launcher=包含启动器
|
modpack.wizard.step.initialization.include_launcher=包含启动器
|
||||||
modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位置
|
modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位置
|
||||||
modpack.wizard.step.initialization.warning=在制作整合包前,请您确认您选择的版本可以正常启动,\n并保证您的 Minecraft 是正式版而非快照版,\n而且不应当将不允许非官方途径传播的 Mod、材质包等纳入整合包。\n整合包会保存您目前的下载源设置
|
modpack.wizard.step.initialization.warning=在制作整合包前,请您确认您选择的版本可以正常启动,\n并保证您的 Minecraft 是正式版而非快照版,\n而且不应当将不允许非官方途径传播的 Mod、材质包等纳入整合包。\n整合包会保存您目前的下载源设置
|
||||||
@@ -405,6 +410,7 @@ settings.launcher.proxy.socks=Socks
|
|||||||
settings.launcher.proxy.username=账户
|
settings.launcher.proxy.username=账户
|
||||||
settings.launcher.theme=主题
|
settings.launcher.theme=主题
|
||||||
|
|
||||||
|
settings.min_memory=最小内存(MB)
|
||||||
settings.max_memory=最大内存(MB)
|
settings.max_memory=最大内存(MB)
|
||||||
settings.physical_memory=物理内存大小
|
settings.physical_memory=物理内存大小
|
||||||
settings.show_log=查看日志
|
settings.show_log=查看日志
|
||||||
|
|||||||
@@ -18,10 +18,14 @@
|
|||||||
package org.jackhuang.hmcl.game;
|
package org.jackhuang.hmcl.game;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -34,8 +38,8 @@ public class LaunchOptions implements Serializable {
|
|||||||
private String versionName;
|
private String versionName;
|
||||||
private String versionType;
|
private String versionType;
|
||||||
private String profileName;
|
private String profileName;
|
||||||
private String minecraftArgs;
|
private List<String> gameArguments = new ArrayList<>();
|
||||||
private String javaArgs;
|
private List<String> javaArguments = new ArrayList<>();
|
||||||
private Integer minMemory;
|
private Integer minMemory;
|
||||||
private Integer maxMemory;
|
private Integer maxMemory;
|
||||||
private Integer metaspace;
|
private Integer metaspace;
|
||||||
@@ -90,15 +94,17 @@ public class LaunchOptions implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* User custom additional minecraft command line arguments.
|
* User custom additional minecraft command line arguments.
|
||||||
*/
|
*/
|
||||||
public String getMinecraftArgs() {
|
@NotNull
|
||||||
return minecraftArgs;
|
public List<String> getGameArguments() {
|
||||||
|
return Collections.unmodifiableList(gameArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User custom additional java virtual machine command line arguments.
|
* User custom additional java virtual machine command line arguments.
|
||||||
*/
|
*/
|
||||||
public String getJavaArgs() {
|
@NotNull
|
||||||
return javaArgs;
|
public List<String> getJavaArguments() {
|
||||||
|
return Collections.unmodifiableList(javaArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,6 +208,150 @@ public class LaunchOptions implements Serializable {
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The game directory
|
||||||
|
*/
|
||||||
|
public File getGameDir() {
|
||||||
|
return options.gameDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Java Environment that Minecraft runs on.
|
||||||
|
*/
|
||||||
|
public JavaVersion getJava() {
|
||||||
|
return options.java;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will shown in the left bottom corner of the main menu of Minecraft.
|
||||||
|
* null if use the id of launch version.
|
||||||
|
*/
|
||||||
|
public String getVersionName() {
|
||||||
|
return options.versionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will shown in the left bottom corner of the main menu of Minecraft.
|
||||||
|
* null if use Version.versionType.
|
||||||
|
*/
|
||||||
|
public String getVersionType() {
|
||||||
|
return options.versionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't know what the hell this is.
|
||||||
|
*/
|
||||||
|
public String getProfileName() {
|
||||||
|
return options.profileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User custom additional minecraft command line arguments.
|
||||||
|
*/
|
||||||
|
public List<String> getGameArguments() {
|
||||||
|
return options.gameArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User custom additional java virtual machine command line arguments.
|
||||||
|
*/
|
||||||
|
public List<String> getJavaArguments() {
|
||||||
|
return options.javaArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum memory that the JVM can allocate.
|
||||||
|
*/
|
||||||
|
public Integer getMinMemory() {
|
||||||
|
return options.minMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum memory that the JVM can allocate.
|
||||||
|
*/
|
||||||
|
public Integer getMaxMemory() {
|
||||||
|
return options.maxMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum metaspace memory that the JVM can allocate.
|
||||||
|
* For Java 7 -XX:PermSize and Java 8 -XX:MetaspaceSize
|
||||||
|
* Containing class instances.
|
||||||
|
*/
|
||||||
|
public Integer getMetaspace() {
|
||||||
|
return options.metaspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The initial game window width
|
||||||
|
*/
|
||||||
|
public Integer getWidth() {
|
||||||
|
return options.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The initial game window height
|
||||||
|
*/
|
||||||
|
public Integer getHeight() {
|
||||||
|
return options.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is inital game window fullscreen.
|
||||||
|
*/
|
||||||
|
public boolean isFullscreen() {
|
||||||
|
return options.fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server ip that will connect to when enter game main menu.
|
||||||
|
*/
|
||||||
|
public String getServerIp() {
|
||||||
|
return options.serverIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i.e. optirun
|
||||||
|
*/
|
||||||
|
public String getWrapper() {
|
||||||
|
return options.wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy settings
|
||||||
|
*/
|
||||||
|
public Proxy getProxy() {
|
||||||
|
return options.proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user name of the proxy, optional.
|
||||||
|
*/
|
||||||
|
public String getProxyUser() {
|
||||||
|
return options.proxyUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password of the proxy, optional
|
||||||
|
*/
|
||||||
|
public String getProxyPass() {
|
||||||
|
return options.proxyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent game launcher from generating default JVM arguments like max memory.
|
||||||
|
*/
|
||||||
|
public boolean isNoGeneratedJVMArgs() {
|
||||||
|
return options.noGeneratedJVMArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called command line before launching the game.
|
||||||
|
*/
|
||||||
|
public String getPreLaunchCommand() {
|
||||||
|
return options.preLaunchCommand;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setGameDir(File gameDir) {
|
public Builder setGameDir(File gameDir) {
|
||||||
options.gameDir = gameDir;
|
options.gameDir = gameDir;
|
||||||
return this;
|
return this;
|
||||||
@@ -227,13 +377,15 @@ public class LaunchOptions implements Serializable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setMinecraftArgs(String minecraftArgs) {
|
public Builder setGameArguments(List<String> gameArguments) {
|
||||||
options.minecraftArgs = minecraftArgs;
|
options.gameArguments.clear();
|
||||||
|
options.gameArguments.addAll(gameArguments);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setJavaArgs(String javaArgs) {
|
public Builder setJavaArguments(List<String> javaArguments) {
|
||||||
options.javaArgs = javaArgs;
|
options.javaArguments.clear();
|
||||||
|
options.javaArguments.addAll(javaArguments);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
|
|
||||||
res.add(options.getJava().getBinary().toString());
|
res.add(options.getJava().getBinary().toString());
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(options.getJavaArgs()))
|
res.addAllWithoutParsing(options.getJavaArguments());
|
||||||
res.addAllWithoutParsing(StringUtils.tokenize(options.getJavaArgs()));
|
|
||||||
|
|
||||||
// JVM Args
|
// JVM Args
|
||||||
if (!options.isNoGeneratedJVMArgs()) {
|
if (!options.isNoGeneratedJVMArgs()) {
|
||||||
@@ -218,8 +217,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(options.getMinecraftArgs()))
|
res.addAllWithoutParsing(options.getGameArguments());
|
||||||
res.addAllWithoutParsing(StringUtils.tokenize(options.getMinecraftArgs()));
|
|
||||||
|
|
||||||
res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get());
|
res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get());
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -0,0 +1,298 @@
|
|||||||
|
package org.jackhuang.hmcl.mod;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ModpackExportInfo {
|
||||||
|
|
||||||
|
private final List<String> whitelist = new ArrayList<>();
|
||||||
|
private String name;
|
||||||
|
private String author;
|
||||||
|
private String version;
|
||||||
|
private String description;
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private boolean forceUpdate;
|
||||||
|
private boolean packWithLauncher;
|
||||||
|
|
||||||
|
private String fileApi;
|
||||||
|
private int minMemory;
|
||||||
|
private List<Integer> supportedJavaVersions;
|
||||||
|
private String launchArguments;
|
||||||
|
private String javaArguments;
|
||||||
|
|
||||||
|
private String authlibInjectorServer;
|
||||||
|
|
||||||
|
private Path output;
|
||||||
|
private List<McbbsModpackManifest.Origin> origins = new ArrayList<>();
|
||||||
|
|
||||||
|
public ModpackExportInfo() {}
|
||||||
|
|
||||||
|
public List<String> getWhitelist() {
|
||||||
|
return whitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setWhitelist(List<String> whitelist) {
|
||||||
|
this.whitelist.clear();
|
||||||
|
this.whitelist.addAll(whitelist);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of this modpack.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author of this modpack.
|
||||||
|
*/
|
||||||
|
public String getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setAuthor(String author) {
|
||||||
|
this.author = author;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version of this modpack.
|
||||||
|
*/
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of this modpack.
|
||||||
|
*
|
||||||
|
* Supports plain HTML text.
|
||||||
|
*/
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileApi() {
|
||||||
|
return fileApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setFileApi(String fileApi) {
|
||||||
|
this.fileApi = fileApi;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modpack official introduction webpage link.
|
||||||
|
*/
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForceUpdate() {
|
||||||
|
return forceUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setForceUpdate(boolean forceUpdate) {
|
||||||
|
this.forceUpdate = forceUpdate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPackWithLauncher() {
|
||||||
|
return packWithLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setPackWithLauncher(boolean packWithLauncher) {
|
||||||
|
this.packWithLauncher = packWithLauncher;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinMemory() {
|
||||||
|
return minMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setMinMemory(int minMemory) {
|
||||||
|
this.minMemory = minMemory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public List<Integer> getSupportedJavaVersions() {
|
||||||
|
return supportedJavaVersions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setSupportedJavaVersions(List<Integer> supportedJavaVersions) {
|
||||||
|
this.supportedJavaVersions = supportedJavaVersions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLaunchArguments() {
|
||||||
|
return launchArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setLaunchArguments(String launchArguments) {
|
||||||
|
this.launchArguments = launchArguments;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJavaArguments() {
|
||||||
|
return javaArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setJavaArguments(String javaArguments) {
|
||||||
|
this.javaArguments = javaArguments;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthlibInjectorServer() {
|
||||||
|
return authlibInjectorServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setAuthlibInjectorServer(String authlibInjectorServer) {
|
||||||
|
this.authlibInjectorServer = authlibInjectorServer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getOutput() {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setOutput(Path output) {
|
||||||
|
this.output = output;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<McbbsModpackManifest.Origin> getOrigins() {
|
||||||
|
return Collections.unmodifiableList(origins);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo setOrigins(List<McbbsModpackManifest.Origin> origins) {
|
||||||
|
this.origins.clear();
|
||||||
|
this.origins.addAll(origins);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModpackExportInfo validate() throws NullPointerException {
|
||||||
|
if (output == null)
|
||||||
|
throw new NullPointerException("ModpackExportInfo.output cannot be null");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Options {
|
||||||
|
private boolean requireUrl;
|
||||||
|
private boolean requireForceUpdate;
|
||||||
|
private boolean requireFileApi;
|
||||||
|
private boolean validateFileApi;
|
||||||
|
private boolean requireMinMemory;
|
||||||
|
private boolean requireAuthlibInjectorServer;
|
||||||
|
private boolean requireLaunchArguments;
|
||||||
|
private boolean requireJavaArguments;
|
||||||
|
private boolean requireOrigins;
|
||||||
|
|
||||||
|
public Options() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireUrl() {
|
||||||
|
return requireUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireForceUpdate() {
|
||||||
|
return requireForceUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireFileApi() {
|
||||||
|
return requireFileApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidateFileApi() {
|
||||||
|
return validateFileApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireMinMemory() {
|
||||||
|
return requireMinMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireAuthlibInjectorServer() {
|
||||||
|
return requireAuthlibInjectorServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireLaunchArguments() {
|
||||||
|
return requireLaunchArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireJavaArguments() {
|
||||||
|
return requireJavaArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireOrigins() {
|
||||||
|
return requireOrigins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireUrl() {
|
||||||
|
requireUrl = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireForceUpdate() {
|
||||||
|
requireForceUpdate = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireFileApi(boolean optional) {
|
||||||
|
requireFileApi = true;
|
||||||
|
validateFileApi = !optional;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireMinMemory() {
|
||||||
|
requireMinMemory = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireAuthlibInjectorServer() {
|
||||||
|
requireAuthlibInjectorServer = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireLaunchArguments() {
|
||||||
|
requireLaunchArguments = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireJavaArguments() {
|
||||||
|
requireJavaArguments = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options requireOrigins() {
|
||||||
|
requireOrigins = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 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.mcbbs;
|
||||||
|
|
||||||
|
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.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.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class McbbsModpackCompletionTask extends Task<Void> {
|
||||||
|
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final String version;
|
||||||
|
private ModpackConfiguration<McbbsModpackManifest> manifest;
|
||||||
|
private GetTask dependent;
|
||||||
|
private McbbsModpackManifest remoteManifest;
|
||||||
|
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) {
|
||||||
|
this(dependencyManager, version, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, String version, ModpackConfiguration<McbbsModpackManifest> manifest) {
|
||||||
|
this.repository = dependencyManager.getGameRepository();
|
||||||
|
this.version = version;
|
||||||
|
|
||||||
|
if (manifest == null) {
|
||||||
|
try {
|
||||||
|
File manifestFile = repository.getModpackConfiguration(version);
|
||||||
|
if (manifestFile.exists()) {
|
||||||
|
this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), new TypeToken<ModpackConfiguration<McbbsModpackManifest>>() {
|
||||||
|
}.getType());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logging.LOG.log(Level.WARNING, "Unable to read mcbbs modpack manifest.json", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.manifest = manifest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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() + "/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(), McbbsModpackManifest.class);
|
||||||
|
} catch (JsonParseException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Path rootPath = repository.getVersionRoot(version).toPath();
|
||||||
|
|
||||||
|
// Because in China, Curse is too difficult to visit,
|
||||||
|
// if failed, ignore it and retry next time.
|
||||||
|
// CurseManifest newManifest = manifest.setFiles(
|
||||||
|
// manifest.getFiles().parallelStream()
|
||||||
|
// .map(file -> {
|
||||||
|
// updateProgress(finished.incrementAndGet(), manifest.getFiles().size());
|
||||||
|
// if (StringUtils.isBlank(file.getFileName())) {
|
||||||
|
// try {
|
||||||
|
// return file.withFileName(NetworkUtils.detectFileName(file.getUrl()));
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// try {
|
||||||
|
// String result = NetworkUtils.doGet(NetworkUtils.toURL(String.format("https://cursemeta.dries007.net/%d/%d.json", file.getProjectID(), file.getFileID())));
|
||||||
|
// CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class);
|
||||||
|
// return file.withFileName(mod.getFileNameOnDisk()).withURL(mod.getDownloadURL());
|
||||||
|
// } catch (FileNotFoundException fof) {
|
||||||
|
// Logging.LOG.log(Level.WARNING, "Could not query cursemeta for deleted mods: " + file.getUrl(), fof);
|
||||||
|
// notFound.set(true);
|
||||||
|
// return file;
|
||||||
|
// } catch (IOException | JsonParseException e2) {
|
||||||
|
// try {
|
||||||
|
// String result = NetworkUtils.doGet(NetworkUtils.toURL(String.format("https://addons-ecs.forgesvc.net/api/v2/addon/%d/file/%d", file.getProjectID(), file.getFileID())));
|
||||||
|
// CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class);
|
||||||
|
// return file.withFileName(mod.getFileName()).withURL(mod.getDownloadURL());
|
||||||
|
// } catch (FileNotFoundException fof) {
|
||||||
|
// Logging.LOG.log(Level.WARNING, "Could not query forgesvc for deleted mods: " + file.getUrl(), fof);
|
||||||
|
// notFound.set(true);
|
||||||
|
// return file;
|
||||||
|
// } catch (IOException | JsonParseException e3) {
|
||||||
|
// Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), e);
|
||||||
|
// Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), e2);
|
||||||
|
// Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), e3);
|
||||||
|
// allNameKnown.set(false);
|
||||||
|
// return file;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// return file;
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .collect(Collectors.toList()));
|
||||||
|
//
|
||||||
|
// 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 missing...
|
||||||
|
// // Re-download it since network problem may cause file missing
|
||||||
|
// download = true;
|
||||||
|
// } 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", 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/" + NetworkUtils.encodeLocation(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(), this.manifest.getName(), this.manifest.getVersion(), remoteManifest.getFiles())));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 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.mcbbs;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
|
import org.jackhuang.hmcl.game.GameVersion;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
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.Logging;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
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 McbbsModpackExportTask extends Task<Void> {
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final String version;
|
||||||
|
private final ModpackExportInfo info;
|
||||||
|
|
||||||
|
public McbbsModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.version = version;
|
||||||
|
this.info = info.validate();
|
||||||
|
|
||||||
|
onDone().register(event -> {
|
||||||
|
if (event.isFailed()) this.info.getOutput().toFile().delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
ArrayList<String> blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST);
|
||||||
|
blackList.add(version + ".jar");
|
||||||
|
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");
|
||||||
|
try (Zipper zip = new Zipper(info.getOutput())) {
|
||||||
|
Path runDirectory = repository.getRunDirectory(version).toPath();
|
||||||
|
List<McbbsModpackManifest.File> files = new ArrayList<>();
|
||||||
|
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||||
|
if (Modpack.acceptFile(path, blackList, info.getWhitelist())) {
|
||||||
|
Path file = runDirectory.resolve(path);
|
||||||
|
if (Files.isRegularFile(file)) {
|
||||||
|
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||||
|
files.add(new McbbsModpackManifest.AddonFile(true, relativePath, encodeHex(digest("SHA-1", file))));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version));
|
||||||
|
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version))
|
||||||
|
.orElseThrow(() -> new IOException("Cannot parse the version of " + version));
|
||||||
|
|
||||||
|
List<McbbsModpackManifest.Addon> addons = new ArrayList<>();
|
||||||
|
addons.add(new McbbsModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion));
|
||||||
|
analyzer.getVersion(FORGE).ifPresent(forgeVersion ->
|
||||||
|
addons.add(new McbbsModpackManifest.Addon(FORGE.getPatchId(), forgeVersion)));
|
||||||
|
analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion ->
|
||||||
|
addons.add(new McbbsModpackManifest.Addon(LITELOADER.getPatchId(), liteLoaderVersion)));
|
||||||
|
analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion ->
|
||||||
|
addons.add(new McbbsModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion)));
|
||||||
|
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
|
||||||
|
addons.add(new McbbsModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion)));
|
||||||
|
|
||||||
|
List<Library> libraries = new ArrayList<>();
|
||||||
|
// TODO libraries
|
||||||
|
|
||||||
|
List<McbbsModpackManifest.Origin> origins = new ArrayList<>();
|
||||||
|
// TODO origins
|
||||||
|
|
||||||
|
McbbsModpackManifest.Settings settings = new McbbsModpackManifest.Settings();
|
||||||
|
McbbsModpackManifest.LaunchInfo launchInfo = new McbbsModpackManifest.LaunchInfo(info.getMinMemory(), info.getSupportedJavaVersions(), StringUtils.tokenize(info.getLaunchArguments()), StringUtils.tokenize(info.getJavaArguments()));
|
||||||
|
|
||||||
|
McbbsModpackManifest manifest = new McbbsModpackManifest(McbbsModpackManifest.MANIFEST_TYPE, 1, info.getName(), info.getVersion(), info.getAuthor(), info.getDescription(), info.getFileApi() == null ? null : StringUtils.removeSuffix(info.getFileApi(), "/"), info.getUrl(), info.isForceUpdate(), origins, addons, libraries, files, settings, launchInfo);
|
||||||
|
zip.putTextFile(JsonUtils.GSON.toJson(manifest), "manifest.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options()
|
||||||
|
.requireFileApi(true)
|
||||||
|
.requireUrl()
|
||||||
|
.requireForceUpdate()
|
||||||
|
.requireMinMemory()
|
||||||
|
.requireAuthlibInjectorServer()
|
||||||
|
.requireJavaArguments()
|
||||||
|
.requireLaunchArguments()
|
||||||
|
.requireOrigins();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 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.mcbbs;
|
||||||
|
|
||||||
|
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.game.Version;
|
||||||
|
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;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class McbbsModpackLocalInstallTask extends Task<Void> {
|
||||||
|
|
||||||
|
private final File zipFile;
|
||||||
|
private final Modpack modpack;
|
||||||
|
private final McbbsModpackManifest manifest;
|
||||||
|
private final String name;
|
||||||
|
private final boolean update;
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||||
|
private final List<Task<?>> dependents = new LinkedList<>();
|
||||||
|
|
||||||
|
public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, McbbsModpackManifest 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.");
|
||||||
|
this.update = repository.hasVersion(name);
|
||||||
|
|
||||||
|
|
||||||
|
GameBuilder builder = dependencyManager.gameBuilder().name(name);
|
||||||
|
for (McbbsModpackManifest.Addon addon : manifest.getAddons()) {
|
||||||
|
builder.version(addon.getId(), addon.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
dependents.add(builder.buildAsync());
|
||||||
|
onDone().register(event -> {
|
||||||
|
if (event.isFailed())
|
||||||
|
repository.removeVersionFromDisk(name);
|
||||||
|
});
|
||||||
|
|
||||||
|
ModpackConfiguration<McbbsModpackManifest> config = null;
|
||||||
|
try {
|
||||||
|
if (json.exists()) {
|
||||||
|
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<McbbsModpackManifest>>() {
|
||||||
|
}.getType());
|
||||||
|
|
||||||
|
if (!MODPACK_TYPE.equals(config.getType()))
|
||||||
|
throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version.");
|
||||||
|
}
|
||||||
|
} catch (JsonParseException | IOException ignore) {
|
||||||
|
}
|
||||||
|
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/overrides", any -> true, config).withStage("hmcl.modpack"));
|
||||||
|
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/overrides", manifest, MODPACK_TYPE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task<?>> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
Version version = repository.readVersionJson(name);
|
||||||
|
Optional<Version> mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst();
|
||||||
|
if (!update) {
|
||||||
|
Version patch = new Version(PATCH_NAME).setLibraries(manifest.getLibraries());
|
||||||
|
dependencies.add(repository.saveAsync(version.addPatch(patch)));
|
||||||
|
} else if (mcbbsPatch.isPresent()) {
|
||||||
|
// This mcbbs modpack was installed by HMCL.
|
||||||
|
Version patch = mcbbsPatch.get().setLibraries(manifest.getLibraries());
|
||||||
|
dependencies.add(repository.saveAsync(version.addPatch(patch)));
|
||||||
|
} else {
|
||||||
|
// This mcbbs modpack was installed by other launchers.
|
||||||
|
// TODO: maintain libraries.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getStages() {
|
||||||
|
return Stream.concat(
|
||||||
|
dependents.stream().flatMap(task -> task.getStages().stream()),
|
||||||
|
Stream.of("hmcl.modpack")
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String PATCH_NAME = "mcbbs";
|
||||||
|
public static final String MODPACK_TYPE = "Mcbbs";
|
||||||
|
}
|
||||||
@@ -0,0 +1,409 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 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.mcbbs;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.jackhuang.hmcl.game.LaunchOptions;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
|
import org.jackhuang.hmcl.util.gson.*;
|
||||||
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
|
||||||
|
|
||||||
|
public class McbbsModpackManifest implements Validation {
|
||||||
|
public static final String MANIFEST_TYPE = "minecraftModpack";
|
||||||
|
|
||||||
|
private final String manifestType;
|
||||||
|
private final int manifestVersion;
|
||||||
|
private final String name;
|
||||||
|
private final String version;
|
||||||
|
private final String author;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final String fileApi;
|
||||||
|
private final String url;
|
||||||
|
private final boolean forceUpdate;
|
||||||
|
@SerializedName("origin")
|
||||||
|
private final List<Origin> origins;
|
||||||
|
private final List<Addon> addons;
|
||||||
|
private final List<Library> libraries;
|
||||||
|
private final List<File> files;
|
||||||
|
private final Settings settings;
|
||||||
|
private final LaunchInfo launchInfo;
|
||||||
|
// sandbox and antiCheating are both not supported.
|
||||||
|
|
||||||
|
public McbbsModpackManifest() {
|
||||||
|
this(MANIFEST_TYPE, 1, "", "", "", "", null, "", false, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), new Settings(), new LaunchInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
public McbbsModpackManifest(String manifestType, int manifestVersion, String name, String version, String author, String description, @Nullable String fileApi, String url, boolean forceUpdate, List<Origin> origins, List<Addon> addons, List<Library> libraries, List<File> files, Settings settings, LaunchInfo launchInfo) {
|
||||||
|
this.manifestType = manifestType;
|
||||||
|
this.manifestVersion = manifestVersion;
|
||||||
|
this.name = name;
|
||||||
|
this.version = version;
|
||||||
|
this.author = author;
|
||||||
|
this.description = description;
|
||||||
|
this.fileApi = fileApi;
|
||||||
|
this.url = url;
|
||||||
|
this.forceUpdate = forceUpdate;
|
||||||
|
this.origins = origins;
|
||||||
|
this.addons = addons;
|
||||||
|
this.libraries = libraries;
|
||||||
|
this.files = files;
|
||||||
|
this.settings = settings;
|
||||||
|
this.launchInfo = launchInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getManifestType() {
|
||||||
|
return manifestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getManifestVersion() {
|
||||||
|
return manifestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileApi() {
|
||||||
|
return fileApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForceUpdate() {
|
||||||
|
return forceUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Origin> getOrigins() {
|
||||||
|
return origins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Addon> getAddons() {
|
||||||
|
return addons;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Library> getLibraries() {
|
||||||
|
return libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<File> getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Settings getSettings() {
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LaunchInfo getLaunchInfo() {
|
||||||
|
return launchInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException, TolerableValidationException {
|
||||||
|
if (!MANIFEST_TYPE.equals(manifestType))
|
||||||
|
throw new JsonParseException("McbbsModpackManifest.manifestType must be 'minecraftModpack'");
|
||||||
|
// if (manifestVersion > 1)
|
||||||
|
// throw new JsonParseException("Only supports version 1 of McbbsModpackManifest");
|
||||||
|
if (files == null)
|
||||||
|
throw new JsonParseException("McbbsModpackManifest.files cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Origin {
|
||||||
|
private final String type;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
public Origin() {
|
||||||
|
this("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Origin(String type, int id) {
|
||||||
|
this.type = type;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Settings {
|
||||||
|
@SerializedName("install_modes")
|
||||||
|
private final boolean installMods;
|
||||||
|
|
||||||
|
@SerializedName("install_resourcepack")
|
||||||
|
private final boolean installResourcepack;
|
||||||
|
|
||||||
|
public Settings() {
|
||||||
|
this(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Settings(boolean installMods, boolean installResourcepack) {
|
||||||
|
this.installMods = installMods;
|
||||||
|
this.installResourcepack = installResourcepack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInstallMods() {
|
||||||
|
return installMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInstallResourcepack() {
|
||||||
|
return installResourcepack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonType(
|
||||||
|
property = "type",
|
||||||
|
subtypes = {
|
||||||
|
@JsonSubtype(clazz = AddonFile.class, name = "addon"),
|
||||||
|
@JsonSubtype(clazz = CurseFile.class, name = "curse")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public static abstract class File implements Validation {
|
||||||
|
private final boolean force;
|
||||||
|
|
||||||
|
public File(boolean force) {
|
||||||
|
this.force = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException, TolerableValidationException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForce() {
|
||||||
|
return force;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class AddonFile extends File {
|
||||||
|
private final String path;
|
||||||
|
private final String hash;
|
||||||
|
|
||||||
|
public AddonFile(boolean force, String path, String hash) {
|
||||||
|
super(force);
|
||||||
|
this.path = path;
|
||||||
|
this.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException, TolerableValidationException {
|
||||||
|
super.validate();
|
||||||
|
|
||||||
|
Validation.requireNonNull(path, "AddonFile.path cannot be null");
|
||||||
|
Validation.requireNonNull(hash, "AddonFile.hash cannot be null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class CurseFile extends File {
|
||||||
|
private final int projectID;
|
||||||
|
private final int fileID;
|
||||||
|
private final String fileName;
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
public CurseFile() {
|
||||||
|
this(false, 0, 0, "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CurseFile(boolean force, int projectID, int fileID, String fileName, String url) {
|
||||||
|
super(force);
|
||||||
|
this.projectID = projectID;
|
||||||
|
this.fileID = fileID;
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProjectID() {
|
||||||
|
return projectID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFileID() {
|
||||||
|
return fileID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return url == null ? NetworkUtils.toURL("https://www.curseforge.com/minecraft/mc-mods/" + projectID + "/download/" + fileID + "/file")
|
||||||
|
: NetworkUtils.toURL(NetworkUtils.encodeLocation(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException, TolerableValidationException {
|
||||||
|
super.validate();
|
||||||
|
|
||||||
|
if (projectID == 0 || fileID == 0) {
|
||||||
|
throw new JsonParseException("CurseFile.{projectID|fileID} cannot be empty.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
CurseFile curseFile = (CurseFile) o;
|
||||||
|
return projectID == curseFile.projectID && fileID == curseFile.fileID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(projectID, fileID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class LaunchInfo {
|
||||||
|
private final int minMemory;
|
||||||
|
private final List<Integer> supportJava;
|
||||||
|
@SerializedName("launchArgument")
|
||||||
|
private final List<String> launchArguments;
|
||||||
|
@SerializedName("javaArgument")
|
||||||
|
private final List<String> javaArguments;
|
||||||
|
|
||||||
|
public LaunchInfo() {
|
||||||
|
this(0, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public LaunchInfo(int minMemory, List<Integer> supportJava, List<String> launchArguments, List<String> javaArguments) {
|
||||||
|
this.minMemory = minMemory;
|
||||||
|
this.supportJava = supportJava;
|
||||||
|
this.launchArguments = launchArguments;
|
||||||
|
this.javaArguments = javaArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinMemory() {
|
||||||
|
return minMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public List<Integer> getSupportJava() {
|
||||||
|
return supportJava;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getLaunchArguments() {
|
||||||
|
return Optional.ofNullable(launchArguments).orElseGet(Collections::emptyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getJavaArguments() {
|
||||||
|
return Optional.ofNullable(javaArguments).orElseGet(Collections::emptyList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ServerInfo {
|
||||||
|
private final String authlibInjectorServer;
|
||||||
|
|
||||||
|
public ServerInfo() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerInfo(String authlibInjectorServer) {
|
||||||
|
this.authlibInjectorServer = authlibInjectorServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getAuthlibInjectorServer() {
|
||||||
|
return authlibInjectorServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Modpack toModpack(Charset encoding) throws IOException {
|
||||||
|
String gameVersion = addons.stream().filter(x -> MINECRAFT.getPatchId().equals(x.id)).findAny()
|
||||||
|
.orElseThrow(() -> new IOException("Cannot find game version")).getVersion();
|
||||||
|
return new Modpack(name, author, version, gameVersion, description, encoding, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectLaunchOptions(LaunchOptions.Builder launchOptions) {
|
||||||
|
launchOptions.getGameArguments().addAll(launchInfo.getLaunchArguments());
|
||||||
|
launchOptions.getJavaArguments().addAll(launchInfo.getJavaArguments());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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, "manifest.json", encoding);
|
||||||
|
McbbsModpackManifest manifest = JsonUtils.fromNonNullJson(json, McbbsModpackManifest.class);
|
||||||
|
return manifest.toModpack(encoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 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.mcbbs;
|
||||||
|
|
||||||
|
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.ModpackConfiguration;
|
||||||
|
import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
|
||||||
|
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 McbbsModpackRemoteInstallTask extends Task<Void> {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final DefaultDependencyManager dependency;
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||||
|
private final List<Task<?>> dependents = new LinkedList<>();
|
||||||
|
private final ServerModpackManifest manifest;
|
||||||
|
|
||||||
|
public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, ServerModpackManifest manifest, String name) {
|
||||||
|
this.name = name;
|
||||||
|
this.dependency = dependencyManager;
|
||||||
|
this.repository = dependencyManager.getGameRepository();
|
||||||
|
this.manifest = manifest;
|
||||||
|
|
||||||
|
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 Server modpack. Cannot update this version.");
|
||||||
|
}
|
||||||
|
} catch (JsonParseException | IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task<?>> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
// dependencies.add(new McbbsModpackCompletionTask(dependency, name, new ModpackConfiguration<>(manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), Collections.emptyList())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String MODPACK_TYPE = "Server";
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ 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.mod.Modpack;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackExportInfo;
|
||||||
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;
|
||||||
@@ -91,4 +92,6 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
|||||||
zip.putTextFile("", ".packignore");
|
zip.putTextFile("", ".packignore");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options().requireMinMemory();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
|||||||
}.getType());
|
}.getType());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logging.LOG.log(Level.WARNING, "Unable to read CurseForge modpack manifest.json", e);
|
Logging.LOG.log(Level.WARNING, "Unable to read Server modpack manifest.json", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.manifest = manifest;
|
this.manifest = manifest;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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.mod.Modpack;
|
||||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
|
import org.jackhuang.hmcl.mod.ModpackExportInfo;
|
||||||
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.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
@@ -43,27 +44,15 @@ import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
|||||||
public class ServerModpackExportTask extends Task<Void> {
|
public class ServerModpackExportTask extends Task<Void> {
|
||||||
private final DefaultGameRepository repository;
|
private final DefaultGameRepository repository;
|
||||||
private final String versionId;
|
private final String versionId;
|
||||||
private final List<String> whitelist;
|
private final ModpackExportInfo exportInfo;
|
||||||
private final File output;
|
|
||||||
private final String modpackName;
|
|
||||||
private final String modpackAuthor;
|
|
||||||
private final String modpackVersion;
|
|
||||||
private final String modpackDescription;
|
|
||||||
private final String modpackFileApi;
|
|
||||||
|
|
||||||
public ServerModpackExportTask(DefaultGameRepository repository, String versionId, List<String> whitelist, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, String modpackFileApi, File output) {
|
public ServerModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo exportInfo) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.versionId = versionId;
|
this.versionId = version;
|
||||||
this.whitelist = whitelist;
|
this.exportInfo = exportInfo.validate();
|
||||||
this.output = output;
|
|
||||||
this.modpackName = modpackName;
|
|
||||||
this.modpackAuthor = modpackAuthor;
|
|
||||||
this.modpackVersion = modpackVersion;
|
|
||||||
this.modpackDescription = modpackDescription;
|
|
||||||
this.modpackFileApi = modpackFileApi;
|
|
||||||
|
|
||||||
onDone().register(event -> {
|
onDone().register(event -> {
|
||||||
if (event.isFailed()) output.delete();
|
if (event.isFailed()) exportInfo.getOutput().toFile().delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,11 +62,11 @@ public class ServerModpackExportTask extends Task<Void> {
|
|||||||
blackList.add(versionId + ".jar");
|
blackList.add(versionId + ".jar");
|
||||||
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(exportInfo.getOutput())) {
|
||||||
Path runDirectory = repository.getRunDirectory(versionId).toPath();
|
Path runDirectory = repository.getRunDirectory(versionId).toPath();
|
||||||
List<ModpackConfiguration.FileInformation> files = new ArrayList<>();
|
List<ModpackConfiguration.FileInformation> files = new ArrayList<>();
|
||||||
zip.putDirectory(runDirectory, "overrides", path -> {
|
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||||
if (Modpack.acceptFile(path, blackList, whitelist)) {
|
if (Modpack.acceptFile(path, blackList, exportInfo.getWhitelist())) {
|
||||||
Path file = runDirectory.resolve(path);
|
Path file = runDirectory.resolve(path);
|
||||||
if (Files.isRegularFile(file)) {
|
if (Files.isRegularFile(file)) {
|
||||||
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||||
@@ -102,8 +91,11 @@ public class ServerModpackExportTask extends Task<Void> {
|
|||||||
addons.add(new ServerModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion)));
|
addons.add(new ServerModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion)));
|
||||||
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
|
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
|
||||||
addons.add(new ServerModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion)));
|
addons.add(new ServerModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion)));
|
||||||
ServerModpackManifest manifest = new ServerModpackManifest(modpackName, modpackAuthor, modpackVersion, modpackDescription, StringUtils.removeSuffix(modpackFileApi, "/"), files, addons);
|
ServerModpackManifest manifest = new ServerModpackManifest(exportInfo.getName(), exportInfo.getAuthor(), exportInfo.getVersion(), exportInfo.getDescription(), StringUtils.removeSuffix(exportInfo.getFileApi(), "/"), files, addons);
|
||||||
zip.putTextFile(JsonUtils.GSON.toJson(manifest), "server-manifest.json");
|
zip.putTextFile(JsonUtils.GSON.toJson(manifest), "server-manifest.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options()
|
||||||
|
.requireFileApi(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.jackhuang.hmcl.util.gson;
|
||||||
|
|
||||||
|
public @interface JsonSubtype {
|
||||||
|
Class<?> clazz();
|
||||||
|
|
||||||
|
String name();
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.jackhuang.hmcl.util.gson;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface JsonType {
|
||||||
|
String property();
|
||||||
|
|
||||||
|
JsonSubtype[] subtypes();
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package org.jackhuang.hmcl.util.gson;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.google.gson.internal.Streams;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class JsonTypeAdapterFactory implements TypeAdapterFactory {
|
||||||
|
|
||||||
|
public static final JsonTypeAdapterFactory INSTANCE = new JsonTypeAdapterFactory();
|
||||||
|
|
||||||
|
private <T> TypeAdapter<T> createForJsonType(Gson gson, TypeToken<T> type) {
|
||||||
|
Class<? super T> rawType = type.getRawType();
|
||||||
|
JsonType jsonType = rawType.getDeclaredAnnotation(JsonType.class);
|
||||||
|
if (jsonType == null)
|
||||||
|
return null;
|
||||||
|
JsonSubtype[] subtypes = jsonType.subtypes();
|
||||||
|
Map<String, TypeAdapter<?>> labelTypeAdapterMap = new HashMap<>();
|
||||||
|
Map<Class<?>, TypeAdapter<?>> classTypeAdapterMap = new HashMap<>();
|
||||||
|
Map<Class<?>, JsonSubtype> classJsonSubtypeMap = new HashMap<>();
|
||||||
|
for (JsonSubtype subtype : subtypes) {
|
||||||
|
TypeAdapter<?> typeAdapter = gson.getDelegateAdapter(this, TypeToken.get(subtype.clazz()));
|
||||||
|
labelTypeAdapterMap.put(subtype.name(), typeAdapter);
|
||||||
|
classTypeAdapterMap.put(subtype.clazz(), typeAdapter);
|
||||||
|
classJsonSubtypeMap.put(subtype.clazz(), subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TypeAdapter<T>() {
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, T value) throws IOException {
|
||||||
|
Class<?> type = value.getClass();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeAdapter<T> delegate = (TypeAdapter<T>) classTypeAdapterMap.get(type);
|
||||||
|
if (delegate == null) {
|
||||||
|
throw new JsonParseException("Cannot serialize " + type.getName() + ". Please check your @JsonType configuration");
|
||||||
|
}
|
||||||
|
JsonSubtype subtype = classJsonSubtypeMap.get(type);
|
||||||
|
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
|
||||||
|
if (jsonObject.has(jsonType.property())) {
|
||||||
|
throw new JsonParseException("Cannot serialize " + type.getName() + ". Because it has already defined a field named '" + jsonType.property() + "'");
|
||||||
|
}
|
||||||
|
jsonObject.add(jsonType.property(), new JsonPrimitive(subtype.name()));
|
||||||
|
Streams.write(jsonObject, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(JsonReader in) {
|
||||||
|
JsonElement jsonElement = Streams.parse(in);
|
||||||
|
JsonElement typeLabelElement = jsonElement.getAsJsonObject().get(jsonType.property());
|
||||||
|
if (typeLabelElement == null) {
|
||||||
|
throw new JsonParseException("Cannot deserialize " + type + ". Because it does not define a field named '" + jsonType.property() + "'");
|
||||||
|
}
|
||||||
|
String typeLabel = typeLabelElement.getAsString();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeAdapter<T> delegate = (TypeAdapter<T>) labelTypeAdapterMap.get(typeLabel);
|
||||||
|
if (delegate == null) {
|
||||||
|
throw new JsonParseException("Cannot deserialize " + type + " with subtype '" + typeLabel + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.fromJsonTree(jsonElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private <T> TypeAdapter<T> createForJsonSubtype(Gson gson, TypeToken<T> type) {
|
||||||
|
Class<? super T> rawType = type.getRawType();
|
||||||
|
if (rawType.getSuperclass() == null) return null;
|
||||||
|
JsonType jsonType = rawType.getSuperclass().getDeclaredAnnotation(JsonType.class);
|
||||||
|
if (jsonType == null)
|
||||||
|
return null;
|
||||||
|
JsonSubtype jsonSubtype = null;
|
||||||
|
for (JsonSubtype subtype : jsonType.subtypes()) {
|
||||||
|
if (subtype.clazz() == rawType) {
|
||||||
|
jsonSubtype = subtype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jsonSubtype == null)
|
||||||
|
return null;
|
||||||
|
final JsonSubtype subtype = jsonSubtype;
|
||||||
|
|
||||||
|
TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
|
||||||
|
|
||||||
|
return new TypeAdapter<T>() {
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, T value) throws IOException {
|
||||||
|
Class<?> type = value.getClass();
|
||||||
|
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
|
||||||
|
if (jsonObject.has(jsonType.property())) {
|
||||||
|
throw new JsonParseException("Cannot serialize " + type.getName() + ". Because it has already defined a field named '" + jsonType.property() + "'");
|
||||||
|
}
|
||||||
|
jsonObject.add(jsonType.property(), new JsonPrimitive(subtype.name()));
|
||||||
|
Streams.write(jsonObject, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(JsonReader in) throws IOException {
|
||||||
|
return delegate.read(in);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||||
|
TypeAdapter<T> typeAdapter = createForJsonType(gson, type);
|
||||||
|
if (typeAdapter == null)
|
||||||
|
typeAdapter = createForJsonSubtype(gson, type);
|
||||||
|
return typeAdapter;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,29 +17,21 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util.gson;
|
package org.jackhuang.hmcl.util.gson;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author yushijinhun
|
* @author yushijinhun
|
||||||
*/
|
*/
|
||||||
public final class JsonUtils {
|
public final class JsonUtils {
|
||||||
|
|
||||||
public static final Gson GSON = new GsonBuilder()
|
public static final Gson GSON = defaultGsonBuilder().create();
|
||||||
.enableComplexMapKeySerialization()
|
|
||||||
.setPrettyPrinting()
|
|
||||||
.registerTypeAdapter(Date.class, DateTypeAdapter.INSTANCE)
|
|
||||||
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
|
|
||||||
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
|
|
||||||
.registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE)
|
|
||||||
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
private JsonUtils() {
|
private JsonUtils() {
|
||||||
}
|
}
|
||||||
@@ -58,4 +50,16 @@ public final class JsonUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GsonBuilder defaultGsonBuilder() {
|
||||||
|
return new GsonBuilder()
|
||||||
|
.enableComplexMapKeySerialization()
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.registerTypeAdapter(Date.class, DateTypeAdapter.INSTANCE)
|
||||||
|
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
|
||||||
|
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
|
||||||
|
.registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE)
|
||||||
|
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE)
|
||||||
|
.registerTypeAdapterFactory(JsonTypeAdapterFactory.INSTANCE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user