Switch to gson-compile

This commit is contained in:
Johannes Frohnmeyer 2022-11-02 00:38:04 +01:00
parent 841fa6b3d4
commit 7ce6a764ec
Signed by: Johannes
GPG Key ID: E76429612C2929F4
97 changed files with 929 additions and 485 deletions

View File

@ -12,7 +12,8 @@ allprojects {
val lwjglVersion by extra("3.3.1")
val imguiVersion by extra("1.86.4")
val jfCommonsVersion by extra("2022.9.18+16-50-22")
val jfCommonsVersion by extra("2022.11.1+18-31-34")
val gsonCompileVersion by extra("1.0-SNAPSHOT")
val jlhttpVersion by extra("2.6")
val flavorProp: String by extra(prop("flavor", "custom"))
if (!setOf("custom", "maven", "fat", "windows", "linux", "macos").contains(flavorProp)) throw IllegalStateException("Unsupported flavor: $flavorProp")
@ -34,7 +35,7 @@ tasks.register("exportMetadata") {
"jvm": ${project(":common").extra["javaVersion"]},
"repositories": [
"https://repo.maven.apache.org/maven2/",
"https://gitlab.com/api/v4/projects/35745143/packages/maven"
"https://maven.frohnmeyer-wds.de/artifacts/"
],
"natives": {
"windows": [

View File

@ -0,0 +1,15 @@
import org.gradle.kotlin.dsl.extra
plugins {
id("inceptum.library-conventions")
}
dependencies {
api("io.gitlab.jfronny.gson:gson-compile-core:${rootProject.extra["gsonCompileVersion"]}")
compileOnly("io.gitlab.jfronny.gson:gson-compile-annotations:${rootProject.extra["gsonCompileVersion"]}")
annotationProcessor("io.gitlab.jfronny.gson:gson-compile-processor:${rootProject.extra["gsonCompileVersion"]}")
}
tasks.withType<JavaCompile> {
options.compilerArgs.add("-AgsonCompileNoReflect")
}

View File

@ -3,6 +3,7 @@ import io.gitlab.jfronny.scripts.*
plugins {
id("inceptum.library-conventions")
id("jf.codegen")
id("inceptum.gson-compile")
}
dependencies {

View File

@ -1,10 +1,9 @@
package io.gitlab.jfronny.inceptum.common;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateChannel;
import java.io.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -32,8 +31,8 @@ public class InceptumConfig {
saveConfig();
}
}
try (Reader reader = Files.newBufferedReader(MetaHolder.CONFIG_PATH);
JsonReader jr = GsonHolder.getGson().newJsonReader(reader)) {
try (JsonReader jr = new JsonReader(Files.newBufferedReader(MetaHolder.CONFIG_PATH))) {
jr.setLenient(true);
jr.beginObject();
while (jr.peek() != JsonToken.END_OBJECT) {
String name = null;
@ -70,8 +69,10 @@ public class InceptumConfig {
}
public static void saveConfig() {
try (Writer writer = Files.newBufferedWriter(MetaHolder.CONFIG_PATH);
JsonWriter jw = GsonHolder.getGson().newJsonWriter(writer)) {
try (JsonWriter jw = new JsonWriter(Files.newBufferedWriter(MetaHolder.CONFIG_PATH))) {
jw.setLenient(true);
jw.setOmitQuotes(true);
jw.setIndent(" ");
jw.beginObject()
.comment("Whether to show snapshots in the version selector for new instances")
.name("snapshots").value(snapshots)

View File

@ -3,7 +3,6 @@ package io.gitlab.jfronny.inceptum.common;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.commons.log.Logger;
import io.gitlab.jfronny.commons.log.StdoutLogger;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import java.io.IOException;
@ -11,7 +10,6 @@ public class InceptumEnvironmentInitializer {
public static void initialize() throws IOException {
Logger.registerFactory(InceptumEnvironmentInitializer::defaultFactory);
HttpUtils.setUserAgent("jfmods/inceptum/" + BuildMetadata.VERSION);
GsonHolder.register();
InceptumConfig.load();
}

View File

@ -2,13 +2,11 @@ package io.gitlab.jfronny.inceptum.common;
import io.gitlab.jfronny.commons.HashUtils;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.commons.cache.FileBackedOperationResultCache;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@ -17,7 +15,7 @@ import java.nio.file.Path;
import java.util.Map;
public class Net {
private static final FileBackedOperationResultCache OBJECT_CACHE = new FileBackedOperationResultCache(MetaHolder.CACHE_DIR);
private static final ObjectCache OBJECT_CACHE = new ObjectCache(MetaHolder.CACHE_DIR);
public static byte[] downloadData(String url) throws IOException, URISyntaxException {
try (InputStream is = HttpUtils.get(url).sendInputStream()) {
@ -32,38 +30,30 @@ public class Net {
return buf;
}
public static <T> T downloadObject(String url, Class<T> type) throws IOException {
return downloadObject(url, type, true);
public static <T> T downloadObject(String url, ThrowingFunction<String, T, IOException> func) throws IOException {
return downloadObject(url, func, true);
}
public static <T> T downloadObject(String url, Class<T> type, boolean cache) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type, cache);
public static <T> T downloadObject(String url, ThrowingFunction<String, T, IOException> func, boolean cache) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).sendString(), func, cache);
}
public static <T> T downloadObject(String url, Type type) throws IOException {
return downloadObject(url, type, true);
public static <T> T downloadObject(String url, ThrowingFunction<String, T, IOException> func, String apiKey) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).header("x-api-key", apiKey).sendString(), func, true);
}
public static <T> T downloadObject(String url, Type type, String apiKey) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).header("x-api-key", apiKey).sendString(), type, true);
public static <T> T downloadObject(String url, String sha1, ThrowingFunction<String, T, IOException> func) throws IOException {
return downloadObject(url, sha1, func, true);
}
public static <T> T downloadObject(String url, Type type, boolean cache) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type, cache);
public static <T> T downloadObject(String url, String sha1, ThrowingFunction<String, T, IOException> func, boolean cache) throws IOException {
return downloadObject(url, () -> downloadString(url, sha1), func, cache);
}
public static <T> T downloadObject(String url, String sha1, Class<T> type) throws IOException {
return downloadObject(url, sha1, type, true);
}
public static <T> T downloadObject(String url, String sha1, Class<T> type, boolean cache) throws IOException {
return downloadObject(url, () -> downloadString(url, sha1), type, cache);
}
private static <T> T downloadObject(String url, ThrowingSupplier<String, Exception> sourceString, Type type, boolean cache) throws IOException {
private static <T> T downloadObject(String url, ThrowingSupplier<String, Exception> sourceString, ThrowingFunction<String, T, IOException> func, boolean cache) throws IOException {
try {
ThrowingSupplier<T, Exception> builder = () -> GsonHolder.getGson().fromJson(sourceString.get(), type);
return cache ? OBJECT_CACHE.get(HashUtils.sha1(url.getBytes(StandardCharsets.UTF_8)), builder, type) : builder.get();
ThrowingSupplier<T, Exception> builder = () -> func.apply(sourceString.get());
return cache ? OBJECT_CACHE.get(HashUtils.sha1(url.getBytes(StandardCharsets.UTF_8)), sourceString, func) : builder.get();
} catch (Exception e) {
throw new IOException("Could not download object and no cache exists", e);
}

View File

@ -0,0 +1,39 @@
package io.gitlab.jfronny.inceptum.common;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ConcurrentHashMap;
public class ObjectCache {
private final ConcurrentHashMap<String, Object> container = new ConcurrentHashMap<>();
private final Path cacheDir;
public ObjectCache(Path cacheDir) {
this.cacheDir = cacheDir;
}
public void remove(String key) throws IOException {
container.remove(key);
Files.delete(cacheDir.resolve(key));
}
public void clear() throws IOException {
container.clear();
JFiles.clearDirectory(cacheDir);
}
public <T, TEx extends Throwable> T get(String key, ThrowingSupplier<String, ? extends TEx> download, ThrowingFunction<String, T, ? extends TEx> builder) throws IOException, TEx {
if (!container.containsKey(key)) {
Path cd = cacheDir.resolve(key);
if (Files.exists(cd)) container.put(key, builder.apply(Files.readString(cd)));
else container.put(key, builder.apply(download.get()));
}
//noinspection unchecked
return (T) container.get(key);
}
}

View File

@ -2,7 +2,6 @@ package io.gitlab.jfronny.inceptum.common;
import io.gitlab.jfronny.commons.ComparableVersion;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.api.GitlabApi;
import io.gitlab.jfronny.inceptum.common.api.MavenApi;
import io.gitlab.jfronny.inceptum.common.model.gitlab.*;
@ -62,7 +61,7 @@ public class Updater {
config.natives.put(Utils.getCurrentFlavor(), natives);
}
JFiles.writeObject(MetaHolder.WRAPPER_CONFIG_PATH, config);
GC_WrapperConfig.write(MetaHolder.WRAPPER_CONFIG_PATH, config);
if (relaunch) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
@ -103,7 +102,7 @@ public class Updater {
}
}
if (configChanged) JFiles.writeObject(MetaHolder.WRAPPER_CONFIG_PATH, wrapperConfig);
if (configChanged) GC_WrapperConfig.write(MetaHolder.WRAPPER_CONFIG_PATH, wrapperConfig);
return buildClasspath(Stream.concat(libs.stream(), natives.stream())).toList();
}
@ -152,16 +151,16 @@ public class Updater {
UpdateMetadata stable = null;
packageLoop:for (GitlabPackage info : GitlabApi.getPackages(project)) {
if (info.status.equals("default") && info.name.equals("io/gitlab/jfronny/inceptum/Inceptum")) {
pipelineLoop:for (GitlabPipeline pipeline : info.pipelines) {
if (!pipeline.ref.equals("master")) continue pipelineLoop;
for (GitlabPipeline pipeline : info.pipelines) {
if (!pipeline.ref.equals("master")) continue;
if (!pipeline.status.equals("success")) {
Utils.LOGGER.warn("Skipping failed CI build");
continue pipelineLoop;
continue;
}
for (GitlabJob job : GitlabApi.getJobs(project, pipeline.id)) {
if (!job.name.equals("build_test")) continue;
try {
UpdateMetadata update = Net.downloadObject(GitlabApi.PROJECTS + project.id + "/jobs/" + job.id + "/artifacts/version.json", UpdateMetadata.class);
UpdateMetadata update = Net.downloadObject(GitlabApi.PROJECTS + project.id + "/jobs/" + job.id + "/artifacts/version.json", GC_UpdateMetadata::read);
if (update.jvm > jvm) {
Utils.LOGGER.error("A newer JVM is required to use the latest inceptum version. Please update!");
continue packageLoop;

View File

@ -1,11 +1,12 @@
package io.gitlab.jfronny.inceptum.common.api;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.gson.reflect.TypeToken;
import io.gitlab.jfronny.gson.compile.util.GList;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.inceptum.common.model.gitlab.*;
import java.io.IOException;
import java.lang.reflect.Type;
import java.io.Reader;
import java.net.URISyntaxException;
import java.util.List;
import java.util.function.Predicate;
@ -16,32 +17,36 @@ public class GitlabApi {
public static final long PROJECT_ID = 30862253L;
public static final String PROJECT_MAVEN = "https://gitlab.com/api/v4/projects/" + PROJECT_ID + "/packages/maven/";
private static final Type packageInfoListType = new TypeToken<List<GitlabPackage>>() {}.getType();
private static final Type jobListType = new TypeToken<List<GitlabJob>>() {}.getType();
private static final Type packageFileInfoListType = new TypeToken<List<GitlabPackageFile>>() {}.getType();
public static GitlabProject getProject(Long projectId) throws IOException, URISyntaxException {
return HttpUtils.get(PROJECTS + projectId).sendSerialized(GitlabProject.class);
try (Reader r = HttpUtils.get(PROJECTS + projectId).sendReader()) {
return GC_GitlabProject.read(r);
}
}
public static List<GitlabPackage> getPackages(GitlabProject project) throws IOException, URISyntaxException {
return HttpUtils.get(PROJECTS + project.id + "/packages?order_by=created_at&sort=desc").sendSerialized(packageInfoListType);
try (JsonReader r = new JsonReader(HttpUtils.get(PROJECTS + project.id + "/packages?order_by=created_at&sort=desc").sendReader())) {
return GList.read(r, GC_GitlabPackage::read);
}
}
public static List<GitlabJob> getJobs(GitlabProject project, Long pipelineId) throws IOException, URISyntaxException {
List<GitlabJob> list = HttpUtils.get(PROJECTS + project.id + "/pipelines/" + pipelineId + "/jobs").sendSerialized(jobListType);
list.sort((left, right) -> right.created_at.compareTo(left.created_at));
return list;
try (JsonReader r = new JsonReader(HttpUtils.get(PROJECTS + project.id + "/pipelines/" + pipelineId + "/jobs").sendReader())) {
List<GitlabJob> list = GList.read(r, GC_GitlabJob::read);
list.sort((left, right) -> right.created_at.compareTo(left.created_at));
return list;
}
}
public static GitlabPackageFile getFile(GitlabProject project, GitlabPackage packageInfo, Predicate<GitlabPackageFile> isValid) throws IOException, URISyntaxException {
int page = 0;
while (true) {
List<GitlabPackageFile> files = HttpUtils.get(PROJECTS + project.id + "/packages/" + packageInfo.id + "/package_files?per_page=100&page=" + ++page).sendSerialized(packageFileInfoListType);
if (files.isEmpty()) return null;
for (GitlabPackageFile file : files) {
if (isValid.test(file))
return file;
try (JsonReader r = new JsonReader(HttpUtils.get(PROJECTS + project.id + "/packages/" + packageInfo.id + "/package_files?per_page=100&page=" + ++page).sendReader())) {
List<GitlabPackageFile> files = GList.read(r, GC_GitlabPackageFile::read);
if (files.isEmpty()) return null;
for (GitlabPackageFile file : files) {
if (isValid.test(file))
return file;
}
}
}
}

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class GitlabArtifact {
public String file_type;
public Long size;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class GitlabCommit {
public String id;
public String short_id;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class GitlabJob {
public Long id;
public String status;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class GitlabPackage {
public Long id;
public String name;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class GitlabPackageFile {
public Long id;
public Long package_id;

View File

@ -1,7 +1,10 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
@GSerializable
public class GitlabPipeline {
public long id;
public long project_id;

View File

@ -1,7 +1,10 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
@GSerializable
public class GitlabProject {
public Long id;
public String description;

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class GitlabRunner {
public Long id;
public String description;

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.common.model.gitlab;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class GitlabUser {
public long id;
public String name;

View File

@ -1,9 +1,11 @@
package io.gitlab.jfronny.inceptum.common.model.inceptum;
import io.gitlab.jfronny.commons.ComparableVersion;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.*;
@GSerializable
public class UpdateMetadata {
public Integer wrapperVersion;
public ComparableVersion version;
@ -12,4 +14,12 @@ public class UpdateMetadata {
public Integer jvm;
public Set<String> repositories;
public Map<String, Set<String>> natives;
// For serialization
public String getVersion() {
return version == null ? null : version.toString();
}
public void setVersion(String version) {
this.version = new ComparableVersion(version);
}
}

View File

@ -1,7 +1,10 @@
package io.gitlab.jfronny.inceptum.common.model.inceptum;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.*;
@GSerializable
public class WrapperConfig {
public Set<String> libraries;
public Set<String> repositories;

View File

@ -1,5 +1,6 @@
plugins {
id("inceptum.library-conventions")
id("inceptum.gson-compile")
}
dependencies {

View File

@ -1,16 +1,10 @@
package io.gitlab.jfronny.inceptum.launcher;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount;
import io.gitlab.jfronny.inceptum.launcher.gson.*;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.OauthTokenResponse;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.MinecraftArgument;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.Rules;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import java.io.IOException;
import java.nio.file.Files;
@ -27,12 +21,6 @@ public class LauncherEnv {
updateBackend(backend);
GsonHolder.registerTypeAdapter(MinecraftArgument.class, new MinecraftArgumentDeserializer());
GsonHolder.registerTypeAdapter(Rules.class, new RulesDeserializer());
GsonHolder.registerTypeAdapter(OauthTokenResponse.class, new OauthTokenResponseDeserializer());
GsonHolder.registerTypeAdapter(ModSource.class, new ModSourceTypeAdapter());
GsonHolder.registerTypeAdapter(ModSourceMapDeserializer.modSourceMapType, new ModSourceMapDeserializer());
InceptumEnvironmentInitializer.initialize();
if (!Files.exists(MetaHolder.CACHE_DIR)) Files.createDirectories(MetaHolder.CACHE_DIR);

View File

@ -6,8 +6,7 @@ import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeFile;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeMod;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.response.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.*;
import java.net.URISyntaxException;
import java.util.*;
@ -39,7 +38,7 @@ public class CurseforgeApi {
"gameVersion", gameVersion,
"pageSize", Integer.toString(pageSize),
"index", Integer.toString(page * pageSize)
)), SearchResponse.class, API_KEY);
)), GC_SearchResponse::read, API_KEY).data;
}
public static CurseforgeMod getMod(String slug) throws IOException {
@ -47,7 +46,7 @@ public class CurseforgeApi {
"gameId", "432",
"classId", "6",
"slug", slug
)), SearchResponse.class, API_KEY);
)), GC_SearchResponse::read, API_KEY);
if (response.pagination.totalCount != 1) {
throw new FileNotFoundException("Could not find mod with slug \"" + slug + "\"");
}
@ -55,8 +54,7 @@ public class CurseforgeApi {
}
public static CurseforgeMod getMod(int id) throws IOException {
GetModResponse response = Net.downloadObject(API_URL + "mods/" + id, GetModResponse.class, API_KEY);
return checkDistribution(response.data);
return checkDistribution(Net.downloadObject(API_URL + "mods/" + id, GC_GetModResponse::read, API_KEY).data);
}
private static CurseforgeMod checkDistribution(CurseforgeMod mod) {
@ -68,13 +66,13 @@ public class CurseforgeApi {
}
public static CurseforgeFile getFile(int modId, int fileId) throws IOException {
GetModFileResponse response = Net.downloadObject(API_URL + "mods/" + modId + "/files/" + fileId, GetModFileResponse.class, API_KEY);
return response.data;
return Net.downloadObject(API_URL + "mods/" + modId + "/files/" + fileId, GC_GetModFileResponse::read, API_KEY).data;
}
public static FingerprintMatchesResponse.Result checkFingerprint(long hash) throws IOException, URISyntaxException {
FingerprintMatchesResponse response = HttpUtils.post(API_URL + "fingerprints").bodyJson("{\"fingerprints\":[" + hash + "]}").sendSerialized(FingerprintMatchesResponse.class);
return response.data;
try (Reader r = HttpUtils.post(API_URL + "fingerprints").bodyJson("{\"fingerprints\":[" + hash + "]}").sendReader()) {
return GC_FingerprintMatchesResponse.read(r).data;
}
}
private static byte[] unsalt(byte[] data, int salt) {

View File

@ -1,30 +1,34 @@
package io.gitlab.jfronny.inceptum.launcher.api;
import io.gitlab.jfronny.gson.reflect.TypeToken;
import io.gitlab.jfronny.gson.compile.util.GList;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.inceptum.common.Net;
import io.gitlab.jfronny.inceptum.common.api.MavenApi;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricVersionLoaderInfo;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.*;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.*;
import io.gitlab.jfronny.inceptum.launcher.util.GameVersionParser;
import java.io.IOException;
import java.lang.reflect.Type;
import java.io.StringReader;
import java.util.*;
public class FabricMetaApi {
private static final Type fabricLoaderVersionListType = new TypeToken<List<FabricVersionLoaderInfo>>() {}.getType();
private static final String META_URL = "https://meta.fabricmc.net/";
public static List<FabricVersionLoaderInfo> getLoaderVersions(VersionsListInfo version) {
try {
return Net.downloadObject(META_URL + "v2/versions/loader/" + version.id, fabricLoaderVersionListType);
return Net.downloadObject(META_URL + "v2/versions/loader/" + version.id, s -> {
try (JsonReader r = new JsonReader(new StringReader(s))) {
return GList.read(r, GC_FabricVersionLoaderInfo::read);
}
});
} catch (IOException e) {
throw new RuntimeException("Could not get fabric loader versions", e);
}
}
public static FabricVersionLoaderInfo getLoaderVersion(String gameVersion, String fabricVersion) throws IOException {
return Net.downloadObject(META_URL + "v2/versions/loader/" + gameVersion + "/" + fabricVersion, FabricVersionLoaderInfo.WithMeta.class);
return Net.downloadObject(META_URL + "v2/versions/loader/" + gameVersion + "/" + fabricVersion, GC_FabricVersionLoaderInfo_WithMeta::read);
}
public static VersionInfo addFabric(VersionInfo version, String fabricVersion, FabricVersionInfoType type) throws IOException {

View File

@ -18,14 +18,14 @@ import static io.gitlab.jfronny.inceptum.common.Net.downloadObject;
public class McApi {
public static VersionsList getVersions() {
try {
return downloadObject("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json", VersionsList.class);
return downloadObject("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json", GC_VersionsList::read);
} catch (IOException e) {
throw new RuntimeException("Could not load version manifest", e);
}
}
public static VersionInfo getVersionInfo(VersionsListInfo listInfo) throws IOException {
return downloadObject(listInfo.url, listInfo.sha1, VersionInfo.class);
return downloadObject(listInfo.url, listInfo.sha1, GC_VersionInfo::read);
}
public static AssetIndex getAssetIndex(VersionInfo info) throws IOException, URISyntaxException {
@ -37,12 +37,12 @@ public class McApi {
} catch (IOException | URISyntaxException e) {
if (!Files.exists(file)) throw e;
}
return JFiles.readObject(file, AssetIndex.class);
return GC_AssetIndex.read(file);
}
public static Map<String, JvmFileInfo.File> getJvm(String component, int majorVersion) throws IOException {
// https://github.com/ATLauncher/ATLauncher/blob/master/src/main/java/com/atlauncher/constants/Constants.java#L123
JvmInfo info = Net.downloadObject("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json", JvmInfo.class);
JvmInfo info = Net.downloadObject("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json", GC_JvmInfo::read);
Map<String, List<JvmInfo.Jvm>> vms = switch (OSUtils.TYPE) {
case WINDOWS -> info.windowsX64;
case LINUX -> info.linux;
@ -53,14 +53,13 @@ public class McApi {
throw new IOException("Invalid component: " + component + " (available: " + String.join(", ", vms.keySet()));
for (JvmInfo.Jvm jvm : vmList) {
if (jvm.version.name.startsWith(Integer.toString(majorVersion))) {
return downloadObject(jvm.manifest.url, jvm.manifest.sha1, JvmFileInfo.class).files;
return downloadObject(jvm.manifest.url, jvm.manifest.sha1, GC_JvmFileInfo::read).files;
}
}
throw new IOException("JVM not found");
}
public static void downloadAsset(AssetIndex.Asset asset, Path path) throws IOException, URISyntaxException {
String url = "http://resources.download.minecraft.net/" + asset.hash.substring(0, 2) + "/" + asset.hash;
Net.downloadFile(url, asset.hash, path);
Net.downloadFile("http://resources.download.minecraft.net/" + asset.hash.substring(0, 2) + "/" + asset.hash, asset.hash, path);
}
}

View File

@ -1,17 +1,18 @@
package io.gitlab.jfronny.inceptum.launcher.api;
import io.gitlab.jfronny.gson.compile.util.GList;
import io.gitlab.jfronny.gson.reflect.TypeToken;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.inceptum.common.Net;
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.*;
import java.io.IOException;
import java.io.*;
import java.lang.reflect.Type;
import java.util.*;
public class ModrinthApi {
private static final String API_HOST = "https://api.modrinth.com/";
private static final int ITEMS_PER_PAGE = 20;
private static final Type modrinthVersionListType = new TypeToken<List<ModrinthVersion>>() {}.getType();
//TODO search by categories: facets:[["versions:$ver","versions:$ver"],["categories:$cat","categories:$cat"]]
//TODO filter server/client-only mods
@ -22,24 +23,28 @@ public class ModrinthApi {
"index", "relevance",
"offset", Integer.toString(page * ITEMS_PER_PAGE),
"limit", Integer.toString(ITEMS_PER_PAGE)
)), ModrinthSearchResult.class);
)), GC_ModrinthSearchResult::read);
}
public static ModrinthProject getMod(String id) throws IOException {
return Net.downloadObject(API_HOST + "v2/project/" + id, ModrinthProject.class);
return Net.downloadObject(API_HOST + "v2/project/" + id, GC_ModrinthProject::read);
}
public static List<ModrinthVersion> getVersions(String mod) throws IOException {
List<ModrinthVersion> versions = Net.downloadObject(API_HOST + "v2/project/" + mod + "/version", modrinthVersionListType);
List<ModrinthVersion> versions = Net.downloadObject(API_HOST + "v2/project/" + mod + "/version", s -> {
try (JsonReader r = new JsonReader(new StringReader(s))) {
return GList.read(r, GC_ModrinthVersion::read);
}
});
versions.sort(Comparator.comparing(version -> version.date_published));
return versions;
}
public static ModrinthVersion getVersion(String id) throws IOException {
return Net.downloadObject(API_HOST + "v2/version/" + id, ModrinthVersion.class);
return Net.downloadObject(API_HOST + "v2/version/" + id, GC_ModrinthVersion::read);
}
public static ModrinthVersion getVersionByHash(String sha1) throws IOException {
return Net.downloadObject(API_HOST + "v2/version_file/" + sha1 + "?algorithm=sha1", ModrinthVersion.class);
return Net.downloadObject(API_HOST + "v2/version_file/" + sha1 + "?algorithm=sha1", GC_ModrinthVersion::read);
}
}

View File

@ -1,20 +1,17 @@
package io.gitlab.jfronny.inceptum.launcher.api.account;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import io.gitlab.jfronny.gson.reflect.TypeToken;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.gson.compile.util.GList;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class AccountManager {
private static final Type abstractAccountListType = new TypeToken<List<MicrosoftAccount>>() {}.getType();
private static MicrosoftAccount SELECTED_ACCOUNT;
private static final List<MicrosoftAccount> ACCOUNTS = new ArrayList<>();
public static final AuthInfo NULL_AUTH = new AuthInfo("Joe", "2536abce90e8476a871679918164abc5", "99abe417230342cb8e9e2168ab46297a", "legacy");
@ -42,8 +39,8 @@ public class AccountManager {
}
public static void saveAccounts() {
try {
JFiles.writeObject(MetaHolder.ACCOUNTS_PATH, ACCOUNTS);
try (JsonWriter w = new JsonWriter(Files.newBufferedWriter(MetaHolder.ACCOUNTS_PATH))) {
GList.write(w, ACCOUNTS, GC_MicrosoftAccount::write);
} catch (IOException e) {
Utils.LOGGER.error("Could not save accounts", e);
}
@ -52,8 +49,8 @@ public class AccountManager {
public static void loadAccounts() {
Utils.LOGGER.info("Loading accounts");
if (Files.exists(MetaHolder.ACCOUNTS_PATH)) {
try {
ACCOUNTS.addAll(JFiles.readObject(MetaHolder.ACCOUNTS_PATH, abstractAccountListType));
try (JsonReader r = new JsonReader(Files.newBufferedReader(MetaHolder.ACCOUNTS_PATH))) {
ACCOUNTS.addAll(GList.read(r, GC_MicrosoftAccount::read));
} catch (IOException e) {
Utils.LOGGER.error("Could not load accounts", e);
}

View File

@ -1,14 +1,17 @@
package io.gitlab.jfronny.inceptum.launcher.api.account;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.common.R;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.launcher.gson.MicrosoftAccountMeta;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.*;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
@GSerializable(with = MicrosoftAccountMeta.class)
public class MicrosoftAccount {
/**
* Auto generated serial.
@ -56,6 +59,30 @@ public class MicrosoftAccount {
*/
public boolean mustLogin;
public MicrosoftAccount(MicrosoftAccountMeta meta) {
this.accountId = meta.accountId();
this.minecraftUsername = meta.minecraftUsername();
this.uuid = meta.uuid();
this.accessToken = meta.accessToken();
this.oauthToken = meta.oauthToken();
this.xstsAuth = meta.xstsAuth();
this.accessTokenExpiresAt = meta.accessTokenExpiresAt();
this.mustLogin = meta.mustLogin();
}
public MicrosoftAccountMeta toMeta() {
return new MicrosoftAccountMeta(
accountId,
minecraftUsername,
uuid,
accessToken,
oauthToken,
xstsAuth,
accessTokenExpiresAt,
mustLogin
);
}
public MicrosoftAccount(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse,
LoginResponse loginResponse, Profile profile) {
update(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
@ -65,14 +92,14 @@ public class MicrosoftAccount {
LoginResponse loginResponse, Profile profile) {
this.oauthToken = oauthTokenResponse;
this.xstsAuth = xstsAuthResponse;
this.accessToken = loginResponse.accessToken;
this.accessToken = loginResponse.accessToken();
this.minecraftUsername = profile.name;
this.uuid = profile.id;
this.accountId = loginResponse.username;
this.accountId = loginResponse.username();
this.mustLogin = false;
this.accessTokenExpiresAt = new Date();
this.accessTokenExpiresAt.setTime(this.accessTokenExpiresAt.getTime() + (loginResponse.expiresIn * 1000));
this.accessTokenExpiresAt.setTime(this.accessTokenExpiresAt.getTime() + (loginResponse.expiresIn() * 1000));
}
public String getAccessToken() {
@ -125,10 +152,10 @@ public class MicrosoftAccount {
AccountManager.saveAccounts();
}
if (force || new Date().after(this.xstsAuth.notAfter)) {
if (force || new Date().after(this.xstsAuth.notAfter())) {
Utils.LOGGER.info("xsts auth expired. Attempting to get new auth");
XboxLiveAuthResponse xboxLiveAuthResponse = MicrosoftAuthAPI.getXBLToken(this.oauthToken.accessToken);
this.xstsAuth = MicrosoftAuthAPI.getXstsToken(xboxLiveAuthResponse.token);
this.xstsAuth = MicrosoftAuthAPI.getXstsToken(xboxLiveAuthResponse.token());
if (xstsAuth == null) {
mustLogin = true;
@ -150,12 +177,12 @@ public class MicrosoftAccount {
return false;
}
this.accessToken = loginResponse.accessToken;
this.accountId = loginResponse.username;
this.accessToken = loginResponse.accessToken();
this.accountId = loginResponse.username();
this.accessTokenExpiresAt = new Date();
this.accessTokenExpiresAt
.setTime(this.accessTokenExpiresAt.getTime() + (loginResponse.expiresIn * 1000));
.setTime(this.accessTokenExpiresAt.getTime() + (loginResponse.expiresIn() * 1000));
AccountManager.saveAccounts();
}
@ -171,7 +198,7 @@ public class MicrosoftAccount {
}
private String getIdentityToken() {
return "XBL3.0 x=" + xstsAuth.displayClaims.xui.get(0).uhs + ";" + xstsAuth.token;
return "XBL3.0 x=" + xstsAuth.displayClaims().xui().get(0).uhs() + ";" + xstsAuth.token();
}
public boolean ensureAccessTokenValid() {

View File

@ -1,9 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.api.account;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.inceptum.launcher.api.account.request.*;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.*;
import java.io.IOException;
import java.io.Reader;
import java.net.URISyntaxException;
import java.util.*;
@ -28,75 +30,81 @@ public class MicrosoftAuthAPI {
public static final String MICROSOFT_MINECRAFT_ENTITLEMENTS_URL = "https://api.minecraftservices.com/entitlements/license?requestId=";
public static OauthTokenResponse tradeCodeForAccessToken(String code) throws IOException, URISyntaxException {
return HttpUtils.post(MICROSOFT_AUTH_TOKEN_URL)
try (Reader r = HttpUtils.post(MICROSOFT_AUTH_TOKEN_URL)
.bodyForm(Map.of("client_id", MICROSOFT_LOGIN_CLIENT_ID,
"code", code,
"grant_type", "authorization_code",
"redirect_uri", MICROSOFT_LOGIN_REDIRECT_URL,
"scope", String.join(" ", MICROSOFT_LOGIN_SCOPES)))
.sendSerialized(OauthTokenResponse.class);
.sendReader()) {
return GC_OauthTokenResponse.read(r);
}
}
public static OauthTokenResponse refreshAccessToken(String refreshToken) throws IOException, URISyntaxException {
return HttpUtils.post(MICROSOFT_AUTH_TOKEN_URL)
try (Reader r = HttpUtils.post(MICROSOFT_AUTH_TOKEN_URL)
.bodyForm(Map.of("client_id", MICROSOFT_LOGIN_CLIENT_ID,
"refresh_token", refreshToken,
"grant_type", "refresh_token",
"redirect_uri", MICROSOFT_LOGIN_REDIRECT_URL))
.sendSerialized(OauthTokenResponse.class);
.sendReader()) {
return GC_OauthTokenResponse.read(r);
}
}
public static XboxLiveAuthResponse getXBLToken(String accessToken) throws IOException, URISyntaxException {
Map<Object, Object> properties = new HashMap<>();
properties.put("AuthMethod", "RPS");
properties.put("SiteName", "user.auth.xboxlive.com");
properties.put("RpsTicket", "d=" + accessToken);
Map<Object, Object> data = new HashMap<>();
data.put("Properties", properties);
data.put("RelyingParty", "http://auth.xboxlive.com");
data.put("TokenType", "JWT");
return HttpUtils.post(MICROSOFT_XBL_AUTH_TOKEN_URL)
try (Reader r = HttpUtils.post(MICROSOFT_XBL_AUTH_TOKEN_URL)
.header("x-xbl-contract-version", "1")
.bodySerialized(data)
.sendSerialized(XboxLiveAuthResponse.class);
.bodyJson(GC_XblTokenRequest.toJson(new XblTokenRequest(
new XblTokenRequest.Properties(
"RPS",
"user.auth.xboxlive.com",
"d=" + accessToken
),
"http://auth.xboxlive.com",
"JWT"
)))
.sendReader()) {
return GC_XboxLiveAuthResponse.read(r);
}
}
public static XboxLiveAuthResponse getXstsToken(String xblToken) throws IOException, URISyntaxException {
Map<Object, Object> properties = new HashMap<>();
properties.put("SandboxId", "RETAIL");
List<String> userTokens = new ArrayList<>();
userTokens.add(xblToken);
properties.put("UserTokens", userTokens);
Map<Object, Object> data = new HashMap<>();
data.put("Properties", properties);
data.put("RelyingParty", "rp://api.minecraftservices.com/");
data.put("TokenType", "JWT");
return HttpUtils.post(MICROSOFT_XSTS_AUTH_TOKEN_URL)
try (Reader r = HttpUtils.post(MICROSOFT_XSTS_AUTH_TOKEN_URL)
.header("x-xbl-contract-version", "1")
.bodySerialized(data)
.sendSerialized(XboxLiveAuthResponse.class);
.bodyJson(GC_XstsTokenRequest.toJson(new XstsTokenRequest(
new XstsTokenRequest.Properties(
"RETAIL",
List.of(xblToken)
),
"rp://api.minecraftservices.com/",
"JWT"
)))
.sendReader()) {
return GC_XboxLiveAuthResponse.read(r);
}
}
public static LoginResponse loginToMinecraft(String xstsToken) throws IOException, URISyntaxException {
Map<Object, Object> data = new HashMap<>();
data.put("xtoken", xstsToken);
data.put("platform", "PC_LAUNCHER");
return HttpUtils.post(MICROSOFT_MINECRAFT_LOGIN_URL)
.bodySerialized(data)
.sendSerialized(LoginResponse.class);
try (Reader r = HttpUtils.post(MICROSOFT_MINECRAFT_LOGIN_URL)
.bodyJson(GC_LoginRequest.toJson(new LoginRequest(
xstsToken,
"PC_LAUNCHER"
)))
.sendReader()) {
return GC_LoginResponse.read(r);
}
}
public static Entitlements getEntitlements(String accessToken) throws IOException, URISyntaxException {
return HttpUtils.get(MICROSOFT_MINECRAFT_ENTITLEMENTS_URL + UUID.randomUUID()).bearer(accessToken).sendSerialized(Entitlements.class);
try (Reader r = HttpUtils.get(MICROSOFT_MINECRAFT_ENTITLEMENTS_URL + UUID.randomUUID()).bearer(accessToken).sendReader()) {
return GC_Entitlements.read(r);
}
}
public static Profile getMcProfile(String accessToken) throws IOException, URISyntaxException {
return HttpUtils.get(MICROSOFT_MINECRAFT_PROFILE_URL).bearer(accessToken).sendSerialized(Profile.class);
try (Reader r = HttpUtils.get(MICROSOFT_MINECRAFT_PROFILE_URL).bearer(accessToken).sendReader()) {
return GC_Profile.read(r);
}
}
}

View File

@ -61,8 +61,8 @@ public class MicrosoftAuthServer implements Closeable {
}
private void addAccount(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse, LoginResponse loginResponse, Profile profile) {
if (this.previous != null || AccountManager.isAccountByName(loginResponse.username)) {
MicrosoftAccount account = (MicrosoftAccount) AccountManager.getAccountByName(loginResponse.username);
if (this.previous != null || AccountManager.isAccountByName(loginResponse.username())) {
MicrosoftAccount account = (MicrosoftAccount) AccountManager.getAccountByName(loginResponse.username());
if (account == null) {
return;
@ -91,7 +91,7 @@ public class MicrosoftAuthServer implements Closeable {
private void acquireXBLToken(OauthTokenResponse oauthTokenResponse) throws Exception {
XboxLiveAuthResponse xblAuthResponse = MicrosoftAuthAPI.getXBLToken(oauthTokenResponse.accessToken);
acquireXsts(oauthTokenResponse, xblAuthResponse.token);
acquireXsts(oauthTokenResponse, xblAuthResponse.token());
}
private void acquireXsts(OauthTokenResponse oauthTokenResponse, String xblToken) throws Exception {
@ -103,8 +103,8 @@ public class MicrosoftAuthServer implements Closeable {
}
private void acquireMinecraftToken(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse) throws Exception {
String xblUhs = xstsAuthResponse.displayClaims.xui.get(0).uhs;
String xblXsts = xstsAuthResponse.token;
String xblUhs = xstsAuthResponse.displayClaims().xui().get(0).uhs();
String xblXsts = xstsAuthResponse.token();
LoginResponse loginResponse = MicrosoftAuthAPI.loginToMinecraft("XBL3.0 x=" + xblUhs + ";" + xblXsts);
@ -112,7 +112,7 @@ public class MicrosoftAuthServer implements Closeable {
throw new Exception("Failed to login to Minecraft");
}
Entitlements entitlements = MicrosoftAuthAPI.getEntitlements(loginResponse.accessToken);
Entitlements entitlements = MicrosoftAuthAPI.getEntitlements(loginResponse.accessToken());
if (!(entitlements.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("product_minecraft"))
&& entitlements.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("game_minecraft")))) {
@ -123,7 +123,7 @@ public class MicrosoftAuthServer implements Closeable {
Profile profile = null;
try {
profile = MicrosoftAuthAPI.getMcProfile(loginResponse.accessToken);
profile = MicrosoftAuthAPI.getMcProfile(loginResponse.accessToken());
} catch (Exception e) {
LauncherEnv.showError("""
No Minecraft profiles were found for this account. Have you purchased Minecraft?

View File

@ -0,0 +1,7 @@
package io.gitlab.jfronny.inceptum.launcher.api.account.request;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public record LoginRequest(String xtoken, String platform) {
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.api.account.request;
import io.gitlab.jfronny.gson.annotations.SerializedName;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public record XblTokenRequest(@SerializedName("Properties") Properties properties,
@SerializedName("RelyingParty") String relyingParty,
@SerializedName("TokenType") String tokenType) {
@GSerializable
public record Properties(@SerializedName("AuthMethod") String authMethod,
@SerializedName("SiteName") String siteName,
@SerializedName("RpsTicket") String rpsTicket) {
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.api.account.request;
import io.gitlab.jfronny.gson.annotations.SerializedName;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
@GSerializable
public record XstsTokenRequest(@SerializedName("Properties") Properties properties,
@SerializedName("RelyingParty") String relyingParty,
@SerializedName("TokenType") String tokenType) {
@GSerializable
public record Properties(@SerializedName("SandboxId") String sandboxId, @SerializedName("UserTokens") List<String> userTokens) {
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.*;
import java.io.IOException;
import java.util.Date;
@GSerializable
public record MicrosoftAccountMeta(String accountId, String minecraftUsername, String uuid, String accessToken, OauthTokenResponse oauthToken, XboxLiveAuthResponse xstsAuth, Date accessTokenExpiresAt, boolean mustLogin) {
public static final long SERIAL_VERSION_UID = 5483749902584257559L;
public static void write(JsonWriter writer, MicrosoftAccount value) throws IOException {
GC_MicrosoftAccountMeta.write(writer, value == null ? null : value.toMeta());
}
public static MicrosoftAccount read(JsonReader reader) throws IOException {
MicrosoftAccountMeta meta = GC_MicrosoftAccountMeta.read(reader);
return meta == null ? null : new MicrosoftAccount(meta);
}
}

View File

@ -0,0 +1,32 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.gson.compile.util.GList;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.*;
import java.io.IOException;
import java.util.*;
public class MinecraftArgumentAdapter {
public static void write(JsonWriter writer, MinecraftArgument rules) throws IOException {
throw new UnsupportedOperationException();
}
public static MinecraftArgument read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.STRING) return new MinecraftArgument(Set.of(reader.nextString()));
Rules rules = null;
List<String> value = null;
reader.beginObject();
while (reader.hasNext()) {
switch (reader.nextName()) {
case "rules" -> rules = GC_Rules.read(reader);
case "value" -> value = GList.read(reader, JsonReader::nextString);
}
}
reader.endObject();
if (rules == null || value == null) throw new JsonParseException("Not a valid minecraft argument");
if (!rules.allow()) return new MinecraftArgument(Set.of());
return new MinecraftArgument(Set.copyOf(value));
}
}

View File

@ -1,34 +0,0 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.MinecraftArgument;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.Rules;
import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set;
public class MinecraftArgumentDeserializer implements JsonDeserializer<MinecraftArgument> {
@Override
public MinecraftArgument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) {
return new MinecraftArgument(Set.of(json.getAsString()));
} else if (json.isJsonObject()) {
JsonObject jo = json.getAsJsonObject();
if (jo.size() != 2 || !jo.has("rules") || !jo.has("value"))
throw new JsonParseException("Not a valid minecraft argument");
Rules r = context.deserialize(jo.get("rules"), Rules.class);
if (!r.allow()) return new MinecraftArgument(Set.of());
Set<String> sel = new LinkedHashSet<>();
JsonElement value = jo.get("value");
if (value.isJsonArray()) {
for (JsonElement val : value.getAsJsonArray()) {
sel.add(val.getAsString());
}
} else if (value.isJsonPrimitive())
sel.add(value.getAsString());
else throw new JsonParseException("Unexpected value type");
return new MinecraftArgument(Set.copyOf(sel));
} else throw new JsonParseException("Not a valid minecraft argument");
}
}

View File

@ -0,0 +1,28 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta$Sources;
import io.gitlab.jfronny.inceptum.launcher.system.source.GC_ModSource;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import java.io.IOException;
import java.util.Optional;
public class ModMetaSourcesAdapter {
public static void write(JsonWriter writer, ModMeta$Sources value) throws IOException {
writer.beginArray();
for (ModSource source : value.keySet()) GC_ModSource.write(writer, source);
writer.endArray();
}
public static ModMeta$Sources read(JsonReader reader) throws IOException {
reader.beginArray();
ModMeta$Sources sources = new ModMeta$Sources();
while (reader.hasNext()) {
sources.put(GC_ModSource.read(reader), Optional.empty());
}
reader.endArray();
return sources;
}
}

View File

@ -0,0 +1,96 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.JsonParseException;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.inceptum.launcher.system.source.*;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;
public class ModSourceAdapter {
public static void write(JsonWriter writer, ModSource src) throws IOException {
writer.beginObject();
if (src instanceof ModrinthModSource mo) {
writer.name("type").value("modrinth")
.name("id").value(mo.getVersionId());
} else if (src instanceof DirectModSource di) {
writer.name("type").value("direct")
.name("fileName").value(di.fileName())
.name("url").value(di.url())
.name("dependencies");
writer.beginArray();
for (ModSource dependency : di.dependencies()) {
write(writer, dependency);
}
writer.endArray();
} else if (src instanceof CurseforgeModSource cu) {
writer.name("type").value("curseforge");
if (cu.getShortName().matches("\\d+")) {
writer.name("projectId").value(cu.getProjectId());
} else {
writer.name("project").value(cu.getShortName());
}
writer.name("fileId").value(cu.getFileId());
} else throw new RuntimeException("ModSources with the type " + src.getClass() + " are not supported");
writer.endObject();
}
public static ModSource read(JsonReader reader) throws IOException {
String type = null;
String mr$id = null;
Integer cf$projectId = null;
String cf$project = null;
Integer cf$fileId = null;
String direct$fileName = null;
String direct$url = null;
Set<ModSource> direct$dependencies = null;
reader.beginObject();
while (reader.hasNext()) {
switch (reader.nextName()) {
case "type" -> type = reader.nextString();
case "id" -> mr$id = reader.nextString();
case "projectId" -> cf$projectId = reader.nextInt();
case "project" -> cf$project = reader.nextString();
case "fileId" -> cf$fileId = reader.nextInt();
case "fileName" -> direct$fileName = reader.nextString();
case "url" -> direct$url = reader.nextString();
case "dependencies" -> {
direct$dependencies = new LinkedHashSet<>();
reader.beginArray();
while (reader.hasNext()) direct$dependencies.add(read(reader));
reader.endArray();
}
}
}
reader.endObject();
if (type == null) throw new JsonParseException("Expected ModSource to contain a type");
return switch (type) {
case "modrinth" -> {
if (mr$id == null) throw new JsonParseException("Expected ModrinthModSource to contain a version ID");
yield new ModrinthModSource(mr$id);
}
case "curseforge" -> {
if (cf$fileId == null)
throw new JsonParseException("Expected a fileId in this curseforge project");
if (cf$projectId != null) yield new CurseforgeModSource(cf$projectId, cf$fileId);
else if (cf$project != null) yield new CurseforgeModSource(cf$project, cf$fileId);
else throw new JsonParseException("Expected a projectId in this curseforge project");
}
case "direct" -> {
if (direct$fileName == null) throw new JsonParseException("Expected direct download to have a fileName");
if (direct$url == null) throw new JsonParseException("Expected direct download to have a url");
if (direct$dependencies == null) yield new DirectModSource(direct$fileName, direct$url, Set.of());
else {
yield new DirectModSource(direct$fileName, direct$url, direct$dependencies);
}
}
default -> throw new JsonParseException("Unexpected ModSource type: " + type);
};
}
}

View File

@ -1,31 +0,0 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.gson.reflect.TypeToken;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import java.lang.reflect.Type;
import java.util.*;
public class ModSourceMapDeserializer implements JsonSerializer<Map<ModSource, Optional<ModSource>>>, JsonDeserializer<Map<ModSource, Optional<ModSource>>> {
public static final Type modSourceMapType = new TypeToken<Map<ModSource, Optional<ModSource>>>() {}.getType();
@Override
public Map<ModSource, Optional<ModSource>> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!json.isJsonArray()) throw new JsonParseException("Not an array");
Map<ModSource, Optional<ModSource>> res = new LinkedHashMap<>();
for (JsonElement element : json.getAsJsonArray()) {
res.put(context.deserialize(element, ModSource.class), Optional.empty());
}
return res;
}
@Override
public JsonElement serialize(Map<ModSource, Optional<ModSource>> src, Type typeOfSrc, JsonSerializationContext context) {
JsonArray res = new JsonArray();
for (ModSource source : src.keySet()) {
res.add(context.serialize(source, ModSource.class));
}
return res;
}
}

View File

@ -1,79 +0,0 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.gson.reflect.TypeToken;
import io.gitlab.jfronny.inceptum.launcher.system.source.*;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Set;
public class ModSourceTypeAdapter implements JsonSerializer<ModSource>, JsonDeserializer<ModSource> {
private static final Type modSourceSetType = new TypeToken<Set<ModSource>>() {}.getType();
@Override
public ModSource deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!(json instanceof JsonObject jo))
throw new JsonParseException("Expected ModSource to be an object");
if (!jo.has("type"))
throw new JsonParseException("Expected ModSource to contain a type");
return switch (jo.get("type").getAsString()) {
case "modrinth" -> {
if (!jo.has("id"))
throw new JsonParseException("Expected ModrinthModSource to contain a version ID");
try {
yield new ModrinthModSource(jo.get("id").getAsString());
} catch (IOException e) {
throw new JsonParseException("Could not fetch Modrinth source", e);
}
}
case "curseforge" -> {
if (!jo.has("fileId"))
throw new JsonParseException("Expected a fileId in this curseforge project");
try {
if (jo.has("projectId")) {
yield new CurseforgeModSource(jo.get("projectId").getAsInt(), jo.get("fileId").getAsInt());
} else if (jo.has("project")) {
yield new CurseforgeModSource(jo.get("project").getAsString(), jo.get("fileId").getAsInt());
} else throw new JsonParseException("Expected a projectId in this curseforge project");
} catch (IOException e) {
throw new JsonParseException("Could not fetch Curseforge source", e);
}
}
case "direct" -> {
if (!jo.has("fileName"))
throw new JsonParseException("Expected direct download to have a fileName");
if (!jo.has("url"))
throw new JsonParseException("Expected direct download to have a url");
if (jo.has("dependencies"))
yield new DirectModSource(jo.get("fileName").getAsString(), jo.get("url").getAsString(), context.deserialize(jo.get("dependencies"), modSourceSetType));
else
yield new DirectModSource(jo.get("fileName").getAsString(), jo.get("url").getAsString(), Set.of());
}
default -> throw new JsonParseException("Unexpected ModSource type: " + jo.get("type").getAsString());
};
}
@Override
public JsonElement serialize(ModSource src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jo = new JsonObject();
if (src instanceof ModrinthModSource mo) {
jo.add("type", new JsonPrimitive("modrinth"));
jo.add("id", new JsonPrimitive(mo.getVersionId()));
} else if (src instanceof DirectModSource di) {
jo.add("type", new JsonPrimitive("direct"));
jo.add("fileName", new JsonPrimitive(di.fileName()));
jo.add("url", new JsonPrimitive(di.url()));
jo.add("dependencies", context.serialize(di.dependencies()));
} else if (src instanceof CurseforgeModSource cu) {
jo.add("type", new JsonPrimitive("curseforge"));
if (cu.getShortName().matches("\\d+")) {
jo.add("projectId", new JsonPrimitive(cu.getProjectId()));
} else {
jo.add("project", new JsonPrimitive(cu.getShortName()));
}
jo.add("fileId", new JsonPrimitive(cu.getFileId()));
} else throw new RuntimeException("ModSources with the type " + src.getClass() + " are not supported");
return jo;
}
}

View File

@ -1,36 +0,0 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.OauthTokenResponse;
import java.lang.reflect.Type;
import java.util.Date;
public class OauthTokenResponseDeserializer implements JsonDeserializer<OauthTokenResponse> {
@Override
public OauthTokenResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
OauthTokenResponse oauthTokenResponse = new OauthTokenResponse();
JsonObject rootObject = json.getAsJsonObject();
oauthTokenResponse.tokenType = rootObject.get("token_type").getAsString();
oauthTokenResponse.expiresIn = rootObject.get("expires_in").getAsInt();
oauthTokenResponse.scope = rootObject.get("scope").getAsString();
oauthTokenResponse.accessToken = rootObject.get("access_token").getAsString();
oauthTokenResponse.refreshToken = rootObject.get("refresh_token").getAsString();
oauthTokenResponse.userId = rootObject.get("user_id").getAsString();
if (rootObject.has("foci")) {
oauthTokenResponse.foci = rootObject.get("foci").isJsonNull() ? null : rootObject.get("foci").getAsString();
}
if (rootObject.has("expires_at")) {
oauthTokenResponse.expiresAt = context.deserialize(rootObject.get("expires_at"), Date.class);
} else {
oauthTokenResponse.expiresAt = new Date();
oauthTokenResponse.expiresAt
.setTime(oauthTokenResponse.expiresAt.getTime() + (oauthTokenResponse.expiresIn * 1000));
}
return oauthTokenResponse;
}
}

View File

@ -0,0 +1,60 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.Rules;
import java.io.IOException;
public class RulesAdapter {
public static void write(JsonWriter writer, Rules rules) throws IOException {
throw new UnsupportedOperationException();
}
public static Rules read(JsonReader reader) throws IOException {
boolean valid = true;
reader.beginArray();
while (reader.hasNext()) {
if (!valid) {
reader.skipValue();
continue;
}
reader.beginObject();
String actionType = null;
boolean hasFeatures = false;
String osName = null;
String osVersion = null;
while (reader.hasNext()) {
switch (reader.nextName()) {
case "action" -> actionType = reader.nextString();
case "features" -> {
reader.skipValue();
hasFeatures = true;
}
case "os" -> {
reader.beginObject();
while (reader.hasNext()) {
switch (reader.nextName()) {
case "name" -> osName = reader.nextString();
case "version" -> osVersion = reader.nextString();
}
}
reader.endObject();
}
}
}
reader.endObject();
if (actionType == null || !actionType.equals("allow") && !actionType.equals("disallow")) {
throw new JsonParseException("Unexpected action in argument: " + actionType);
}
if (!hasFeatures) valid = false;
if (osName != null && !OSUtils.TYPE.getMojName().equals(osName)) valid = false;
if (osVersion != null && !System.getProperty("os.version").matches(osVersion)) valid = false;
if (actionType.equals("disallow")) valid = !valid;
}
reader.endArray();
return new Rules(valid);
}
}

View File

@ -1,44 +0,0 @@
package io.gitlab.jfronny.inceptum.launcher.gson;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.Rules;
import java.lang.reflect.Type;
public class RulesDeserializer implements JsonDeserializer<Rules> {
@Override
public Rules deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
boolean valid = true;
for (JsonElement rule : json.getAsJsonArray()) {
JsonObject ro = rule.getAsJsonObject();
String actionType = ro.get("action").getAsJsonPrimitive().getAsString();
if (!actionType.equals("allow") && !actionType.equals("disallow")) {
throw new JsonParseException("Unexpected action in argument: " + actionType);
}
boolean matched = true;
if (ro.has("features")) {
matched = false;
}
if (ro.has("os")) {
JsonObject osObject = ro.get("os").getAsJsonObject();
if (osObject.has("name")) {
if (!OSUtils.TYPE.getMojName().equals(osObject.get("name").getAsString())) {
matched = false;
}
}
if (osObject.has("version")) {
if (!System.getProperty("os.version").matches(osObject.get("version").getAsString())) {
matched = false;
}
}
}
if (actionType.equals("disallow")) matched = !matched;
if (!matched) {
valid = false;
break;
}
}
return new Rules(valid);
}
}

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.model.curseforge;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class CurseforgeFile {
public Integer id;
public Integer gameId;
@ -45,6 +48,7 @@ public class CurseforgeFile {
public Long fileFingerprint; // murmur5 hash
public List<Module> modules;
@GSerializable
public static class Hash {
public String value;
/* Possible values:
@ -53,6 +57,7 @@ public class CurseforgeFile {
public Integer algo;
}
@GSerializable
public static class GameVersion {
public String gameVersionName;
public String gameVersionPadded;
@ -61,6 +66,7 @@ public class CurseforgeFile {
public Integer gameVersionTypeId;
}
@GSerializable
public static class Dependency {
public Integer modId;
/* Possible values:
@ -73,6 +79,7 @@ public class CurseforgeFile {
public Integer relationType;
}
@GSerializable
public static class Module {
public String name;
public Long fingerprint;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.model.curseforge;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class CurseforgeMod {
public Integer id;
public Integer gameId;
@ -41,6 +44,7 @@ public class CurseforgeMod {
public Boolean isAvailable;
public Integer thumbsUpCount;
@GSerializable
public static class Links {
public String websiteUrl;
public String wikiUrl;
@ -48,6 +52,7 @@ public class CurseforgeMod {
public String sourcesUrl;
}
@GSerializable
public static class Category {
public Integer id;
public Integer gameId;
@ -61,12 +66,14 @@ public class CurseforgeMod {
public Integer primaryCategoryId;
}
@GSerializable
public static class Author {
public Integer id;
public String name;
public String url;
}
@GSerializable
public static class Logo {
public Integer id;
public Integer modId;
@ -76,6 +83,7 @@ public class CurseforgeMod {
public String url;
}
@GSerializable
public static class Screenshot {
public Integer id;
public Integer modId;
@ -85,6 +93,7 @@ public class CurseforgeMod {
public String url;
}
@GSerializable
public static class LatestFileIndex {
public String gameVersion;
public Integer fileId;

View File

@ -1,7 +1,10 @@
package io.gitlab.jfronny.inceptum.launcher.model.curseforge;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Set;
@GSerializable
public class CurseforgeModpackManifest {
public Minecraft minecraft;
public String manifestType;
@ -12,16 +15,19 @@ public class CurseforgeModpackManifest {
public Set<File> files;
public String overrides;
@GSerializable
public static class Minecraft {
public String version;
public Set<ModLoader> modLoaders;
@GSerializable
public static class ModLoader {
public String id;
public Boolean primary;
}
}
@GSerializable
public static class File {
public Integer projectID;
public Integer fileID;

View File

@ -1,12 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.model.curseforge.response;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeFile;
import java.util.List;
@GSerializable
public class FingerprintMatchesResponse {
public Result data;
@GSerializable
public static class Result {
public Boolean isCacheBuilt;
public List<Match> exactMatches;
@ -15,6 +18,7 @@ public class FingerprintMatchesResponse {
public List<Integer> installedFingerprints;
public List<Integer> unmatchedFingerprints;
@GSerializable
public static class Match {
public Integer id;
public CurseforgeFile file;

View File

@ -1,7 +1,9 @@
package io.gitlab.jfronny.inceptum.launcher.model.curseforge.response;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeFile;
@GSerializable
public class GetModFileResponse {
public CurseforgeFile data;
}

View File

@ -1,7 +1,9 @@
package io.gitlab.jfronny.inceptum.launcher.model.curseforge.response;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeMod;
@GSerializable
public class GetModResponse {
public CurseforgeMod data;
}

View File

@ -1,13 +1,16 @@
package io.gitlab.jfronny.inceptum.launcher.model.curseforge.response;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeMod;
import java.util.List;
@GSerializable
public class SearchResponse {
public List<CurseforgeMod> data;
public Pagination pagination;
@GSerializable
public static class Pagination {
public Integer index;
public Integer pageSite;

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.model.fabric;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class FabricLoaderVersion {
public String separator;
public Integer build;

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.model.fabric;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class FabricModJson {
public String id;
public String name;

View File

@ -1,30 +1,38 @@
package io.gitlab.jfronny.inceptum.launcher.model.fabric;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
@GSerializable
public class FabricVersionLoaderInfo {
public FabricLoaderVersion loader;
public IntermediaryVersion intermediary;
@GSerializable
public static class WithMeta extends FabricVersionLoaderInfo {
public LauncherMeta launcherMeta;
@GSerializable
public static class LauncherMeta {
public int version;
public Libraries libraries;
public MainClass mainClass;
@GSerializable
public static class Libraries {
public List<Library> client;
public List<Library> common;
public List<Library> server;
@GSerializable
public static class Library {
public String name;
public String url;
}
}
@GSerializable
public static class MainClass {
public String client;
public String server;

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.model.fabric;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class IntermediaryVersion {
public String maven;
public String version;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.model.inceptum;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
import java.util.Objects;
@GSerializable
public class InstanceMeta {
public String version;
public String java;
@ -11,6 +14,7 @@ public class InstanceMeta {
public Long lastLaunched;
public Arguments arguments;
@GSerializable
public static class Arguments {
public List<String> jvm;
public List<String> client;

View File

@ -0,0 +1,78 @@
package io.gitlab.jfronny.inceptum.launcher.model.inceptum;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.launcher.gson.ModMetaSourcesAdapter;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@GSerializable(with = ModMetaSourcesAdapter.class)
public class ModMeta$Sources implements Map<ModSource, Optional<ModSource>> {
private Map<ModSource, Optional<ModSource>> delegate = new LinkedHashMap<>();
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return delegate.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return delegate.containsValue(o);
}
@Override
public Optional<ModSource> get(Object o) {
return delegate.get(o);
}
@Nullable
@Override
public Optional<ModSource> put(ModSource modSource, Optional<ModSource> modSource2) {
return delegate.put(modSource, modSource2);
}
@Override
public Optional<ModSource> remove(Object o) {
return delegate.remove(o);
}
@Override
public void putAll(@NotNull Map<? extends ModSource, ? extends Optional<ModSource>> map) {
delegate.putAll(map);
}
@Override
public void clear() {
delegate.clear();
}
@NotNull
@Override
public Set<ModSource> keySet() {
return delegate.keySet();
}
@NotNull
@Override
public Collection<Optional<ModSource>> values() {
return delegate.values();
}
@NotNull
@Override
public Set<Entry<ModSource, Optional<ModSource>>> entrySet() {
return delegate.entrySet();
}
}

View File

@ -1,6 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.model.inceptum;
import io.gitlab.jfronny.commons.HashUtils;
import io.gitlab.jfronny.gson.compile.annotations.GPrefer;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.response.FingerprintMatchesResponse;
import io.gitlab.jfronny.inceptum.launcher.api.CurseforgeApi;
@ -14,14 +16,19 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@GSerializable
public class ModMeta {
public Map<ModSource, Optional<ModSource>> sources; //key: source, value: update
public ModMeta$Sources sources; //key: source, value: update
public String sha1;
public Long murmur2;
public List<String> dependents; // by file name
public List<String> dependencies; // by file name
public boolean explicit = true;
@GPrefer
public ModMeta() {
}
public boolean initialize(String gameVersion) {
boolean modrinth = false;
boolean curseforge = false;
@ -56,7 +63,7 @@ public class ModMeta {
public static ModMeta of(Path mod) {
ModMeta res = new ModMeta();
res.sources = new LinkedHashMap<>();
res.sources = new ModMeta$Sources();
if (!Files.isDirectory(mod)) {
try {
byte[] data = Files.readAllBytes(mod);
@ -73,7 +80,7 @@ public class ModMeta {
public static ModMeta of(String sha1, Long murmur2, @Nullable ModSource knownSource, String gameVersion) {
ModMeta res = new ModMeta();
res.sources = new LinkedHashMap<>();
res.sources = new ModMeta$Sources();
res.sha1 = sha1;
res.murmur2 = murmur2;
res.dependents = new ArrayList<>();

View File

@ -1,11 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.model.microsoft;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
@GSerializable
public class Entitlements {
public List<StoreItem> items;
public String signature;
@GSerializable
public static class StoreItem {
public String name;
public String signature;

View File

@ -1,16 +1,12 @@
package io.gitlab.jfronny.inceptum.launcher.model.microsoft;
import io.gitlab.jfronny.gson.annotations.SerializedName;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
public class LoginResponse {
public String username;
@SerializedName("access_token")
public String accessToken;
@SerializedName("token_type")
public String tokenType;
@SerializedName("expires_in")
public Integer expiresIn;
//TODO test SerializedName
@GSerializable
public record LoginResponse(String username,
@SerializedName("access_token") String accessToken,
@SerializedName("token_type") String tokenType,
@SerializedName("expires_in") Integer expiresIn) {
}

View File

@ -1,9 +1,12 @@
package io.gitlab.jfronny.inceptum.launcher.model.microsoft;
import io.gitlab.jfronny.gson.annotations.SerializedName;
import io.gitlab.jfronny.gson.compile.annotations.GPrefer;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
@GSerializable
public class OauthTokenResponse {
@SerializedName("token_type")
public String tokenType;
@ -26,4 +29,20 @@ public class OauthTokenResponse {
public String userId;
public String foci;
@GPrefer
public OauthTokenResponse(String tokenType, Integer expiresIn, Date expiresAt, String scope, String accessToken, String refreshToken, String userId, String foci) {
this.tokenType = tokenType;
this.expiresIn = expiresIn;
this.expiresAt = expiresAt;
this.scope = scope;
this.accessToken = accessToken;
this.refreshToken = refreshToken;
this.userId = userId;
this.foci = foci;
if (this.expiresAt == null) {
this.expiresAt = new Date();
this.expiresAt.setTime(this.expiresAt.getTime() + this.expiresIn * 1000);
}
}
}

View File

@ -1,13 +1,17 @@
package io.gitlab.jfronny.inceptum.launcher.model.microsoft;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
@GSerializable
public class Profile {
public String id;
public String name;
public List<Skin> skins;
public List<Cape> capes;
@GSerializable
public static class Skin {
public String id;
public String state;
@ -16,6 +20,7 @@ public class Profile {
public String alias;
}
@GSerializable
public static class Cape {
public String id;
}

View File

@ -1,28 +1,21 @@
package io.gitlab.jfronny.inceptum.launcher.model.microsoft;
import io.gitlab.jfronny.gson.annotations.SerializedName;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
public class XboxLiveAuthResponse {
@SerializedName("IssueInstant")
public Date issueInstant;
@SerializedName("NotAfter")
public Date notAfter;
@SerializedName("Token")
public String token;
@SerializedName("DisplayClaims")
public DisplayClaims displayClaims;
public static class DisplayClaims {
public List<XUIClaim> xui;
public static class XUIClaim {
public String uhs;
//TODO test SerializedName
@GSerializable
public record XboxLiveAuthResponse(@SerializedName("IssueInstant") Date issueInstant,
@SerializedName("NotAfter") Date notAfter,
@SerializedName("Token") String token,
@SerializedName("DisplayClaims") DisplayClaims displayClaims) {
@GSerializable
public record DisplayClaims(List<XUIClaim> xui) {
@GSerializable
public record XUIClaim(String uhs) {
}
}
}

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.model.modrinth;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class ModrinthHashes { //TODO ensure this can parse with additional hashes
public String sha1;
public String sha512;

View File

@ -1,9 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.model.modrinth;
import io.gitlab.jfronny.gson.annotations.SerializedName;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
@GSerializable
public class ModrinthModpackManifest {
public Integer formatVersion; // 1
public String game; // "minecraft"
@ -13,6 +15,7 @@ public class ModrinthModpackManifest {
public List<File> files;
public Dependencies dependencies;
@GSerializable
public static class File {
public String path;
public ModrinthHashes hashes;
@ -20,12 +23,14 @@ public class ModrinthModpackManifest {
public List<String> downloads;
public Long fileSize;
@GSerializable
public static class Env {
public ModrinthDependencyType client;
public ModrinthDependencyType server;
}
}
@GSerializable
public static class Dependencies { // All are nullable
public String minecraft;
public String forge;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.model.modrinth;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class ModrinthProject {
public String id;
public String slug;
@ -30,18 +33,21 @@ public class ModrinthProject {
public List<DonationUrl> donation_urls;
public List<GalleryItem> gallery;
@GSerializable
public static class DonationUrl {
public String id;
public String platform;
public String url;
}
@GSerializable
public static class License {
public String id;
public String name;
public String url;
}
@GSerializable
public static class GalleryItem {
public String url;
public Boolean featured;

View File

@ -1,14 +1,18 @@
package io.gitlab.jfronny.inceptum.launcher.model.modrinth;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class ModrinthSearchResult {
public List<ModResult> hits;
public Integer offset;
public Integer limit;
public Integer total_hits;
@GSerializable
public static class ModResult {
public String project_id;
public ModrinthProjectType project_type;

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.model.modrinth;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
import java.util.List;
@GSerializable
public class ModrinthVersion {
public String id;
public String project_id;
@ -24,6 +27,7 @@ public class ModrinthVersion {
alpha, beta, release
}
@GSerializable
public static class File {
public ModrinthHashes hashes;
public String url;
@ -31,6 +35,7 @@ public class ModrinthVersion {
public Boolean primary;
}
@GSerializable
public static class Dependency {
public String version_id;
public String project_id;

View File

@ -1,10 +1,14 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Map;
@GSerializable
public class AssetIndex {
public Map<String, Asset> objects;
@GSerializable
public static class Asset {
public String hash;
public int size;

View File

@ -1,15 +1,20 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Map;
@GSerializable
public class JvmFileInfo {
public Map<String, File> files;
@GSerializable
public static class File {
public Downloads downloads;
public boolean executable;
public String type;
@GSerializable
public static class Downloads {
public MojangFileDownload lzma;
public MojangFileDownload raw;

View File

@ -1,10 +1,12 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.annotations.SerializedName;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
import java.util.Map;
@GSerializable
public class JvmInfo {
public Map<String, List<Jvm>> linux;
@SerializedName("mac-os")
@ -12,16 +14,19 @@ public class JvmInfo {
@SerializedName("windows-x64")
public Map<String, List<Jvm>> windowsX64;
@GSerializable
public static class Jvm {
public Availability availability;
public MojangFileDownload manifest;
public Version version;
@GSerializable
public static class Availability {
public int group;
public int progress;
}
@GSerializable
public static class Version {
public String name;
public String released;

View File

@ -1,8 +1,12 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.launcher.gson.MinecraftArgumentAdapter;
import java.util.LinkedHashSet;
import java.util.Set;
@GSerializable(with = MinecraftArgumentAdapter.class)
public record MinecraftArgument(Set<String> arg) implements Cloneable {
@Override
protected MinecraftArgument clone() {

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
@GSerializable
public class MojangFileDownload implements Cloneable {
public String sha1;
public int size;

View File

@ -1,5 +1,9 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.launcher.gson.RulesAdapter;
@GSerializable(with = RulesAdapter.class)
public record Rules(boolean allow) implements Cloneable {
@Override
protected Rules clone() {

View File

@ -1,8 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.*;
@SuppressWarnings("MethodDoesntCallSuperMethod")
@GSerializable
public class VersionInfo extends VersionsListInfo implements Cloneable {
public Arguments arguments;
public AssetIndex assetIndex;
@ -31,6 +34,7 @@ public class VersionInfo extends VersionsListInfo implements Cloneable {
return clone;
}
@GSerializable
public static class Arguments implements Cloneable {
public List<MinecraftArgument> game;
public List<MinecraftArgument> jvm;
@ -46,6 +50,7 @@ public class VersionInfo extends VersionsListInfo implements Cloneable {
}
}
@GSerializable
public static class AssetIndex extends MojangFileDownload {
public String id;
public int totalSize;
@ -60,6 +65,7 @@ public class VersionInfo extends VersionsListInfo implements Cloneable {
}
}
@GSerializable
public static class Downloads implements Cloneable {
public MojangFileDownload client;
public MojangFileDownload client_mappings;
@ -77,6 +83,7 @@ public class VersionInfo extends VersionsListInfo implements Cloneable {
}
}
@GSerializable
public static class JavaVersion implements Cloneable {
public String component;
public int majorVersion;
@ -90,6 +97,7 @@ public class VersionInfo extends VersionsListInfo implements Cloneable {
}
}
@GSerializable
public static class Library implements Cloneable {
public Downloads downloads;
public String name;
@ -107,6 +115,7 @@ public class VersionInfo extends VersionsListInfo implements Cloneable {
return clone;
}
@GSerializable
public static class Downloads implements Cloneable {
public Artifact artifact;
public Map<String, Artifact> classifiers;
@ -120,6 +129,7 @@ public class VersionInfo extends VersionsListInfo implements Cloneable {
return clone;
}
@GSerializable
public static class Artifact extends MojangFileDownload implements Cloneable {
public String path;

View File

@ -1,11 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
@GSerializable
public class VersionsList {
public Latest latest;
public List<VersionsListInfo> versions;
@GSerializable
public static class Latest {
public String release;
public String snapshot;

View File

@ -1,7 +1,10 @@
package io.gitlab.jfronny.inceptum.launcher.model.mojang;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.Date;
@GSerializable
public class VersionsListInfo {
public String id;
public String type;

View File

@ -1,11 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.model.multimc;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import java.util.List;
@GSerializable
public class MMCPackMeta {
public List<Component> components;
public Integer formatVersion;
@GSerializable
public static class Component {
public Boolean dependencyOnly;
public Boolean important;

View File

@ -1,8 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.system.exporter;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.InceptumConfig;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.GC_CurseforgeModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource;
@ -38,7 +38,7 @@ public class CurseForgeExporter extends Exporter<CurseforgeModpackManifest> {
loader.primary = true;
manifest.minecraft.modLoaders.add(loader);
}
JFiles.writeObject(root.resolve("manifest.json"), manifest);
GC_CurseforgeModpackManifest.write(root.resolve("manifest.json"), manifest);
return manifest;
}
@ -58,6 +58,6 @@ public class CurseForgeExporter extends Exporter<CurseforgeModpackManifest> {
Files.createDirectories(modsOverrides);
Files.copy(mod.getJarPath(), modsOverrides.resolve(mod.getJarPath().getFileName().toString()));
}
JFiles.writeObject(root.resolve("manifest.json"), manifest);
GC_CurseforgeModpackManifest.write(root.resolve("manifest.json"), manifest);
}
}

View File

@ -1,8 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.system.exporter;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.GC_ModrinthModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.ModrinthModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModrinthModSource;
@ -31,7 +31,7 @@ public class ModrinthExporter extends Exporter<ModrinthModpackManifest> {
if (instance.isFabric()) {
manifest.dependencies.fabricLoader = instance.getLoaderVersion();
}
JFiles.writeObject(root.resolve("modrinth.index.json"), manifest);
GC_ModrinthModpackManifest.write(root.resolve("modrinth.index.json"), manifest);
return manifest;
}
@ -51,6 +51,6 @@ public class ModrinthExporter extends Exporter<ModrinthModpackManifest> {
Files.createDirectories(modsOverrides);
Files.copy(mod.getJarPath(), modsOverrides.resolve(mod.getJarPath().getFileName().toString()));
}
JFiles.writeObject(root.resolve("modrinth.index.json"), manifest);
GC_ModrinthModpackManifest.write(root.resolve("modrinth.index.json"), manifest);
}
}

View File

@ -1,8 +1,8 @@
package io.gitlab.jfronny.inceptum.launcher.system.exporter;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.multimc.GC_MMCPackMeta;
import io.gitlab.jfronny.inceptum.launcher.model.multimc.MMCPackMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod;
import java.io.IOException;
@ -70,7 +70,7 @@ public class MultiMCExporter extends Exporter<MMCPackMeta> {
fabric.version = instance.getLoaderVersion();
manifest.components.add(fabric);
}
JFiles.writeObject(root.resolve("mmc-pack.json"), manifest);
GC_MMCPackMeta.write(root.resolve("mmc-pack.json"), manifest);
return manifest;
}
}

View File

@ -1,11 +1,13 @@
package io.gitlab.jfronny.inceptum.launcher.system.importer;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.GC_CurseforgeModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_ModMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.ModPath;
import io.gitlab.jfronny.inceptum.launcher.system.source.*;
import io.gitlab.jfronny.inceptum.launcher.util.*;
import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModDownload;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
import java.io.IOException;
import java.nio.file.Files;
@ -14,7 +16,7 @@ import java.util.Set;
public class CurseForgeImporter extends Importer<CurseforgeModpackManifest> {
public CurseForgeImporter() {
super("CurseForge", "manifest.json", CurseforgeModpackManifest.class);
super("CurseForge", "manifest.json", GC_CurseforgeModpackManifest::read);
}
@Override
@ -45,7 +47,7 @@ public class CurseForgeImporter extends Importer<CurseforgeModpackManifest> {
ModDownload download = source.download();
ModMeta imod = ModMeta.of(download.sha1(), download.murmur2(), source, manifest.minecraft.version);
Files.createDirectories(modsPath);
JFiles.writeObject(modsPath.resolve(source.getShortName() + ModPath.EXT_IMOD), imod);
GC_ModMeta.write(modsPath.resolve(source.getShortName() + ModPath.EXT_IMOD), imod);
}
}
}

View File

@ -1,10 +1,12 @@
package io.gitlab.jfronny.inceptum.launcher.system.importer;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.*;
import io.gitlab.jfronny.inceptum.launcher.system.setup.Steps;
@ -18,9 +20,9 @@ import java.nio.file.Path;
public abstract class Importer<T> {
private final String name;
private final String manifestFile;
private final Class<T> manifestClass;
private final ThrowingFunction<Path, T, IOException> manifestClass;
public Importer(String name, String manifestFile, Class<T> manifestClass) {
public Importer(String name, String manifestFile, ThrowingFunction<Path, T, IOException> manifestClass) {
this.name = name;
this.manifestFile = manifestFile;
this.manifestClass = manifestClass;
@ -47,7 +49,7 @@ public abstract class Importer<T> {
public Path importPack(Path sourceRoot, ProcessState state) throws IOException {
state.incrementStep("Generating skeleton");
T manifest = JFiles.readObject(sourceRoot.resolve(manifestFile), manifestClass);
T manifest = manifestClass.apply(sourceRoot.resolve(manifestFile));
IntermediaryManifest man = validateManifest(manifest, sourceRoot);
String name = man.name();
if (name == null || !Utils.VALID_FILENAME.matcher(name).matches()) name = "New Pack";
@ -56,7 +58,7 @@ public abstract class Importer<T> {
Instance.setSetupLock(iDir, true);
InstanceMeta meta = new InstanceMeta();
meta.version = createVersionString(man.gameVersion(), man.fabricVersion());
JFiles.writeObject(iDir.resolve("instance.json"), meta);
GC_InstanceMeta.write(iDir.resolve("instance.json"), meta);
state.incrementStep("Downloading mods");
downloadMods(manifest, iDir, state);

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.inceptum.launcher.system.importer;
import io.gitlab.jfronny.inceptum.common.Net;
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.GC_ModrinthModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.ModrinthModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
@ -11,7 +12,7 @@ import java.util.ArrayList;
public class ModrinthImporter extends Importer<ModrinthModpackManifest> {
public ModrinthImporter() {
super("Modrinth", "modrinth.index.json", ModrinthModpackManifest.class);
super("Modrinth", "modrinth.index.json", GC_ModrinthModpackManifest::read);
}
@Override

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.inceptum.launcher.system.importer;
import io.gitlab.jfronny.inceptum.launcher.model.multimc.GC_MMCPackMeta;
import io.gitlab.jfronny.inceptum.launcher.model.multimc.MMCPackMeta;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
@ -10,7 +11,7 @@ import java.util.stream.Stream;
public class MultiMCImporter extends Importer<MMCPackMeta> {
public MultiMCImporter() {
super("MultiMC", "mmc-pack.json", MMCPackMeta.class);
super("MultiMC", "mmc-pack.json", GC_MMCPackMeta::read);
}
@Override

View File

@ -3,6 +3,7 @@ package io.gitlab.jfronny.inceptum.launcher.system.instance;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.R;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.util.GameVersionParser;
@ -131,7 +132,7 @@ public record Instance(String id, Path path, InstanceMeta meta, ModsDirScanner m
public void writeMeta() {
try {
JFiles.writeObject(path.resolve("instance.json"), meta);
GC_InstanceMeta.write(path.resolve("instance.json"), meta);
} catch (IOException e) {
Utils.LOGGER.error("Could not write instance config", e);
}

View File

@ -1,17 +1,16 @@
package io.gitlab.jfronny.inceptum.launcher.system.instance;
import io.gitlab.jfronny.commons.cache.FileBackedRef;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.commons.throwable.*;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.util.FileBackedRef;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.*;
import java.util.*;
public class InstanceList {
@ -55,7 +54,7 @@ public class InstanceList {
if (!metas.containsKey(instancePath)) {
metas.put(instancePath, new IEntry(
instancePath,
new FileBackedRef<>(instancePath.resolve("instance.json"), InstanceMeta.class)
new FileBackedRef<>(instancePath.resolve("instance.json"), GC_InstanceMeta::read)
));
}
return metas.get(instancePath).toPub();

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.inceptum.launcher.system.instance;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_ModMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModDownload;
@ -31,7 +31,7 @@ public class ModManager {
public record DownloadMeta(ModDownload download, ModMeta meta, ModSource source, Path metaFile) {
public void write() throws IOException {
JFiles.writeObject(metaFile, meta);
GC_ModMeta.write(metaFile, meta);
}
}
}

View File

@ -6,14 +6,13 @@ import io.gitlab.jfronny.inceptum.common.api.MavenApi;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.*;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.instance.*;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.*;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
import io.gitlab.jfronny.inceptum.launcher.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.launcher.api.account.AuthInfo;
import io.gitlab.jfronny.inceptum.launcher.system.setup.steps.DownloadLibrariesStep;
import io.gitlab.jfronny.inceptum.launcher.system.instance.ModPath;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import io.gitlab.jfronny.inceptum.launcher.util.*;
@ -101,17 +100,9 @@ public class InstanceLauncher {
// Fabric imods
if (instance.isFabric()) {
StringBuilder fabricAddMods = new StringBuilder("-Dfabric.addMods=");
if (Files.exists(instance.modsDir())) {
for (Path imod : JFiles.list(instance.modsDir(), path -> ModPath.isImod(path) && ModPath.isEnabled(path))) {
String fn = imod.getFileName().toString();
if (Files.exists(imod.getParent().resolve(fn.substring(0, fn.length() - 5))))
continue;
Map<ModSource, Optional<ModSource>> sources = JFiles.readObject(imod, ModMeta.class).sources;
if (sources.isEmpty()) throw new LaunchException(".imod without attached jar contains no sources");
ModSource ms = List.copyOf(sources.keySet()).get(0);
Path p = ms.getJarPath().toAbsolutePath();
if (!Files.exists(p)) ms.download();
fabricAddMods.append(p);
for (Mod mod : instance.getMods()) {
if (mod.isEnabled() && mod.needsInject()) {
fabricAddMods.append(mod.getJarPath());
fabricAddMods.append(File.pathSeparatorChar);
}
}

View File

@ -4,6 +4,8 @@ import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.gson.JsonParseException;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.GC_FabricModJson;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_ModMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod;
@ -45,7 +47,7 @@ public record FileScanTask(ProtoInstance instance, Path file, BiConsumer<Path, M
private <TEx extends Throwable> void evaluateSources(Path modFile, MetadataRef ref) throws IOException, TEx {
boolean modified = false;
if (ref.meta.initialize(gameVersion)) {
JFiles.writeObject(ref.imodPath, ref.meta);
GC_ModMeta.write(ref.imodPath, ref.meta);
modified = true;
}
ModSource selectedSource = null;
@ -77,14 +79,14 @@ public record FileScanTask(ProtoInstance instance, Path file, BiConsumer<Path, M
try (FileSystem fs = Utils.openZipFile(modJarDefault, false)) {
Path fmjPath = fs.getPath("fabric.mod.json");
if (!Files.exists(fmjPath)) return null;
return JFiles.readObject(fmjPath, FabricModJson.class);
return GC_FabricModJson.read(fmjPath);
}
}
private static class MetadataRef {
public MetadataRef(Path imodPath, @Nullable Function<Path, ModMeta> defaultMeta) throws IOException {
this.imodPath = imodPath;
if (!Files.exists(imodPath) && defaultMeta != null) JFiles.writeObject(imodPath, defaultMeta.apply(imodPath));
if (!Files.exists(imodPath) && defaultMeta != null) GC_ModMeta.write(imodPath, defaultMeta.apply(imodPath));
if (Files.exists(imodPath)) update();
}
@ -93,7 +95,7 @@ public record FileScanTask(ProtoInstance instance, Path file, BiConsumer<Path, M
public ModMeta meta;
public void update() throws IOException {
this.meta = JFiles.readObject(imodPath, ModMeta.class);
this.meta = GC_ModMeta.read(imodPath);
}
}
}

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.inceptum.launcher.system.mds;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_ModMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.*;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
@ -131,6 +131,6 @@ public class MdsMod extends Mod {
}
private void write() throws IOException {
JFiles.writeObject(imodPath, meta);
GC_ModMeta.write(imodPath, meta);
}
}

View File

@ -36,8 +36,7 @@ class ModsDirScannerImpl implements ModsDirScanner {
protected static ModsDirScannerImpl get(Path modsDir, InstanceMeta instance) throws IOException {
if (SCANNERS.containsKey(modsDir)) {
ModsDirScannerImpl mds = SCANNERS.get(modsDir);
if (mds.instance.equals(instance))
return mds;
if (mds.instance.meta.equals(instance)) return mds;
mds.close();
}
ModsDirScannerImpl mds = new ModsDirScannerImpl(modsDir, instance);
@ -153,14 +152,19 @@ class ModsDirScannerImpl implements ModsDirScanner {
public static void closeAll() {
for (ModsDirScannerImpl value : SCANNERS.values().toArray(ModsDirScannerImpl[]::new)) {
value.close();
try {
value.close();
} catch (IOException e) {
Utils.LOGGER.error("Could not close MDS", e);
}
}
POOL.shutdown();
}
@Override
public void close() {
public void close() throws IOException {
disposed = true;
service.close();
SCANNERS.remove(instance.modsDir);
}
}

View File

@ -3,6 +3,7 @@ package io.gitlab.jfronny.inceptum.launcher.system.mds;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import java.nio.file.Path;
import java.util.Objects;
public class ProtoInstance {
public final Path modsDir;
@ -14,4 +15,9 @@ public class ProtoInstance {
this.mds = mds;
this.meta = meta;
}
@Override
public int hashCode() {
return Objects.hash(modsDir, mds, meta);
}
}

View File

@ -1,11 +1,10 @@
package io.gitlab.jfronny.inceptum.launcher.system.setup.steps;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.setup.Step;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.system.setup.SetupStepInfo;
import io.gitlab.jfronny.inceptum.launcher.system.setup.Step;
import java.io.IOException;
import java.nio.file.Path;
@ -16,7 +15,7 @@ public class RunMdsStep implements Step {
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
info.setState("Running MDS");
Path instance = MetaHolder.INSTANCE_DIR.resolve(info.name());
ModsDirScanner.get(instance.resolve("mods"), JFiles.readObject(instance.resolve("instance.json"), InstanceMeta.class))
ModsDirScanner.get(instance.resolve("mods"), GC_InstanceMeta.read(instance.resolve("instance.json")))
.runOnce((path, iwModDescription) -> info.setState("Scanned " + path));
}
}

View File

@ -1,11 +1,11 @@
package io.gitlab.jfronny.inceptum.launcher.system.setup.steps;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.GC_InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.setup.Step;
import io.gitlab.jfronny.inceptum.launcher.system.setup.SetupStepInfo;
import io.gitlab.jfronny.inceptum.launcher.system.setup.Step;
import java.io.IOException;
import java.nio.file.Files;
@ -21,7 +21,7 @@ public class WriteMetadataStep implements Step {
if (!Files.exists(metaPath)) {
InstanceMeta meta = new InstanceMeta();
meta.version = info.version().id;
JFiles.writeObject(metaPath, meta);
GC_InstanceMeta.write(metaPath, meta);
}
Instance.setSetupLock(instance, false);
if (!Files.exists(instance.resolve(".gitignore"))) {

View File

@ -1,12 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.system.source;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.launcher.gson.ModSourceAdapter;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
@GSerializable(with = ModSourceAdapter.class)
public interface ModSource {
ModDownload download() throws IOException;

View File

@ -0,0 +1,39 @@
package io.gitlab.jfronny.inceptum.launcher.util;
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.*;
public class FileBackedRef<T> implements Closeable {
private final Path filePath;
private final ThrowingFunction<Path, T, IOException> read;
private final WatchService service;
private T cache = null;
public FileBackedRef(Path filePath, ThrowingFunction<Path, T, IOException> read) throws IOException {
this.filePath = filePath;
this.read = read;
this.service = FileSystems.getDefault().newWatchService();
filePath.getParent().register(service, StandardWatchEventKinds.ENTRY_MODIFY);
}
public T get() throws IOException {
WatchKey key = service.poll();
boolean update = cache == null;
if (key != null) {
for (WatchEvent<?> event : key.pollEvents()) {
update |= event.context().equals(filePath);
}
key.reset();
}
if (update) cache = read.apply(filePath);
return cache;
}
@Override
public void close() throws IOException {
service.close();
}
}

View File

@ -27,7 +27,7 @@ public class Wrapper {
throw new FileNotFoundException("Something went wrong while downloading the latest version.");
}
}
List<Path> classpath = Updater.getLaunchClasspath(JFiles.readObject(MetaHolder.WRAPPER_CONFIG_PATH, WrapperConfig.class));
List<Path> classpath = Updater.getLaunchClasspath(GC_WrapperConfig.read(MetaHolder.WRAPPER_CONFIG_PATH));
if (!BuildMetadata.IS_RELEASE) {
System.out.println("Using classpath: " + classpath.stream().map(Path::toString).collect(Collectors.joining("" + File.pathSeparator)));
}