feat: MCBBS modpack
This commit is contained in:
@@ -18,10 +18,14 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
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 versionType;
|
||||
private String profileName;
|
||||
private String minecraftArgs;
|
||||
private String javaArgs;
|
||||
private List<String> gameArguments = new ArrayList<>();
|
||||
private List<String> javaArguments = new ArrayList<>();
|
||||
private Integer minMemory;
|
||||
private Integer maxMemory;
|
||||
private Integer metaspace;
|
||||
@@ -90,15 +94,17 @@ public class LaunchOptions implements Serializable {
|
||||
/**
|
||||
* User custom additional minecraft command line arguments.
|
||||
*/
|
||||
public String getMinecraftArgs() {
|
||||
return minecraftArgs;
|
||||
@NotNull
|
||||
public List<String> getGameArguments() {
|
||||
return Collections.unmodifiableList(gameArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* User custom additional java virtual machine command line arguments.
|
||||
*/
|
||||
public String getJavaArgs() {
|
||||
return javaArgs;
|
||||
@NotNull
|
||||
public List<String> getJavaArguments() {
|
||||
return Collections.unmodifiableList(javaArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,6 +208,150 @@ public class LaunchOptions implements Serializable {
|
||||
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) {
|
||||
options.gameDir = gameDir;
|
||||
return this;
|
||||
@@ -227,13 +377,15 @@ public class LaunchOptions implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMinecraftArgs(String minecraftArgs) {
|
||||
options.minecraftArgs = minecraftArgs;
|
||||
public Builder setGameArguments(List<String> gameArguments) {
|
||||
options.gameArguments.clear();
|
||||
options.gameArguments.addAll(gameArguments);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setJavaArgs(String javaArgs) {
|
||||
options.javaArgs = javaArgs;
|
||||
public Builder setJavaArguments(List<String> javaArguments) {
|
||||
options.javaArguments.clear();
|
||||
options.javaArguments.addAll(javaArguments);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +82,7 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
res.add(options.getJava().getBinary().toString());
|
||||
|
||||
if (StringUtils.isNotBlank(options.getJavaArgs()))
|
||||
res.addAllWithoutParsing(StringUtils.tokenize(options.getJavaArgs()));
|
||||
res.addAllWithoutParsing(options.getJavaArguments());
|
||||
|
||||
// JVM Args
|
||||
if (!options.isNoGeneratedJVMArgs()) {
|
||||
@@ -218,8 +217,7 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(options.getMinecraftArgs()))
|
||||
res.addAllWithoutParsing(StringUtils.tokenize(options.getMinecraftArgs()));
|
||||
res.addAllWithoutParsing(options.getGameArguments());
|
||||
|
||||
res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get());
|
||||
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.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.gson.JsonUtils;
|
||||
@@ -46,7 +47,7 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
||||
private final File output;
|
||||
|
||||
/**
|
||||
* @param output mod pack file.
|
||||
* @param output mod pack file.
|
||||
* @param versionId to locate version.json
|
||||
*/
|
||||
public MultiMCModpackExportTask(DefaultGameRepository repository, String versionId, List<String> whitelist, MultiMCInstanceConfiguration configuration, File output) {
|
||||
@@ -91,4 +92,6 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
||||
zip.putTextFile("", ".packignore");
|
||||
}
|
||||
}
|
||||
|
||||
public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options().requireMinMemory();
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
||||
}.getType());
|
||||
}
|
||||
} 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 {
|
||||
this.manifest = manifest;
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.mod.ModAdviser;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.ModpackExportInfo;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
@@ -43,27 +44,15 @@ import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
||||
public class ServerModpackExportTask extends Task<Void> {
|
||||
private final DefaultGameRepository repository;
|
||||
private final String versionId;
|
||||
private final List<String> whitelist;
|
||||
private final File output;
|
||||
private final String modpackName;
|
||||
private final String modpackAuthor;
|
||||
private final String modpackVersion;
|
||||
private final String modpackDescription;
|
||||
private final String modpackFileApi;
|
||||
private final ModpackExportInfo exportInfo;
|
||||
|
||||
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.versionId = versionId;
|
||||
this.whitelist = whitelist;
|
||||
this.output = output;
|
||||
this.modpackName = modpackName;
|
||||
this.modpackAuthor = modpackAuthor;
|
||||
this.modpackVersion = modpackVersion;
|
||||
this.modpackDescription = modpackDescription;
|
||||
this.modpackFileApi = modpackFileApi;
|
||||
this.versionId = version;
|
||||
this.exportInfo = exportInfo.validate();
|
||||
|
||||
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 + ".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())) {
|
||||
try (Zipper zip = new Zipper(exportInfo.getOutput())) {
|
||||
Path runDirectory = repository.getRunDirectory(versionId).toPath();
|
||||
List<ModpackConfiguration.FileInformation> files = new ArrayList<>();
|
||||
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||
if (Modpack.acceptFile(path, blackList, whitelist)) {
|
||||
if (Modpack.acceptFile(path, blackList, exportInfo.getWhitelist())) {
|
||||
Path file = runDirectory.resolve(path);
|
||||
if (Files.isRegularFile(file)) {
|
||||
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)));
|
||||
analyzer.getVersion(FABRIC).ifPresent(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");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author yushijinhun
|
||||
*/
|
||||
public final class JsonUtils {
|
||||
|
||||
public static final Gson GSON = 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)
|
||||
.create();
|
||||
public static final Gson GSON = defaultGsonBuilder().create();
|
||||
|
||||
private JsonUtils() {
|
||||
}
|
||||
@@ -58,4 +50,16 @@ public final class JsonUtils {
|
||||
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