Update for CFCore API

This commit is contained in:
Johannes Frohnmeyer 2022-06-05 16:45:22 +02:00
parent c205603bae
commit 11679ceb75
Signed by: Johannes
GPG Key ID: E76429612C2929F4
28 changed files with 384 additions and 332 deletions

View File

@ -1,4 +1,4 @@
image: gradle:jdk17
image: gradle:jdk18
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"

View File

@ -38,12 +38,12 @@ allprojects {
}
ext {
lwjglVersion = '3.3.0'
lwjglVersion = '3.3.1'
imguiVersion = '1.86.4'
log4jVersion = '2.14.1'
slf4jVersion = '1.7.36'
logbackVersion = '1.2.11'
jfCommonsVersion = '0.1.0.2022.4.28.21.6.56'
jfCommonsVersion = '2022.6.5+13-35-53'
jgitVersion = '6.1.0.202203080745-r'
flavorProp = project.hasProperty('flavor') ? project.getProperty('flavor') : 'custom'
flavor = flavorProp
isPublic = project.hasProperty('public')
@ -69,7 +69,7 @@ dependencies {
implementation "ch.qos.logback:logback-classic:$logbackVersion"
implementation "io.gitlab.jfronny:commons:$jfCommonsVersion"
implementation "io.gitlab.jfronny:commons-gson:$jfCommonsVersion"
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.1.0.202203080745-r'
implementation "org.eclipse.jgit:org.eclipse.jgit:$jgitVersion"
implementation project(":wrapper")
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.inceptum;
import io.gitlab.jfronny.commons.serialize.gson.GsonHolder;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import io.gitlab.jfronny.inceptum.frontend.cli.CommandResolution;
import io.gitlab.jfronny.inceptum.frontend.cli.Commands;
import io.gitlab.jfronny.inceptum.frontend.gui.window.dialog.AlertWindow;

View File

@ -157,7 +157,6 @@ public class InceptumGui {
* Main application loop.
*/
public static void run() {
Map<String, Integer> windowCountByName = new HashMap<>();
while (!GLFW.glfwWindowShouldClose(handle)) {
//frame
clearBuffer();
@ -166,17 +165,9 @@ public class InceptumGui {
//render
if (WINDOWS.isEmpty()) exit();
else {
windowCountByName.clear();
for (Window window : WINDOWS.toArray(new Window[0])) {
if (window.isNew()) window.preFirstDraw();
String title = window.getName();
if (!windowCountByName.containsKey(title))
windowCountByName.put(title, 1);
else {
int count = windowCountByName.get(title) + 1;
windowCountByName.put(title, count);
title += "##" + count;
}
String title = window.getName() + "##" + System.identityHashCode(window);
if (ImGui.begin(title, window.getOpenState(), window.getFlags()))
window.draw();
ImGui.end();

View File

@ -188,8 +188,8 @@ public class AddModWindow extends Window {
}
else {
if (ImGui.button("Add##" + mod.id)) {
CurseforgeMod.GameVersionLatestFile latest = null;
for (CurseforgeMod.GameVersionLatestFile file : mod.gameVersionLatestFiles) {
CurseforgeMod.LatestFileIndex latest = null;
for (CurseforgeMod.LatestFileIndex file : mod.latestFilesIndexes) {
if (file.gameVersion.equals(instance.getMinecraftVersion())) {
if (latest == null) latest = file;
}
@ -198,10 +198,10 @@ public class AddModWindow extends Window {
Inceptum.showError("No valid version could be identified for this mod", "No version found");
}
else {
CurseforgeMod.GameVersionLatestFile finalLatest = latest;
CurseforgeMod.LatestFileIndex finalLatest = latest;
new Thread(() -> {
try {
download(new CurseforgeModSource(mod.id, finalLatest.projectFileId), modsDir.resolve((mod.slug == null ? mod.id : mod.slug) + PathUtil.EXT_IMOD), mds).write();
download(new CurseforgeModSource(mod.id, finalLatest.fileId), modsDir.resolve((mod.slug == null ? mod.id : mod.slug) + PathUtil.EXT_IMOD), mds).write();
} catch (IOException e) {
Inceptum.showError("Could not download mod", e);
}
@ -211,7 +211,7 @@ public class AddModWindow extends Window {
}
ImGui.sameLine();
if (ImGui.button("Web##" + mod.id)) {
Utils.openWebBrowser(new URI(mod.websiteUrl));
Utils.openWebBrowser(new URI(mod.links.websiteUrl));
}
}
ImGui.endTable();
@ -236,7 +236,7 @@ public class AddModWindow extends Window {
}
ModDownload md = ms.download();
ModDescription manifest = ModDescription.of(md.sha1(), md.murmur2(), ms, mds.getGameVersion());
for (ModSource dependency : ms.getDependencies()) {
for (ModSource dependency : ms.getDependencies(mds.getGameVersion())) {
DownloadMeta depMan = download(dependency, metaFile.getParent().resolve(dependency.getShortName() + PathUtil.EXT_IMOD), mds);
depMan.description.dependents.add(md.file().getFileName().toString());
manifest.dependencies.add(depMan.download.file().getFileName().toString());

View File

@ -1,23 +1,17 @@
package io.gitlab.jfronny.inceptum.frontend.gui.window;
import com.sun.net.httpserver.HttpServer;
import imgui.ImGui;
import io.gitlab.jfronny.commons.serialize.gson.GsonHolder;
import io.gitlab.jfronny.inceptum.Inceptum;
import com.sun.net.httpserver.*;
import imgui.*;
import io.gitlab.jfronny.commons.serialize.gson.api.*;
import io.gitlab.jfronny.inceptum.*;
import io.gitlab.jfronny.inceptum.model.microsoft.*;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.util.api.account.MicrosoftAccount;
import io.gitlab.jfronny.inceptum.util.api.account.MicrosoftAuthAPI;
import io.gitlab.jfronny.inceptum.util.*;
import io.gitlab.jfronny.inceptum.util.api.account.*;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import java.io.*;
import java.net.*;
import java.nio.charset.*;
import java.util.*;
public class MicrosoftLoginWindow extends Window {
private static HttpServer server;

View File

@ -30,12 +30,14 @@ public class ModSourceTypeAdapter implements JsonSerializer<ModSource>, JsonDese
}
}
case "curseforge" -> {
if (!jo.has("projectId"))
throw new JsonParseException("Expected a projectId in this curseforge project");
if (!jo.has("fileId"))
throw new JsonParseException("Expected a fileId in this curseforge project");
try {
yield new CurseforgeModSource(jo.get("projectId").getAsInt(), jo.get("fileId").getAsInt());
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);
}
@ -69,7 +71,11 @@ public class ModSourceTypeAdapter implements JsonSerializer<ModSource>, JsonDese
}
else if (src instanceof CurseforgeModSource cu) {
jo.add("type", new JsonPrimitive("curseforge"));
jo.add("projectId", new JsonPrimitive(cu.getProjectId()));
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");

View File

@ -1,8 +0,0 @@
package io.gitlab.jfronny.inceptum.model.curseforge;
public class CurseforgeDependency {
public Integer id;
public Integer addonId;
public Integer type;
public Integer fileId;
}

View File

@ -1,24 +1,79 @@
package io.gitlab.jfronny.inceptum.model.curseforge;
import java.util.List;
import java.util.*;
public class CurseforgeFile {
public Integer id;
public Integer gameId;
public Integer modId;
public Boolean isAvailable;
public String displayName;
public String fileName;
public String fileDate;
public Integer fileLength;
/* Possible values:
1=Release
2=Beta
3=Alpha*/
public Integer releaseType;
/* Possible values:
1=Processing
2=ChangesRequired
3=UnderReview
4=Approved
5=Rejected
6=MalwareDetected
7=Deleted
8=Archived
9=Testing
10=Released
11=ReadyForReview
12=Deprecated
13=Baking
14=AwaitingPublishing
15=FailedPublishing*/
public Integer fileStatus;
public List<Hash> hashes;
public Date fileDate;
public Integer fileLength;
public Long downloadCount;
public String downloadUrl;
public Boolean isAlternate;
public List<String> gameVersions;
public List<GameVersion> sortableGameVersions;
public List<Dependency> dependencies;
public Integer alternateFileId;
public List<CurseforgeDependency> dependencies;
public Boolean isAvailable;
public List<CurseforgeModule> modules;
public Long packageFingerprint;
public List<String> gameVersion;
public String gameVersionDateReleased;
public Integer serverPackFileId;
public Boolean hasInstallScript;
public Boolean isServerPack;
public Long fileFingerprint; // murmur5 hash
public List<Module> modules;
public static class Hash {
public String value;
/* Possible values:
1=Sha1
2=Md5*/
public Integer algo;
}
public static class GameVersion {
public String gameVersionName;
public String gameVersionPadded;
public String gameVersion;
public Date gameVersionReleaseDate;
public Integer gameVersionTypeId;
}
public static class Dependency {
public Integer modId;
/* Possible values:
1=EmbeddedLibrary
2=OptionalDependency
3=RequiredDependency
4=Tool
5=Incompatible
6=Include*/
public Integer relationType;
}
public static class Module {
public String name;
public Long fingerprint;
}
}

View File

@ -1,15 +0,0 @@
package io.gitlab.jfronny.inceptum.model.curseforge;
import java.util.List;
public class CurseforgeFingerprint {
public Boolean isCacheBuilt;
public List<Mod> exactMatches;
public List<Long> exactFingerprints;
public static class Mod {
public Integer id;
public CurseforgeFile file;
public List<CurseforgeMod.File> latestFiles;
}
}

View File

@ -1,137 +1,95 @@
package io.gitlab.jfronny.inceptum.model.curseforge;
import java.util.List;
import java.util.*;
public class CurseforgeMod {
public Integer id;
public String name;
public List<Author> authors;
public List<Attachment> attachments;
public String issueTrackerUrl;
public String wikiUrl;
public String sourceUrl;
public String websiteUrl;
public Integer gameId;
public String summary;
public Integer downloadFiles;
public Long downloadCount;
public List<File> latestFiles;
public List<Category> categories;
public Integer statue;
public Integer primaryCategoryId;
public CategorySection categorySection;
public String name;
public String slug;
public List<GameVersionLatestFile> gameVersionLatestFiles;
public Links links;
public String summary;
/* Possible values:
1=New
2=ChangesRequired
3=UnderSoftReview
4=Approved
5=Rejected
6=ChangesMade
7=Inactive
8=Abandoned
9=Deleted
10=UnderReview*/
public Integer status;
public Long downloadCount;
public Boolean isFeatured;
public Float popularityScore;
public Integer primaryCategoryId;
public List<Category> categories;
public Integer classId;
public List<Author> authors;
public Logo logo;
public List<Screenshot> screenshots;
public Integer mainFileId;
public List<CurseforgeFile> latestFiles;
public List<LatestFileIndex> latestFilesIndexes;
public Date dateCreated;
public Date dateModified;
public Date dateReleased;
public Boolean allowModDistribution;
public Integer gamePopularityRank;
public String primaryLanguage;
public String gameSlug;
public List<String> modLoaders;
public String gameName;
public String portalName;
//public Date dateModified;
//public Date dateCreated;
//public Date dateReleased;
public Boolean isAvailable;
public Boolean isExperimental;
public Integer thumbsUpCount;
public static class Author {
public String name;
public String url;
public Integer projectId;
public Integer id;
public Integer userId;
public Integer twitchId;
}
public static class Attachment {
public Integer id;
public Integer projectId;
public String description;
public Boolean isDefault;
public String thumbnailUrl;
public String title;
public String url;
public Integer status;
}
public static class File {
public Integer id;
public String displayName;
public String fileName;
//public Date fileDate;
public Integer fileLength;
public Integer releaseType;
public Integer fileStatus;
public String downloadUrl;
public Boolean isAlternate;
public Integer alternatFileId;
public List<CurseforgeDependency> dependencies;
public Boolean isAvailable;
public List<CurseforgeModule> modules;
public Long packageFingerprint;
public List<String> gameVersion;
public List<SortableGameVersion> sortableGameVersion;
public String changelog;
public Boolean hasInstallScript;
public Boolean isCompatibleWithClient;
public Integer categorySectionPackageType;
public Integer restrictProjectFileAccess;
public Integer projectStatus;
public Integer renderCacheId;
public Integer projectId;
public Long packageFingerprintId;
//public Date gameVersionDateReleased;
public Long gameVersionMappingId;
public Integer gameVersionId;
public Integer gameId;
public Boolean isServerPack;
public List<Hash> hashes;
public static class SortableGameVersion {
public String gameVersionPadded;
public String gameVersion;
//public Date gameVersionReleaseDate;
public String gameVersionName;
public Integer gameVersionTypeId;
}
public static class Hash {
public Integer algorithm;
public String value;
}
public static class Links {
public String websiteUrl;
public String wikiUrl;
public String issuesUrl;
public String sourcesUrl;
}
public static class Category {
public Integer categoryId;
public String name;
public String url;
public String avatarUrl;
public Integer parentId;
public Integer rootId;
public Integer projectId;
public Integer avatarId;
public Integer gameId;
public String slug;
//public Date dateModified;
}
public static class CategorySection {
public Integer id;
public Integer gameId;
public String name;
public Integer packageType;
public String path;
public String initialInclusionPattern;
public Integer gameCategoryId;
public String slug;
public String url;
public String iconUrl;
public Date dateModified;
public Boolean isClass;
public Integer classId;
public Integer primaryCategoryId;
}
public static class GameVersionLatestFile {
public static class Author {
public Integer id;
public String name;
public String url;
}
public static class Logo {
public Integer id;
public Integer modId;
public String title;
public String description;
public String thumbnailUrl;
public String url;
}
public static class Screenshot {
public Integer id;
public Integer modId;
public String title;
public String description;
public String thumbnailUrl;
public String url;
}
public static class LatestFileIndex {
public String gameVersion;
public Integer projectFileId;
public String projectFileName;
public Integer fileType;
public Integer fileId;
public String filename;
public Integer releaseType;
public Integer gameVersionTypeId;
public Integer modLoader;
}
}

View File

@ -1,7 +0,0 @@
package io.gitlab.jfronny.inceptum.model.curseforge;
public class CurseforgeModule {
public String folderName;
public Long fingerprint;
public Integer type;
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.inceptum.model.curseforge.response;
import io.gitlab.jfronny.inceptum.model.curseforge.*;
import java.util.*;
public class FingerprintMatchesResponse {
public Result data;
public static class Result {
public Boolean isCacheBuilt;
public List<Match> exactMatches;
public List<Integer> exactFingerprints;
public List<Match> partialMatches;
public List<Integer> installedFingerprints;
public List<Integer> unmatchedFingerprints;
public static class Match {
public Integer id;
public CurseforgeFile file;
public List<CurseforgeFile> latestFiles;
}
}
}

View File

@ -0,0 +1,7 @@
package io.gitlab.jfronny.inceptum.model.curseforge.response;
import io.gitlab.jfronny.inceptum.model.curseforge.*;
public class GetModFileResponse {
public CurseforgeFile data;
}

View File

@ -0,0 +1,7 @@
package io.gitlab.jfronny.inceptum.model.curseforge.response;
import io.gitlab.jfronny.inceptum.model.curseforge.*;
public class GetModResponse {
public CurseforgeMod data;
}

View File

@ -0,0 +1,17 @@
package io.gitlab.jfronny.inceptum.model.curseforge.response;
import io.gitlab.jfronny.inceptum.model.curseforge.*;
import java.util.*;
public class SearchResponse {
public List<CurseforgeMod> data;
public Pagination pagination;
public static class Pagination {
public Integer index;
public Integer pageSite;
public Integer resultCount;
public Integer totalCount;
}
}

View File

@ -1,12 +1,11 @@
package io.gitlab.jfronny.inceptum.model.inceptum;
import io.gitlab.jfronny.commons.serialize.gson.GsonIgnore;
import io.gitlab.jfronny.commons.serialize.gson.api.*;
import java.util.List;
import java.util.Objects;
import java.util.*;
public class InstanceMeta {
@GsonIgnore
@Ignore
public static final String floaderPrefix = "fabric-loader-";
public String version;
public String java;

View File

@ -1,25 +1,21 @@
package io.gitlab.jfronny.inceptum.model.inceptum;
import io.gitlab.jfronny.commons.HashUtils;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeFingerprint;
import io.gitlab.jfronny.inceptum.util.source.CurseforgeModSource;
import io.gitlab.jfronny.inceptum.util.source.ModSource;
import io.gitlab.jfronny.inceptum.util.source.ModrinthModSource;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.util.api.CurseforgeApi;
import io.gitlab.jfronny.inceptum.util.api.ModrinthApi;
import org.eclipse.jgit.annotations.Nullable;
import io.gitlab.jfronny.commons.*;
import io.gitlab.jfronny.inceptum.model.curseforge.response.*;
import io.gitlab.jfronny.inceptum.util.*;
import io.gitlab.jfronny.inceptum.util.api.*;
import io.gitlab.jfronny.inceptum.util.source.*;
import org.eclipse.jgit.annotations.*;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
public class ModDescription {
public Map<ModSource, Optional<ModSource>> sources; //key: source, value: update
public String sha1;
public String murmur2;
public Long murmur2;
public List<String> dependents; // by file name
public List<String> dependencies; // by file name
@ -43,9 +39,9 @@ public class ModDescription {
}
if (!curseforge) {
try {
CurseforgeFingerprint cf = CurseforgeApi.checkFingerprint(murmur2);
FingerprintMatchesResponse.Result cf = CurseforgeApi.checkFingerprint(murmur2);
if (!cf.exactMatches.isEmpty()) {
CurseforgeFingerprint.Mod f = cf.exactMatches.get(0);
FingerprintMatchesResponse.Result.Match f = cf.exactMatches.get(0);
addSource(new CurseforgeModSource(f.id, f.file.id), gameVersion);
changed = true;
}
@ -72,7 +68,7 @@ public class ModDescription {
return res;
}
public static ModDescription of(String sha1, String murmur2, @Nullable ModSource knownSource, String gameVersion) {
public static ModDescription of(String sha1, Long murmur2, @Nullable ModSource knownSource, String gameVersion) {
ModDescription res = new ModDescription();
res.sources = new LinkedHashMap<>();
res.sha1 = sha1;

View File

@ -1,27 +1,21 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeModpackManifest;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.model.multimc.MMCPackMeta;
import io.gitlab.jfronny.inceptum.util.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.util.source.CurseforgeModSource;
import io.gitlab.jfronny.inceptum.util.source.ModSource;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import io.gitlab.jfronny.inceptum.model.curseforge.*;
import io.gitlab.jfronny.inceptum.model.inceptum.*;
import io.gitlab.jfronny.inceptum.model.multimc.*;
import io.gitlab.jfronny.inceptum.util.mds.*;
import io.gitlab.jfronny.inceptum.util.source.*;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.*;
import java.io.IOException;
import java.net.URISyntaxException;
import java.io.*;
import java.net.*;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.nio.file.*;
import java.time.*;
import java.util.*;
//TODO deduplicate
//TODO modrinth export

View File

@ -1,46 +1,86 @@
package io.gitlab.jfronny.inceptum.util.api;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.gson.reflect.TypeToken;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeFile;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeFingerprint;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeMod;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.commons.*;
import io.gitlab.jfronny.inceptum.model.curseforge.*;
import io.gitlab.jfronny.inceptum.model.curseforge.response.*;
import io.gitlab.jfronny.inceptum.util.*;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.io.*;
import java.net.*;
import java.util.*;
public class CurseforgeApi {
private static final String API_URL = "https://addons-ecs.forgesvc.net/api/v2/";
private static final Type curseforgeModListType = new TypeToken<List<CurseforgeMod>>() {}.getType();
// So you found the API key.
// Please be aware that CurseForge requires you to change this if you make any kind of derivative work
// Creating your own API key is relatively simple, so please don't abuse this
private static final String API_KEY = new String(unsalt(new byte[] {
-30, 50, -60, -121, 62, -31, 35, 17, 16, -53,
-53, -88, 21, -21, 15, -105, -115, -108, 114, -50,
-49, -4, 56, -65, -70, 108, -65, -3, -55, -4,
36, -86, -40, 116, 71, -5, 75, -9, -43, 4,
91, -91, -29, 40, 66, 87, -80, -74, 71, 41,
76, -96, 108, -61, -113, 118, 7, -39, -116, -120
}, 1024));
private static final String API_URL = "https://api.curseforge.com/v1/";
private static final int pageSize = 20;
//TODO use gameVersion
public static List<CurseforgeMod> search(String gameVersion, String query, int page, String sort) throws IOException {
return Utils.downloadObject(Utils.buildUrl(API_URL, "addon/search", Map.of(
return Utils.downloadObject(Utils.buildUrl(API_URL, "mods/search", Map.of(
"gameId", "432", // minecraft
"modLoaderType", "4", // fabric, forge would be 1
"sectionId", "6", // mods
"modLoaderType", "4", // fabric
"classId", "6", // mods
// "categoryId", ""
"searchFilter", query,
"sort", sort,
"sortDescending", "true",
"sortField", sort,
"sortOrder", "desc",
"gameVersion", gameVersion,
"pageSize", Integer.toString(pageSize),
"index", Integer.toString(page * pageSize)
)), curseforgeModListType);
)), SearchResponse.class, API_KEY);
}
public static CurseforgeMod getMod(String slug) throws IOException {
SearchResponse response = Utils.downloadObject(Utils.buildUrl(API_URL, "mods/search", Map.of(
"gameId", "432",
"classId", "6",
"slug", slug
)), SearchResponse.class, API_KEY);
if (response.pagination.totalCount != 1) {
throw new FileNotFoundException("Could not find mod with slug \"" + slug + "\"");
}
return checkDistribution(response.data.get(0));
}
public static CurseforgeMod getMod(int id) throws IOException {
return Utils.downloadObject(API_URL + "addon/" + id, CurseforgeMod.class);
GetModResponse response = Utils.downloadObject(API_URL + "mods/" + id, GetModResponse.class, API_KEY);
return checkDistribution(response.data);
}
private static CurseforgeMod checkDistribution(CurseforgeMod mod) {
if (!mod.allowModDistribution) {
throw new IllegalArgumentException("The author of the mod \"" + mod.slug + "\" has chosen to deliberately break your ability of downloading it.\n"
+ "Please let them know that disabling third party downloads does nothing but make the users life harder for no reason.");
}
return mod;
}
public static CurseforgeFile getFile(int modId, int fileId) throws IOException {
return Utils.downloadObject(API_URL + "addon/" + modId + "/file/" + fileId, CurseforgeFile.class);
GetModFileResponse response = Utils.downloadObject(API_URL + "mods/" + modId + "/files/" + fileId, GetModFileResponse.class, API_KEY);
return response.data;
}
public static CurseforgeFingerprint checkFingerprint(String hash) throws IOException, URISyntaxException {
return HttpUtils.post(API_URL + "fingerprint").bodyJson("[" + hash + "]").sendSerialized(CurseforgeFingerprint.class);
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;
}
private static byte[] unsalt(byte[] data, int salt) {
byte[] result = new byte[data.length];
new Random(salt).nextBytes(result);
for (int i = 0; i < data.length; i++) {
result[i] ^= data[i];
}
return result;
}
}

View File

@ -3,7 +3,6 @@ package io.gitlab.jfronny.inceptum.util.source;
import io.gitlab.jfronny.commons.HashUtils;
import io.gitlab.jfronny.commons.tuple.Triple;
import io.gitlab.jfronny.commons.tuple.Tuple;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeDependency;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeFile;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeMod;
import io.gitlab.jfronny.inceptum.util.Utils;
@ -34,6 +33,13 @@ public final class CurseforgeModSource implements ModSource {
this.mod = CurseforgeApi.getMod(projectId);
}
public CurseforgeModSource(String projectSlug, int fileId) throws IOException {
this.mod = CurseforgeApi.getMod(projectSlug);
this.projectId = mod.id;
this.fileId = fileId;
this.current = CurseforgeApi.getFile(projectId, fileId);
}
@Override
public ModDownload download() throws IOException {
Path path = getJarPath();
@ -47,12 +53,18 @@ public final class CurseforgeModSource implements ModSource {
}
@Override
public Set<ModSource> getDependencies() throws IOException {
public Set<ModSource> getDependencies(String gameVersion) throws IOException {
return DEPENDENCIES_CACHE.get(Tuple.of(projectId, fileId), () -> {
Set<ModSource> deps = new HashSet<>();
for (CurseforgeDependency dependency : current.dependencies) {
if (dependency.type == 3) //TODO support other types (3=required, 2=optional)
deps.add(new CurseforgeModSource(dependency.addonId, dependency.fileId));
for (CurseforgeFile.Dependency dependency : current.dependencies) {
if (dependency.relationType == 3) { //TODO support other types (IDs are documented on field declaration)
for (CurseforgeMod.LatestFileIndex index : CurseforgeApi.getMod(dependency.modId).latestFilesIndexes) {
if (index.gameVersion.equals(gameVersion)) {
deps.add(new CurseforgeModSource(dependency.modId, index.fileId));
break;
}
}
}
}
return deps;
});
@ -61,11 +73,11 @@ public final class CurseforgeModSource implements ModSource {
@Override
public Optional<ModSource> getUpdate(String gameVersion) throws IOException {
return UPDATE_CACHE.get(Tuple.of(projectId, fileId, gameVersion), () -> {
for (CurseforgeMod.GameVersionLatestFile file : mod.gameVersionLatestFiles) {
for (CurseforgeMod.LatestFileIndex file : mod.latestFilesIndexes) {
if (file.gameVersion.equals(gameVersion)) {
return file.projectFileId == fileId
return file.fileId == fileId
? Optional.empty()
: Optional.of(new CurseforgeModSource(projectId, file.projectFileId));
: Optional.of(new CurseforgeModSource(projectId, file.fileId));
}
}
return Optional.empty();

View File

@ -28,7 +28,7 @@ public record DirectModSource(String fileName, String url, Set<ModSource> depend
}
@Override
public Set<ModSource> getDependencies() throws IOException {
public Set<ModSource> getDependencies(String gameVersion) throws IOException {
return dependencies;
}

View File

@ -2,5 +2,5 @@ package io.gitlab.jfronny.inceptum.util.source;
import java.nio.file.Path;
public record ModDownload(String sha1, String murmur2, Path file) {
public record ModDownload(String sha1, long murmur2, Path file) {
}

View File

@ -9,7 +9,7 @@ import java.util.Set;
public interface ModSource {
ModDownload download() throws IOException;
Set<ModSource> getDependencies() throws IOException;
Set<ModSource> getDependencies(String gameVersion) throws IOException;
Optional<ModSource> getUpdate(String gameVersion) throws IOException;
String getVersion();
String getName();
@ -17,6 +17,11 @@ public interface ModSource {
String getFileName();
boolean equals(ModSource other);
default Path getJarPath() {
return MetaHolder.LIBRARIES_DIR.resolve("com").resolve(getName()).resolve(getFileName());
try {
return MetaHolder.LIBRARIES_DIR.resolve("com").resolve(getName()).resolve(getFileName());
} catch (NullPointerException npe) {
System.identityHashCode(this);
throw npe;
}
}
}

View File

@ -43,7 +43,7 @@ public final class ModrinthModSource implements ModSource {
}
@Override
public Set<ModSource> getDependencies() throws IOException {
public Set<ModSource> getDependencies(String gameVersion) throws IOException {
return DEPENDENCIES_CACHE.get(versionId, () -> {
Set<ModSource> deps = new HashSet<>();
for (ModrinthVersion.Dependency dependency : current.dependencies) {

View File

@ -1,22 +1,16 @@
package io.gitlab.jfronny.inceptum;
import io.gitlab.jfronny.commons.ComparableVersion;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.commons.serialize.gson.GsonHolder;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateChannel;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateInfo;
import io.gitlab.jfronny.commons.*;
import io.gitlab.jfronny.commons.serialize.gson.api.*;
import io.gitlab.jfronny.inceptum.model.inceptum.*;
import io.gitlab.jfronny.inceptum.util.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.stream.Stream;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
public class Wrapper {
private static final Path INCEPTUM_LIB_DIR = MetaHolder.BASE_PATH.resolve("libraries/io/gitlab/jfronny/inceptum/Inceptum");

View File

@ -1,20 +1,12 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.commons.ComparableVersion;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.commons.serialize.gson.ComparableVersionAdapter;
import io.gitlab.jfronny.gson.GsonBuilder;
import io.gitlab.jfronny.inceptum.model.inceptum.InceptumVersion;
import io.gitlab.jfronny.commons.*;
import io.gitlab.jfronny.commons.serialize.gson.api.*;
import io.gitlab.jfronny.inceptum.model.inceptum.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.*;
import java.net.*;
import java.nio.file.*;
public class MetaHolder {
public static final InceptumVersion VERSION;
@ -23,10 +15,7 @@ public class MetaHolder {
static {
try (InputStream is = MetaHolder.class.getClassLoader().getResourceAsStream("version.json");
InputStreamReader isr = new InputStreamReader(is)) {
VERSION = new GsonBuilder()
.registerTypeAdapter(ComparableVersion.class, new ComparableVersionAdapter())
.create()
.fromJson(isr, InceptumVersion.class);
VERSION = GsonHolder.getGson().fromJson(isr, InceptumVersion.class);
if (System.getProperty("inceptum.base") == null) {
Path runDir = getRunDir();
BASE_PATH = VERSION.isRelease && !Files.exists(runDir)

View File

@ -1,35 +1,25 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.commons.HashUtils;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.commons.serialize.Serializer;
import io.gitlab.jfronny.commons.serialize.SerializerHolder;
import io.gitlab.jfronny.commons.serialize.gson.GsonHolder;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import io.gitlab.jfronny.inceptum.WrapperStrap;
import io.gitlab.jfronny.inceptum.util.cache.GsonFileCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.gitlab.jfronny.commons.*;
import io.gitlab.jfronny.commons.serialize.gson.api.*;
import io.gitlab.jfronny.commons.throwable.*;
import io.gitlab.jfronny.inceptum.*;
import io.gitlab.jfronny.inceptum.util.cache.*;
import org.slf4j.*;
import java.awt.*;
import java.io.*;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.lang.reflect.*;
import java.net.*;
import java.nio.charset.*;
import java.nio.file.FileSystem;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.HashMap;
import java.nio.file.attribute.*;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;
public class Utils {
public static final Pattern VALID_FILENAME = Pattern.compile("[a-zA-Z0-9_\\-.][a-zA-Z0-9 _\\-.]*[a-zA-Z0-9_\\-.]");
@ -61,6 +51,10 @@ public class Utils {
return downloadObject(url, type, 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, Type type, boolean cache) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type, cache);
}