1.19.4-pre1 port + modrinth fetcher
This commit is contained in:
parent
618c4c5f42
commit
28957f946e
100
README.md
100
README.md
|
@ -3,43 +3,65 @@ Packs are updated on startup whenever possible, meaning that very large packs wi
|
|||
|
||||
Example config:
|
||||
```
|
||||
[
|
||||
{
|
||||
"fetcher": "file",
|
||||
"source": "https://github.com/FaithfulTeam/Faithful/raw/releases/1.16.zip",
|
||||
"name": "Faithful"
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "seasnail8169/SnailPack",
|
||||
"name": "SnailPack"
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "spiralhalo/LumiLights/release",
|
||||
"name": "LumiLights",
|
||||
"forceDownload": true
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "spiralhalo/LumiPBRExt/tag/v0.7",
|
||||
"name": "LumiPBRExt"
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "FaithfulTeam/Faithful/branch/1.16",
|
||||
"name": "Faithful"
|
||||
},
|
||||
{
|
||||
"fetcher": "file",
|
||||
"source": "https://media.forgecdn.net/files/3031/178/BattyCoordinates_005.zip",
|
||||
"name": "Battys Coordinates"
|
||||
},
|
||||
{
|
||||
"fetcher": "curseforge",
|
||||
"source": "325017",
|
||||
"name": "Vanilla Additions",
|
||||
"forceEnable": true
|
||||
}
|
||||
]
|
||||
{
|
||||
// The packs to be loaded by resclone
|
||||
"packs": [
|
||||
{
|
||||
"fetcher": "file",
|
||||
"source": "https://github.com/FaithfulTeam/Faithful/raw/releases/1.16.zip",
|
||||
"name": "Faithful",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "seasnail8169/SnailPack",
|
||||
"name": "SnailPack",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "spiralhalo/LumiLights/release",
|
||||
"name": "LumiLights",
|
||||
"forceDownload": true,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "spiralhalo/LumiPBRExt/tag/v0.7",
|
||||
"name": "LumiPBRExt",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "FaithfulTeam/Faithful/branch/1.16",
|
||||
"name": "Faithful",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "file",
|
||||
"source": "https://media.forgecdn.net/files/3031/178/BattyCoordinates_005.zip",
|
||||
"name": "Battys Coordinates",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "curseforge",
|
||||
"source": "325017",
|
||||
"name": "Vanilla Additions",
|
||||
"forceDownload": false,
|
||||
"forceEnable": true
|
||||
},
|
||||
{
|
||||
"fetcher": "modrinth",
|
||||
"source": "new-in-town",
|
||||
"name": "New in Town"
|
||||
}
|
||||
],
|
||||
// Whether to prune unused packs from the cache
|
||||
"pruneUnused": true
|
||||
}
|
||||
```
|
|
@ -10,5 +10,5 @@ dependencies {
|
|||
|
||||
// Dev env
|
||||
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil:${prop("libjf_version")}")
|
||||
modLocalRuntime("com.terraformersmc:modmenu:5.0.2")
|
||||
modLocalRuntime("com.terraformersmc:modmenu:${prop("modmenu_version")}")
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# https://fabricmc.net/develop
|
||||
minecraft_version=1.19.3
|
||||
yarn_mappings=build.5
|
||||
loader_version=0.14.12
|
||||
minecraft_version=1.19.4-pre1
|
||||
yarn_mappings=build.6
|
||||
loader_version=0.14.17
|
||||
|
||||
maven_group=io.gitlab.jfronny
|
||||
archives_base_name=resclone
|
||||
|
||||
modrinth_id=resclone
|
||||
|
||||
fabric_version=0.70.0+1.19.3
|
||||
libjf_version=3.4.1
|
||||
fabric_version=0.75.1+1.19.4
|
||||
libjf_version=3.5.0-SNAPSHOT
|
||||
modmenu_version=6.1.0-beta.3
|
|
@ -1,18 +1,19 @@
|
|||
package io.gitlab.jfronny.resclone;
|
||||
|
||||
import com.google.gson.*;
|
||||
import io.gitlab.jfronny.commons.log.*;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.*;
|
||||
import io.gitlab.jfronny.commons.log.Logger;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolders;
|
||||
import io.gitlab.jfronny.resclone.api.*;
|
||||
import io.gitlab.jfronny.resclone.data.*;
|
||||
import io.gitlab.jfronny.resclone.processors.*;
|
||||
import io.gitlab.jfronny.resclone.util.*;
|
||||
import net.fabricmc.api.*;
|
||||
import net.fabricmc.loader.api.*;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaLoaded;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
||||
import io.gitlab.jfronny.resclone.processors.RemoveEmptyProcessor;
|
||||
import io.gitlab.jfronny.resclone.processors.RootPathProcessor;
|
||||
import io.gitlab.jfronny.resclone.util.PackUrlCache;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
@ -28,6 +29,11 @@ public class Resclone implements ModInitializer, RescloneApi {
|
|||
|
||||
public static final String MOD_ID = "resclone";
|
||||
public static final Logger LOGGER = Logger.forName(MOD_ID);
|
||||
public static final String USER_AGENT = "jfmods/" + MOD_ID + "/" + FabricLoader.getInstance()
|
||||
.getModContainer(MOD_ID).orElseThrow()
|
||||
.getMetadata()
|
||||
.getVersion()
|
||||
.getFriendlyString();
|
||||
|
||||
public static PackUrlCache urlCache;
|
||||
|
||||
|
@ -37,7 +43,7 @@ public class Resclone implements ModInitializer, RescloneApi {
|
|||
@Override
|
||||
public void onInitialize() {
|
||||
LOGGER.info("Initialising Resclone.");
|
||||
GsonHolder.register();
|
||||
GsonHolders.registerSerializer();
|
||||
|
||||
urlCache = new PackUrlCache(getConfigPath().resolve("urlCache.properties"));
|
||||
CONF.clear();
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
package io.gitlab.jfronny.resclone;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolders;
|
||||
import io.gitlab.jfronny.resclone.api.RescloneApi;
|
||||
import io.gitlab.jfronny.resclone.api.RescloneEntry;
|
||||
import io.gitlab.jfronny.resclone.util.config.*;
|
||||
import io.gitlab.jfronny.resclone.fetchers.BasicFileFetcher;
|
||||
import io.gitlab.jfronny.resclone.fetchers.CurseforgeFetcher;
|
||||
import io.gitlab.jfronny.resclone.fetchers.GitHubFetcher;
|
||||
import io.gitlab.jfronny.resclone.fetchers.*;
|
||||
import io.gitlab.jfronny.resclone.processors.PruneVanillaProcessor;
|
||||
import io.gitlab.jfronny.resclone.util.config.*;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public class RescloneEntryDefault implements RescloneEntry {
|
||||
@Override
|
||||
public void init(RescloneApi api) {
|
||||
GsonHolder.registerTypeAdapter(RescloneConfig.class, new RescloneConfigTypeAdapter());
|
||||
GsonHolders.registerTypeAdapter(RescloneConfig.class, new RescloneConfigTypeAdapter());
|
||||
api.addFetcher(new BasicFileFetcher());
|
||||
api.addFetcher(new GitHubFetcher());
|
||||
api.addFetcher(new CurseforgeFetcher());
|
||||
api.addFetcher(new ModrinthFetcher());
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||
api.addProcessor(new PruneVanillaProcessor());
|
||||
ConfigLoader.load(api);
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package io.gitlab.jfronny.resclone.data.modrinth;
|
||||
|
||||
import io.gitlab.jfronny.gson.annotations.SerializedName;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class Version {
|
||||
public String name;
|
||||
public String version_number;
|
||||
@Nullable public String changelog;
|
||||
public List<Dependency> dependencies;
|
||||
public List<String> game_versions;
|
||||
public VersionType version_type;
|
||||
public List<String> loaders;
|
||||
public Boolean featured;
|
||||
public Status status;
|
||||
@Nullable public Status requested_status;
|
||||
public String id;
|
||||
public String project_id;
|
||||
public String author_id;
|
||||
public Date date_published;
|
||||
public Integer downloads;
|
||||
public List<File> files;
|
||||
|
||||
public static class Dependency {
|
||||
@Nullable public String version_id;
|
||||
@Nullable public String project_id;
|
||||
@Nullable public String file_name;
|
||||
public Type dependency_type;
|
||||
|
||||
public enum Type {
|
||||
required, optional, incompatible, embedded
|
||||
}
|
||||
}
|
||||
|
||||
public enum VersionType {
|
||||
release, beta, alpha
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
listed, archived, draft, unlisted, scheduled, unknown
|
||||
}
|
||||
|
||||
public static class File {
|
||||
public Hashes hashes;
|
||||
public String url;
|
||||
public String filename;
|
||||
public Boolean primary;
|
||||
public Integer size;
|
||||
@Nullable public Type file_type;
|
||||
|
||||
public static class Hashes {
|
||||
public String sha512;
|
||||
public String sha1;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
@SerializedName("required-resource-pack") REQUIRED_RESOURCE_PACK,
|
||||
@SerializedName("optional-resource-pack") OPTIONAL_RESOURCE_PACK
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
import io.gitlab.jfronny.commons.HttpUtils;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.api.PackFetcher;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
|
@ -34,8 +33,8 @@ public abstract class BasePackFetcher implements PackFetcher {
|
|||
|
||||
Resclone.LOGGER.info("Downloading pack: " + url);
|
||||
|
||||
try (InputStream is = new URL(url).openStream()) {
|
||||
FileOutputStream os = new FileOutputStream(p.toFile());
|
||||
try (InputStream is = HttpUtils.get(url).userAgent(Resclone.USER_AGENT).sendInputStream();
|
||||
OutputStream os = Files.newOutputStream(p)) {
|
||||
byte[] dataBuffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = is.read(dataBuffer, 0, 1024)) != -1) {
|
||||
|
@ -43,7 +42,7 @@ public abstract class BasePackFetcher implements PackFetcher {
|
|||
}
|
||||
Resclone.LOGGER.info("Finished downloading.");
|
||||
} catch (Throwable e) {
|
||||
throw new Exception("Could not download pack", e);
|
||||
throw new IOException("Could not download pack", e);
|
||||
}
|
||||
Resclone.packCount++;
|
||||
return new Result(p, true);
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
import io.gitlab.jfronny.commons.*;
|
||||
import io.gitlab.jfronny.resclone.*;
|
||||
import io.gitlab.jfronny.resclone.data.curseforge.*;
|
||||
import net.minecraft.*;
|
||||
import io.gitlab.jfronny.commons.HttpUtils;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.data.curseforge.GetModFilesResponse;
|
||||
import io.gitlab.jfronny.resclone.data.curseforge.GetModResponse;
|
||||
import net.minecraft.MinecraftVersion;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
|
||||
public class CurseforgeFetcher extends BasePackFetcher {
|
||||
// So you found the API key.
|
||||
|
@ -41,8 +44,7 @@ public class CurseforgeFetcher extends BasePackFetcher {
|
|||
boolean foundMatchingVersion = false;
|
||||
|
||||
for (GetModFilesResponse.Data addon : GET(baseUrl + "/files", GetModFilesResponse.class).data) {
|
||||
if (foundMatchingVersion && !addon.gameVersions.contains(version))
|
||||
continue;
|
||||
if (foundMatchingVersion && !addon.gameVersions.contains(version)) continue;
|
||||
if (!foundMatchingVersion && addon.gameVersions.contains(version)) {
|
||||
foundMatchingVersion = true;
|
||||
latest = null;
|
||||
|
@ -53,11 +55,11 @@ public class CurseforgeFetcher extends BasePackFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
if (!foundMatchingVersion)
|
||||
Resclone.LOGGER.error("Could not find pack for matching version, using latest");
|
||||
if (latest == null) throw new FileNotFoundException("Could not identify valid version");
|
||||
if (!foundMatchingVersion) Resclone.LOGGER.error("Could not find matching version of " + baseUrl + ", using latest");
|
||||
return latest.downloadUrl;
|
||||
} catch (Throwable e) {
|
||||
throw new Exception("Could not get CF download for " + baseUrl, e);
|
||||
throw new IOException("Could not get CurseForge download for " + baseUrl, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.commons.HttpUtils;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.data.modrinth.Version;
|
||||
import net.minecraft.MinecraftVersion;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class ModrinthFetcher extends BasePackFetcher {
|
||||
private static final Type versionType = new TypeToken<List<Version>>() {}.getType();
|
||||
@Override
|
||||
public String getSourceTypeName() {
|
||||
return "modrinth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String baseUrl) throws Exception {
|
||||
try {
|
||||
String version = MinecraftVersion.CURRENT.getName();
|
||||
|
||||
Version latest = null;
|
||||
Date latestDate = null;
|
||||
boolean foundMatchingVersion = false;
|
||||
|
||||
for (Version ver : HttpUtils.get("https://api.modrinth.com/v2/project/" + baseUrl + "/version")
|
||||
.userAgent(Resclone.USER_AGENT)
|
||||
.<List<Version>>sendSerialized(versionType)) {
|
||||
if (foundMatchingVersion && !ver.game_versions.contains(version)) continue;
|
||||
if (ver.files.isEmpty()) continue;
|
||||
if (!foundMatchingVersion && ver.game_versions.contains(version)) {
|
||||
foundMatchingVersion = true;
|
||||
latest = null;
|
||||
}
|
||||
if (latest == null || ver.date_published.after(latestDate)) {
|
||||
latest = ver;
|
||||
latestDate = ver.date_published;
|
||||
}
|
||||
}
|
||||
|
||||
if (latest == null) throw new FileNotFoundException("Could not identify valid version");
|
||||
if (!foundMatchingVersion) Resclone.LOGGER.error("Could not find matching version of " + baseUrl + ", using latest");
|
||||
|
||||
for (Version.File file : latest.files) {
|
||||
if (file.primary) return file.url;
|
||||
}
|
||||
Resclone.LOGGER.error("Could not identify primary file of " + baseUrl + ", attempting identification by file_type");
|
||||
for (Version.File file : latest.files) {
|
||||
if (file.file_type == Version.File.Type.REQUIRED_RESOURCE_PACK) return file.url;
|
||||
}
|
||||
Resclone.LOGGER.error("Identification failed, using first file of " + baseUrl);
|
||||
return latest.files.get(0).url;
|
||||
} catch (Throwable e) {
|
||||
throw new IOException("Could not get Modrinth download for " + baseUrl, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package io.gitlab.jfronny.resclone.util.config;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolders;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.api.RescloneApi;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
||||
|
@ -19,7 +19,7 @@ public class ConfigLoader {
|
|||
save(api, new HashSet<>(), true);
|
||||
}
|
||||
try (BufferedReader reader = Files.newBufferedReader(configPath)) {
|
||||
RescloneConfig data = GsonHolder.getGson().fromJson(reader, RescloneConfig.class);
|
||||
RescloneConfig data = GsonHolders.CONFIG.getGson().fromJson(reader, RescloneConfig.class);
|
||||
api.setPruneUnused(data.pruneUnused());
|
||||
for (PackMetaUnloaded meta : data.packs()) {
|
||||
api.addPack(meta.fetcher, meta.source, meta.name, meta.forceDownload, meta.forceEnable);
|
||||
|
@ -34,7 +34,7 @@ public class ConfigLoader {
|
|||
|
||||
public static void save(RescloneApi api, Set<PackMetaUnloaded> packs, boolean pruneUnused) {
|
||||
try (var writer = Files.newBufferedWriter(api.getConfigPath().resolve("config.json"))) {
|
||||
GsonHolder.getGson().toJson(new RescloneConfig(packs, pruneUnused), writer);
|
||||
GsonHolders.CONFIG.getGson().toJson(new RescloneConfig(packs, pruneUnused), writer);
|
||||
} catch (IOException e) {
|
||||
Resclone.LOGGER.error("Could not write config", e);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package io.gitlab.jfronny.resclone.util.config;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
|
||||
import io.gitlab.jfronny.gson.*;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolders;
|
||||
import io.gitlab.jfronny.gson.JsonParseException;
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.*;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
||||
|
||||
|
@ -19,7 +20,7 @@ public class RescloneConfigTypeAdapter extends TypeAdapter<RescloneConfig> {
|
|||
public RescloneConfig read(JsonReader jsonReader) throws IOException {
|
||||
if (jsonReader.peek() == JsonToken.BEGIN_ARRAY) {
|
||||
// Legacy format compatibility
|
||||
return new RescloneConfig(GsonHolder.getGson().fromJson(jsonReader, META_SET), false, true);
|
||||
return new RescloneConfig(GsonHolders.CONFIG.getGson().fromJson(jsonReader, META_SET), false, true);
|
||||
} else if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) {
|
||||
// New format
|
||||
jsonReader.beginObject();
|
||||
|
@ -31,7 +32,7 @@ public class RescloneConfigTypeAdapter extends TypeAdapter<RescloneConfig> {
|
|||
switch (name) {
|
||||
case PACKS -> {
|
||||
if (packs != null) throw new JsonParseException("Unexpected duplicate \"" + PACKS + "\" in Resclone config");
|
||||
packs = GsonHolder.getGson().fromJson(jsonReader, META_SET);
|
||||
packs = GsonHolders.CONFIG.getGson().fromJson(jsonReader, META_SET);
|
||||
}
|
||||
case PRUNE_UNUSED -> {
|
||||
if (pruneUnused != null) throw new JsonParseException("Unexpected duplicate \"" + PRUNE_UNUSED + "\" in Resclone config");
|
||||
|
@ -55,7 +56,7 @@ public class RescloneConfigTypeAdapter extends TypeAdapter<RescloneConfig> {
|
|||
jsonWriter.beginObject()
|
||||
.comment("The packs to be loaded by resclone")
|
||||
.name(PACKS);
|
||||
GsonHolder.getGson().toJson(rescloneConfig.packs(), META_SET, jsonWriter);
|
||||
GsonHolders.CONFIG.getGson().toJson(rescloneConfig.packs(), META_SET, jsonWriter);
|
||||
jsonWriter.comment("Whether to prune unused packs from the cache")
|
||||
.name(PRUNE_UNUSED)
|
||||
.value(rescloneConfig.pruneUnused())
|
||||
|
|
Loading…
Reference in New Issue