Compare commits
No commits in common. "master" and "v1.9.1" have entirely different histories.
2
.gitlab-ci.yml
Normal file
2
.gitlab-ci.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
include:
|
||||||
|
- remote: 'https://jfmods.gitlab.io/scripts/jfmod.yml'
|
@ -1 +0,0 @@
|
|||||||
#link https://pages.frohnmeyer-wds.de/scripts/jfmod.yml
|
|
100
README.md
100
README.md
@ -3,65 +3,43 @@ Packs are updated on startup whenever possible, meaning that very large packs wi
|
|||||||
|
|
||||||
Example config:
|
Example config:
|
||||||
```
|
```
|
||||||
{
|
[
|
||||||
// The packs to be loaded by resclone
|
{
|
||||||
"packs": [
|
"fetcher": "file",
|
||||||
{
|
"source": "https://github.com/FaithfulTeam/Faithful/raw/releases/1.16.zip",
|
||||||
"fetcher": "file",
|
"name": "Faithful"
|
||||||
"source": "https://github.com/FaithfulTeam/Faithful/raw/releases/1.16.zip",
|
},
|
||||||
"name": "Faithful",
|
{
|
||||||
"forceDownload": false,
|
"fetcher": "github",
|
||||||
"forceEnable": false
|
"source": "seasnail8169/SnailPack",
|
||||||
},
|
"name": "SnailPack"
|
||||||
{
|
},
|
||||||
"fetcher": "github",
|
{
|
||||||
"source": "seasnail8169/SnailPack",
|
"fetcher": "github",
|
||||||
"name": "SnailPack",
|
"source": "spiralhalo/LumiLights/release",
|
||||||
"forceDownload": false,
|
"name": "LumiLights",
|
||||||
"forceEnable": false
|
"forceDownload": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetcher": "github",
|
"fetcher": "github",
|
||||||
"source": "spiralhalo/LumiLights/release",
|
"source": "spiralhalo/LumiPBRExt/tag/v0.7",
|
||||||
"name": "LumiLights",
|
"name": "LumiPBRExt"
|
||||||
"forceDownload": true,
|
},
|
||||||
"forceEnable": false
|
{
|
||||||
},
|
"fetcher": "github",
|
||||||
{
|
"source": "FaithfulTeam/Faithful/branch/1.16",
|
||||||
"fetcher": "github",
|
"name": "Faithful"
|
||||||
"source": "spiralhalo/LumiPBRExt/tag/v0.7",
|
},
|
||||||
"name": "LumiPBRExt",
|
{
|
||||||
"forceDownload": false,
|
"fetcher": "file",
|
||||||
"forceEnable": false
|
"source": "https://media.forgecdn.net/files/3031/178/BattyCoordinates_005.zip",
|
||||||
},
|
"name": "Battys Coordinates"
|
||||||
{
|
},
|
||||||
"fetcher": "github",
|
{
|
||||||
"source": "FaithfulTeam/Faithful/branch/1.16",
|
"fetcher": "curseforge",
|
||||||
"name": "Faithful",
|
"source": "325017",
|
||||||
"forceDownload": false,
|
"name": "Vanilla Additions",
|
||||||
"forceEnable": false
|
"forceEnable": true
|
||||||
},
|
}
|
||||||
{
|
]
|
||||||
"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
|
|
||||||
}
|
|
||||||
```
|
```
|
6
build.gradle
Normal file
6
build.gradle
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
apply from: "https://jfmods.gitlab.io/scripts/jfmod.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
include modImplementation("io.gitlab.jfronny.libjf:libjf-base:${project.jfapi_version}") // for JfCommons
|
||||||
|
modImplementation "com.terraformersmc:modmenu:4.0.5"
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("jfmod") version "1.6-SNAPSHOT"
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
accessWidenerPath.set(file("src/main/resources/resclone.accesswidener"))
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects { group = "io.gitlab.jfronny" }
|
|
||||||
base.archivesName = "resclone"
|
|
||||||
|
|
||||||
val modmenuVersion = "11.0.0-beta.1"
|
|
||||||
val commonsVersion = "2.0.0-SNAPSHOT"
|
|
||||||
jfMod {
|
|
||||||
minecraftVersion = "1.21"
|
|
||||||
yarn("build.1")
|
|
||||||
loaderVersion = "0.15.11"
|
|
||||||
libJfVersion = "3.16.0"
|
|
||||||
fabricApiVersion = "0.100.1+1.21"
|
|
||||||
|
|
||||||
modrinth {
|
|
||||||
projectId = "resclone"
|
|
||||||
optionalDependencies.add("fabric-api")
|
|
||||||
}
|
|
||||||
curseforge {
|
|
||||||
projectId = "839008"
|
|
||||||
optionalDependencies.add("fabric-api")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
include(modImplementation("io.gitlab.jfronny.libjf:libjf-base")!!) // for JfCommons
|
|
||||||
include(modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v2")!!) // for JfCommons
|
|
||||||
include(modImplementation("net.fabricmc.fabric-api:fabric-resource-loader-v0")!!)
|
|
||||||
|
|
||||||
compileOnly("io.gitlab.jfronny:commons-serialize-generator-annotations:$commonsVersion")
|
|
||||||
annotationProcessor("io.gitlab.jfronny:commons-serialize-generator:$commonsVersion")
|
|
||||||
|
|
||||||
// Dev env
|
|
||||||
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny")
|
|
||||||
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil")
|
|
||||||
modLocalRuntime("com.terraformersmc:modmenu:$modmenuVersion")
|
|
||||||
// for modmenu
|
|
||||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-resource-loader-v0")
|
|
||||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-screen-api-v1")
|
|
||||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-key-binding-api-v1")
|
|
||||||
}
|
|
11
gradle.properties
Normal file
11
gradle.properties
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# https://fabricmc.net/develop
|
||||||
|
minecraft_version=1.19.1
|
||||||
|
yarn_mappings=build.1
|
||||||
|
loader_version=0.14.8
|
||||||
|
maven_group=io.gitlab.jfronny
|
||||||
|
archives_base_name=resclone
|
||||||
|
|
||||||
|
fabric_version=0.58.5+1.19.1
|
||||||
|
jfapi_version=2.10.0
|
||||||
|
|
||||||
|
modrinth_id=resclone
|
@ -1,9 +0,0 @@
|
|||||||
pluginManagement {
|
|
||||||
repositories {
|
|
||||||
maven("https://maven.fabricmc.net/") // FabricMC
|
|
||||||
maven("https://maven.frohnmeyer-wds.de/artifacts") // scripts
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.name = "resclone"
|
|
@ -1,36 +1,31 @@
|
|||||||
package io.gitlab.jfronny.resclone;
|
package io.gitlab.jfronny.resclone;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.logger.SystemLoggerPlus;
|
import com.google.gson.*;
|
||||||
import io.gitlab.jfronny.resclone.data.PackMetaLoaded;
|
import io.gitlab.jfronny.commons.log.*;
|
||||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
import io.gitlab.jfronny.commons.serialize.gson.api.*;
|
||||||
import io.gitlab.jfronny.resclone.fetchers.*;
|
import io.gitlab.jfronny.resclone.api.*;
|
||||||
|
import io.gitlab.jfronny.resclone.data.*;
|
||||||
import io.gitlab.jfronny.resclone.processors.*;
|
import io.gitlab.jfronny.resclone.processors.*;
|
||||||
import io.gitlab.jfronny.resclone.util.PackUrlCache;
|
import io.gitlab.jfronny.resclone.util.*;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.*;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.loader.api.*;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
import java.net.URI;
|
import java.net.*;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class Resclone implements ModInitializer {
|
public class Resclone implements ModInitializer, RescloneApi {
|
||||||
|
public static final Set<PackMetaUnloaded> CONF = new LinkedHashSet<>();
|
||||||
public static final Map<String, PackFetcher> FETCHER_INSTANCES = new LinkedHashMap<>();
|
public static final Map<String, PackFetcher> FETCHER_INSTANCES = new LinkedHashMap<>();
|
||||||
public static final Set<PackProcessor> PROCESSORS = new LinkedHashSet<>();
|
public static final Set<PackProcessor> PROCESSORS = new LinkedHashSet<>();
|
||||||
public static final Set<PackMetaLoaded> DOWNLOADED_PACKS = new LinkedHashSet<>();
|
public static final Set<PackMetaLoaded> DOWNLOADED_PACKS = new LinkedHashSet<>();
|
||||||
public static final Set<PackMetaLoaded> NEW_PACKS = new LinkedHashSet<>(); // Client-only!
|
public static final Set<PackMetaLoaded> NEW_PACKS = new LinkedHashSet<>(); // Client-only!
|
||||||
|
|
||||||
public static final String MOD_ID = "resclone";
|
public static final String MOD_ID = "resclone";
|
||||||
public static final SystemLoggerPlus LOGGER = SystemLoggerPlus.forName(MOD_ID);
|
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;
|
public static PackUrlCache urlCache;
|
||||||
|
|
||||||
@ -39,83 +34,90 @@ public class Resclone implements ModInitializer {
|
|||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
LOGGER.info("Initialising Resclone.");
|
LOGGER.info("Initialising Resclone.");
|
||||||
|
GsonHolder.register();
|
||||||
|
|
||||||
urlCache = new PackUrlCache(getConfigPath().resolve("urlCache.properties"));
|
urlCache = new PackUrlCache(getConfigPath().resolve("urlCache.properties"));
|
||||||
|
CONF.clear();
|
||||||
FETCHER_INSTANCES.clear();
|
FETCHER_INSTANCES.clear();
|
||||||
PROCESSORS.clear();
|
PROCESSORS.clear();
|
||||||
DOWNLOADED_PACKS.clear();
|
DOWNLOADED_PACKS.clear();
|
||||||
|
|
||||||
addProcessor(new RootPathProcessor()); //This should be run before any other processor to make sure the root is correct
|
addProcessor(new RootPathProcessor()); //This should be run before any other processor to make sure the path is valid
|
||||||
addFetcher(new BasicFileFetcher());
|
for (RescloneEntry entry : FabricLoader.getInstance().getEntrypoints(MOD_ID, RescloneEntry.class)) {
|
||||||
addFetcher(new GitHubFetcher());
|
try {
|
||||||
addFetcher(new CurseforgeFetcher());
|
entry.init(this);
|
||||||
addFetcher(new ModrinthFetcher());
|
} catch (Exception e) {
|
||||||
if (RescloneConfig.filterPacks) {
|
Resclone.LOGGER.error("Could not initialize resclone pack supplier", e);
|
||||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) addProcessor(new PruneVanillaProcessor());
|
}
|
||||||
addProcessor(new RemoveEmptyProcessor());
|
|
||||||
}
|
}
|
||||||
|
addProcessor(new RemoveEmptyProcessor());
|
||||||
reload();
|
reload();
|
||||||
|
|
||||||
LOGGER.info("Installed {0} resource pack{1}.", packCount, packCount == 1 ? "" : "s");
|
LOGGER.info("Installed {} resource pack{}.", packCount, packCount == 1 ? "" : "s");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addFetcher(PackFetcher fetcher) {
|
public void addFetcher(PackFetcher fetcher) {
|
||||||
FETCHER_INSTANCES.put(fetcher.getSourceTypeName(), fetcher);
|
FETCHER_INSTANCES.put(fetcher.getSourceTypeName(), fetcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addProcessor(PackProcessor processor) {
|
public void addProcessor(PackProcessor processor) {
|
||||||
PROCESSORS.add(processor);
|
PROCESSORS.add(processor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addPack(String fetcher, String pack, String name) {
|
public void addPack(String fetcher, String pack, String name) {
|
||||||
addPack(fetcher, pack, name, false);
|
addPack(fetcher, pack, name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addPack(String fetcher, String pack, String name, boolean forceRedownload) {
|
public void addPack(String fetcher, String pack, String name, boolean forceRedownload) {
|
||||||
addPack(fetcher, pack, name, forceRedownload, false);
|
addPack(fetcher, pack, name, forceRedownload, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addPack(String fetcher, String pack, String name, boolean forceRedownload, boolean forceEnable) {
|
public void addPack(String fetcher, String pack, String name, boolean forceRedownload, boolean forceEnable) {
|
||||||
RescloneConfig.packs.add(new PackMetaUnloaded(fetcher, pack, name, forceRedownload, forceEnable));
|
CONF.add(new PackMetaUnloaded(fetcher, pack, name, forceRedownload, forceEnable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
Set<PackMetaLoaded> metas = new LinkedHashSet<>();
|
Set<PackMetaLoaded> metas = new LinkedHashSet<>();
|
||||||
if (RescloneConfig.packs.isEmpty()) {
|
try {
|
||||||
LOGGER.info("No resclone pack was specified, add one");
|
if (CONF.isEmpty()) {
|
||||||
}
|
LOGGER.info("No resclone pack was specified, add one");
|
||||||
else {
|
}
|
||||||
try (ExecutorService pool = Executors.newFixedThreadPool(RescloneConfig.packs.size())) {
|
else {
|
||||||
for (PackMetaUnloaded s : RescloneConfig.packs) {
|
ExecutorService pool = Executors.newFixedThreadPool(CONF.size());
|
||||||
|
for (PackMetaUnloaded s : CONF) {
|
||||||
pool.submit(generateTask(s, metas));
|
pool.submit(generateTask(s, metas));
|
||||||
}
|
}
|
||||||
pool.shutdown();
|
pool.shutdown();
|
||||||
if (!pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS)) {
|
if (!pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS)) {
|
||||||
LOGGER.error("Download timed out. This shouldn't be possible");
|
LOGGER.error("Download timed out. This shouldn't be possible");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOGGER.error("Could not execute pack download task", e);
|
|
||||||
}
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOGGER.error("Could not execute pack download task", e);
|
||||||
}
|
}
|
||||||
urlCache.save();
|
urlCache.save();
|
||||||
DOWNLOADED_PACKS.clear();
|
DOWNLOADED_PACKS.clear();
|
||||||
DOWNLOADED_PACKS.addAll(metas);
|
DOWNLOADED_PACKS.addAll(metas);
|
||||||
|
|
||||||
if (RescloneConfig.pruneUnused) pruneCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Runnable generateTask(PackMetaUnloaded meta, Set<PackMetaLoaded> metas) {
|
private Runnable generateTask(PackMetaUnloaded meta, Set<PackMetaLoaded> metas) {
|
||||||
return () -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
if (!FETCHER_INSTANCES.containsKey(meta.fetcher()))
|
if (!FETCHER_INSTANCES.containsKey(meta.fetcher))
|
||||||
throw new Exception("Invalid fetcher: " + meta.fetcher());
|
throw new Exception("Invalid fetcher: " + meta.fetcher);
|
||||||
Path cacheDir = getConfigPath().resolve("cache");
|
Path cacheDir = getConfigPath().resolve("cache");
|
||||||
PackMetaLoaded p;
|
PackMetaLoaded p;
|
||||||
try {
|
try {
|
||||||
boolean isNew = !urlCache.containsKey(meta.source());
|
boolean isNew = !urlCache.containsKey(meta.source);
|
||||||
//Download
|
//Download
|
||||||
PackFetcher.Result fr = FETCHER_INSTANCES.get(meta.fetcher()).get(meta.source(), cacheDir, meta.forceDownload());
|
PackFetcher.Result fr = FETCHER_INSTANCES.get(meta.fetcher).get(meta.source, cacheDir, meta.forceDownload);
|
||||||
p = new PackMetaLoaded(fr.downloadPath(), meta.name(), meta.forceEnable());
|
p = new PackMetaLoaded(fr.downloadPath(), meta.name, meta.forceEnable);
|
||||||
metas.add(p);
|
metas.add(p);
|
||||||
if (isNew && FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) NEW_PACKS.add(p);
|
if (isNew && FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) NEW_PACKS.add(p);
|
||||||
if (fr.freshDownload()) {
|
if (fr.freshDownload()) {
|
||||||
@ -135,35 +137,14 @@ public class Resclone implements ModInitializer {
|
|||||||
throw new Exception("Failed to download pack", e);
|
throw new Exception("Failed to download pack", e);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
LOGGER.error("Encountered issue while preparing " + meta.name(), e);
|
LOGGER.error("Encountered issue while preparing " + meta.name, e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pruneCache() {
|
@Override
|
||||||
Set<Path> loadedPacks = DOWNLOADED_PACKS.stream().map(PackMetaLoaded::zipPath).collect(Collectors.toUnmodifiableSet());
|
public Path getConfigPath() {
|
||||||
Set<Path> toDelete = new HashSet<>();
|
Path configPath = FabricLoader.getInstance().getConfigDir().resolve("resclone");
|
||||||
try (Stream<Path> cacheEntries = Files.list(getConfigPath().resolve("cache"))) {
|
|
||||||
cacheEntries
|
|
||||||
.filter(s -> !Files.isRegularFile(s) || !loadedPacks.contains(s))
|
|
||||||
.forEach(toDelete::add);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.error("Could find cache entries to prune", e);
|
|
||||||
}
|
|
||||||
if (!toDelete.isEmpty()) {
|
|
||||||
LOGGER.info("Pruning " + toDelete.size() + " unused cache entries");
|
|
||||||
for (Path path : toDelete) {
|
|
||||||
try {
|
|
||||||
Files.delete(path);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.error("Could not delete unused cache entry: " + path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Path getConfigPath() {
|
|
||||||
Path configPath = FabricLoader.getInstance().getConfigDir().resolve(MOD_ID);
|
|
||||||
if (!Files.isDirectory(configPath.resolve("cache"))) {
|
if (!Files.isDirectory(configPath.resolve("cache"))) {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(configPath.resolve("cache"));
|
Files.createDirectories(configPath.resolve("cache"));
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
package io.gitlab.jfronny.resclone;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
|
|
||||||
import io.gitlab.jfronny.commons.serialize.Token;
|
|
||||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
|
||||||
import io.gitlab.jfronny.commons.serialize.json.JsonWriter;
|
|
||||||
import io.gitlab.jfronny.libjf.LibJf;
|
|
||||||
import io.gitlab.jfronny.libjf.config.api.v2.JfCustomConfig;
|
|
||||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.DSL;
|
|
||||||
import io.gitlab.jfronny.resclone.data.GC_PackMetaUnloaded;
|
|
||||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
|
||||||
import io.gitlab.jfronny.resclone.util.ListAdaptation;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class RescloneConfig implements JfCustomConfig {
|
|
||||||
public static Set<PackMetaUnloaded> packs;
|
|
||||||
public static boolean pruneUnused;
|
|
||||||
public static boolean filterPacks;
|
|
||||||
public static boolean logProcessing;
|
|
||||||
|
|
||||||
private static final String ERR_DUPLICATE = "Unexpected duplicate \"%s\" in Resclone config";
|
|
||||||
private static final String PACKS = "packs";
|
|
||||||
private static final String PRUNE_UNUSED = "pruneUnused";
|
|
||||||
private static final String FILTER_PACKS = "filterPacks";
|
|
||||||
private static final String LOG_PROCESSING = "logProcessing";
|
|
||||||
|
|
||||||
private static void load(Path path) throws IOException {
|
|
||||||
if (!Files.exists(path)) {
|
|
||||||
packs = new HashSet<>();
|
|
||||||
pruneUnused = true;
|
|
||||||
filterPacks = true;
|
|
||||||
logProcessing = false;
|
|
||||||
write(path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean updateRequired = false;
|
|
||||||
try (BufferedReader br = Files.newBufferedReader(path);
|
|
||||||
JsonReader reader = LibJf.LENIENT_TRANSPORT.createReader(br)) {
|
|
||||||
if (reader.peek() == Token.BEGIN_ARRAY) {
|
|
||||||
// Legacy format compatibility
|
|
||||||
packs = ListAdaptation.deserializeSet(reader, GC_PackMetaUnloaded::deserialize);
|
|
||||||
updateRequired = true;
|
|
||||||
} else if (reader.peek() == Token.BEGIN_OBJECT) {
|
|
||||||
// New format
|
|
||||||
reader.beginObject();
|
|
||||||
Set<PackMetaUnloaded> packs = null;
|
|
||||||
Boolean pruneUnused = null;
|
|
||||||
Boolean filterPacks = null;
|
|
||||||
Boolean logProcessing = null;
|
|
||||||
while (reader.peek() != Token.END_OBJECT) {
|
|
||||||
final String name = reader.nextName();
|
|
||||||
switch (name) {
|
|
||||||
case PACKS -> {
|
|
||||||
if (packs != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(PACKS));
|
|
||||||
if (reader.peek() == Token.BEGIN_ARRAY) {
|
|
||||||
packs = ListAdaptation.deserializeSet(reader, GC_PackMetaUnloaded::deserialize);
|
|
||||||
} else {
|
|
||||||
packs = Set.of(GC_PackMetaUnloaded.deserialize(reader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case PRUNE_UNUSED -> {
|
|
||||||
if (pruneUnused != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(PRUNE_UNUSED));
|
|
||||||
pruneUnused = reader.nextBoolean();
|
|
||||||
}
|
|
||||||
case FILTER_PACKS -> {
|
|
||||||
if (filterPacks != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(FILTER_PACKS));
|
|
||||||
filterPacks = reader.nextBoolean();
|
|
||||||
}
|
|
||||||
case LOG_PROCESSING -> {
|
|
||||||
if (logProcessing != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(LOG_PROCESSING));
|
|
||||||
logProcessing = reader.nextBoolean();
|
|
||||||
}
|
|
||||||
default -> throw new MalformedDataException("Unexpected element: \"" + name + "\" in Resclone config");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader.endObject();
|
|
||||||
if (packs == null) throw new MalformedDataException("Expected Resclone config object to contain packs");
|
|
||||||
if (pruneUnused == null) {
|
|
||||||
pruneUnused = true;
|
|
||||||
updateRequired = true;
|
|
||||||
}
|
|
||||||
if (filterPacks == null) {
|
|
||||||
filterPacks = true;
|
|
||||||
updateRequired = true;
|
|
||||||
}
|
|
||||||
if (logProcessing == null) {
|
|
||||||
logProcessing = false;
|
|
||||||
updateRequired = true;
|
|
||||||
}
|
|
||||||
RescloneConfig.packs = packs;
|
|
||||||
RescloneConfig.pruneUnused = pruneUnused;
|
|
||||||
RescloneConfig.filterPacks = filterPacks;
|
|
||||||
RescloneConfig.logProcessing = logProcessing;
|
|
||||||
} else throw new MalformedDataException("Expected Resclone config to be an object or array");
|
|
||||||
}
|
|
||||||
if (updateRequired) write(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void write(Path path) throws IOException {
|
|
||||||
try (BufferedWriter bw = Files.newBufferedWriter(path);
|
|
||||||
JsonWriter writer = LibJf.LENIENT_TRANSPORT.createWriter(bw)) {
|
|
||||||
writer.beginObject()
|
|
||||||
.comment("The packs to be loaded by resclone")
|
|
||||||
.name(PACKS)
|
|
||||||
.beginArray();
|
|
||||||
for (PackMetaUnloaded pack : packs) {
|
|
||||||
GC_PackMetaUnloaded.serialize(pack, writer);
|
|
||||||
}
|
|
||||||
writer.endArray()
|
|
||||||
.comment("Automatically remove all downloaded packs that are not in the config to free up unneeded space")
|
|
||||||
.name(PRUNE_UNUSED)
|
|
||||||
.value(pruneUnused)
|
|
||||||
.comment("Whether to filter packs to remove files unchanged from vanilla and empty directories")
|
|
||||||
.name(FILTER_PACKS)
|
|
||||||
.value(filterPacks)
|
|
||||||
.comment("Log automatic processing steps applied to downloaded packs")
|
|
||||||
.name(LOG_PROCESSING)
|
|
||||||
.value(logProcessing)
|
|
||||||
.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
Path path = Resclone.getConfigPath().resolve("config.json");
|
|
||||||
DSL.create(Resclone.MOD_ID).register(builder ->
|
|
||||||
builder.setLoadMethod(configInstance -> {
|
|
||||||
try {
|
|
||||||
load(path);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Resclone.LOGGER.error("Could not load config", e);
|
|
||||||
}
|
|
||||||
}).setWriteMethod(configInstance -> {
|
|
||||||
try {
|
|
||||||
write(path);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Resclone.LOGGER.error("Could not write config", e);
|
|
||||||
}
|
|
||||||
}).setPath(path)
|
|
||||||
.<Set<PackMetaUnloaded>>value(PACKS, new HashSet<>(), Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, io.gitlab.jfronny.libjf.config.api.v2.type.Type.ofClass(new TypeToken<Set<PackMetaUnloaded>>(){}.getType()), 100, () -> packs, p -> packs = p)
|
|
||||||
.value(PRUNE_UNUSED, pruneUnused, () -> pruneUnused, p -> pruneUnused = p)
|
|
||||||
.value(FILTER_PACKS, filterPacks, () -> filterPacks, p -> filterPacks = p)
|
|
||||||
.value(LOG_PROCESSING, logProcessing, () -> logProcessing, p -> logProcessing = p)
|
|
||||||
).load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void register(DSL.Defaulted dsl) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,23 @@
|
|||||||
|
package io.gitlab.jfronny.resclone;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.resclone.api.RescloneApi;
|
||||||
|
import io.gitlab.jfronny.resclone.api.RescloneEntry;
|
||||||
|
import io.gitlab.jfronny.resclone.util.ConfigLoader;
|
||||||
|
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.processors.PruneVanillaProcessor;
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
|
||||||
|
public class RescloneEntryDefault implements RescloneEntry {
|
||||||
|
@Override
|
||||||
|
public void init(RescloneApi api) {
|
||||||
|
api.addFetcher(new BasicFileFetcher());
|
||||||
|
api.addFetcher(new GitHubFetcher());
|
||||||
|
api.addFetcher(new CurseforgeFetcher());
|
||||||
|
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||||
|
api.addProcessor(new PruneVanillaProcessor());
|
||||||
|
ConfigLoader.load(api);
|
||||||
|
}
|
||||||
|
}
|
@ -3,57 +3,26 @@ package io.gitlab.jfronny.resclone;
|
|||||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.api.metadata.ModMetadata;
|
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||||
import net.minecraft.resource.*;
|
import net.minecraft.resource.ZipResourcePack;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class RescloneResourcePack extends ZipResourcePack implements ModResourcePack {
|
public class RescloneResourcePack extends ZipResourcePack implements ModResourcePack {
|
||||||
private static final ModMetadata METADATA = FabricLoader.getInstance().getModContainer(Resclone.MOD_ID).orElseThrow().getMetadata();
|
private static final ModMetadata METADATA = FabricLoader.getInstance().getModContainer(Resclone.MOD_ID).get().getMetadata();
|
||||||
private final ResourcePackInfo info;
|
private final String name;
|
||||||
private final ZipFileWrapper file;
|
|
||||||
|
|
||||||
RescloneResourcePack(ZipFileWrapper file, ResourcePackInfo info, String overlay) {
|
public RescloneResourcePack(File file, String name) {
|
||||||
super(info, file, overlay);
|
super(file);
|
||||||
this.info = info;
|
this.name = name;
|
||||||
this.file = file;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModMetadata getFabricModMetadata() {
|
public ModMetadata getFabricModMetadata() {
|
||||||
return METADATA;
|
return METADATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ModResourcePack createOverlay(String overlay) {
|
|
||||||
return new RescloneResourcePack(this.file, this.info, overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements ResourcePackProfile.PackFactory {
|
|
||||||
private final File file;
|
|
||||||
private final ResourcePackInfo info;
|
|
||||||
|
|
||||||
public Factory(File file, ResourcePackInfo info) {
|
|
||||||
this.file = file;
|
|
||||||
this.info = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourcePack open(ResourcePackInfo info) {
|
|
||||||
ZipFileWrapper zipFileWrapper = new ZipFileWrapper(this.file);
|
|
||||||
return new RescloneResourcePack(zipFileWrapper, this.info, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourcePack openWithOverlays(ResourcePackInfo info, ResourcePackProfile.Metadata metadata) {
|
|
||||||
ZipFileWrapper zipFileWrapper = new ZipFileWrapper(this.file);
|
|
||||||
ZipResourcePack resourcePack = new RescloneResourcePack(zipFileWrapper, this.info, "");
|
|
||||||
List<String> overlays = metadata.overlays();
|
|
||||||
if (overlays.isEmpty()) return resourcePack;
|
|
||||||
List<ResourcePack> overlayPacks = new ArrayList<>(overlays.size());
|
|
||||||
for (String string : overlays) overlayPacks.add(new RescloneResourcePack(zipFileWrapper, this.info, string));
|
|
||||||
return new OverlayResourcePack(resourcePack, overlayPacks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package io.gitlab.jfronny.resclone.fetchers;
|
package io.gitlab.jfronny.resclone.api;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package io.gitlab.jfronny.resclone.processors;
|
package io.gitlab.jfronny.resclone.api;
|
||||||
|
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
|
|
@ -0,0 +1,19 @@
|
|||||||
|
package io.gitlab.jfronny.resclone.api;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public interface RescloneApi {
|
||||||
|
void addFetcher(PackFetcher fetcher);
|
||||||
|
|
||||||
|
void addProcessor(PackProcessor processor);
|
||||||
|
|
||||||
|
void addPack(String fetcher, String pack, String name);
|
||||||
|
|
||||||
|
void addPack(String fetcher, String pack, String name, boolean forceRedownload);
|
||||||
|
|
||||||
|
void addPack(String fetcher, String pack, String name, boolean forceRedownload, boolean forceEnable);
|
||||||
|
|
||||||
|
void reload();
|
||||||
|
|
||||||
|
Path getConfigPath();
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package io.gitlab.jfronny.resclone.api;
|
||||||
|
|
||||||
|
public interface RescloneEntry {
|
||||||
|
void init(RescloneApi api) throws Exception;
|
||||||
|
}
|
@ -1,7 +1,20 @@
|
|||||||
package io.gitlab.jfronny.resclone.data;
|
package io.gitlab.jfronny.resclone.data;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
// Represents a pack as present in the config
|
||||||
|
// Can't be a record since it'll need to be parsed by Gson
|
||||||
|
@SuppressWarnings("ClassCanBeRecord")
|
||||||
|
public class PackMetaUnloaded {
|
||||||
|
public final String fetcher;
|
||||||
|
public final String source;
|
||||||
|
public final String name;
|
||||||
|
public final boolean forceDownload;
|
||||||
|
public final boolean forceEnable;
|
||||||
|
|
||||||
@GSerializable
|
public PackMetaUnloaded(String fetcher, String source, String name, boolean forceDownload, boolean forceEnable) {
|
||||||
public record PackMetaUnloaded(String fetcher, String source, String name, boolean forceDownload, boolean forceEnable) {
|
this.fetcher = fetcher;
|
||||||
|
this.source = source;
|
||||||
|
this.name = name;
|
||||||
|
this.forceDownload = forceDownload;
|
||||||
|
this.forceEnable = forceEnable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
package io.gitlab.jfronny.resclone.data.curseforge;
|
package io.gitlab.jfronny.resclone.data.curseforge;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
import java.util.*;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public class GetModFilesResponse {
|
public class GetModFilesResponse {
|
||||||
public List<Data> data;
|
public List<Data> data;
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public static class Data {
|
public static class Data {
|
||||||
public String downloadUrl;
|
public String downloadUrl;
|
||||||
public Date fileDate;
|
public Date fileDate;
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
package io.gitlab.jfronny.resclone.data.curseforge;
|
package io.gitlab.jfronny.resclone.data.curseforge;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public class GetModResponse {
|
public class GetModResponse {
|
||||||
public Data data;
|
public Data data;
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public static class Data {
|
public static class Data {
|
||||||
public Boolean allowModDistribution;
|
public Boolean allowModDistribution;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
package io.gitlab.jfronny.resclone.data.github;
|
package io.gitlab.jfronny.resclone.data.github;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
import java.util.*;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public class Release {
|
public class Release {
|
||||||
public List<Asset> assets;
|
public List<Asset> assets;
|
||||||
public String zipball_url;
|
public String zipball_url;
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public static class Asset {
|
public static class Asset {
|
||||||
public String name;
|
public String name;
|
||||||
public String content_type;
|
public String content_type;
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package io.gitlab.jfronny.resclone.data.github;
|
package io.gitlab.jfronny.resclone.data.github;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public class Repository {
|
public class Repository {
|
||||||
public String default_branch;
|
public String default_branch;
|
||||||
}
|
}
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
package io.gitlab.jfronny.resclone.data.modrinth;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.serialize.annotations.SerializedName;
|
|
||||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
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;
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
public static class File {
|
|
||||||
public Hashes hashes;
|
|
||||||
public String url;
|
|
||||||
public String filename;
|
|
||||||
public Boolean primary;
|
|
||||||
public Integer size;
|
|
||||||
@Nullable public Type file_type;
|
|
||||||
|
|
||||||
@GSerializable
|
|
||||||
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,9 +1,11 @@
|
|||||||
package io.gitlab.jfronny.resclone.fetchers;
|
package io.gitlab.jfronny.resclone.fetchers;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
import io.gitlab.jfronny.resclone.Resclone;
|
||||||
|
import io.gitlab.jfronny.resclone.api.PackFetcher;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
@ -32,8 +34,8 @@ public abstract class BasePackFetcher implements PackFetcher {
|
|||||||
|
|
||||||
Resclone.LOGGER.info("Downloading pack: " + url);
|
Resclone.LOGGER.info("Downloading pack: " + url);
|
||||||
|
|
||||||
try (InputStream is = HttpClient.get(url).userAgent(Resclone.USER_AGENT).sendInputStream();
|
try (InputStream is = new URL(url).openStream()) {
|
||||||
OutputStream os = Files.newOutputStream(p)) {
|
FileOutputStream os = new FileOutputStream(p.toFile());
|
||||||
byte[] dataBuffer = new byte[1024];
|
byte[] dataBuffer = new byte[1024];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
while ((bytesRead = is.read(dataBuffer, 0, 1024)) != -1) {
|
while ((bytesRead = is.read(dataBuffer, 0, 1024)) != -1) {
|
||||||
@ -41,7 +43,7 @@ public abstract class BasePackFetcher implements PackFetcher {
|
|||||||
}
|
}
|
||||||
Resclone.LOGGER.info("Finished downloading.");
|
Resclone.LOGGER.info("Finished downloading.");
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new IOException("Could not download pack", e);
|
throw new Exception("Could not download pack", e);
|
||||||
}
|
}
|
||||||
Resclone.packCount++;
|
Resclone.packCount++;
|
||||||
return new Result(p, true);
|
return new Result(p, true);
|
||||||
|
@ -1,22 +1,13 @@
|
|||||||
package io.gitlab.jfronny.resclone.fetchers;
|
package io.gitlab.jfronny.resclone.fetchers;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
import io.gitlab.jfronny.commons.*;
|
||||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
import io.gitlab.jfronny.resclone.*;
|
||||||
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
|
import io.gitlab.jfronny.resclone.data.curseforge.*;
|
||||||
import io.gitlab.jfronny.libjf.LibJf;
|
import net.minecraft.*;
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
|
||||||
import io.gitlab.jfronny.resclone.data.curseforge.GC_GetModFilesResponse;
|
|
||||||
import io.gitlab.jfronny.resclone.data.curseforge.GC_GetModResponse;
|
|
||||||
import io.gitlab.jfronny.resclone.data.curseforge.GetModFilesResponse;
|
|
||||||
import io.gitlab.jfronny.resclone.data.curseforge.GetModResponse;
|
|
||||||
import net.minecraft.MinecraftVersion;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
import java.net.*;
|
||||||
import java.io.Reader;
|
import java.util.*;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class CurseforgeFetcher extends BasePackFetcher {
|
public class CurseforgeFetcher extends BasePackFetcher {
|
||||||
// So you found the API key.
|
// So you found the API key.
|
||||||
@ -39,7 +30,7 @@ public class CurseforgeFetcher extends BasePackFetcher {
|
|||||||
@Override
|
@Override
|
||||||
public String getDownloadUrl(String baseUrl) throws Exception {
|
public String getDownloadUrl(String baseUrl) throws Exception {
|
||||||
try {
|
try {
|
||||||
GetModResponse response = GET(baseUrl, GC_GetModResponse::deserialize);
|
GetModResponse response = GET(baseUrl, GetModResponse.class);
|
||||||
if (!response.data.allowModDistribution)
|
if (!response.data.allowModDistribution)
|
||||||
throw new Exception("The author of " + baseUrl + " disabled access to this pack outside of the curseforge launcher");
|
throw new Exception("The author of " + baseUrl + " disabled access to this pack outside of the curseforge launcher");
|
||||||
|
|
||||||
@ -49,8 +40,9 @@ public class CurseforgeFetcher extends BasePackFetcher {
|
|||||||
Date latestDate = null;
|
Date latestDate = null;
|
||||||
boolean foundMatchingVersion = false;
|
boolean foundMatchingVersion = false;
|
||||||
|
|
||||||
for (GetModFilesResponse.Data addon : GET(baseUrl + "/files", GC_GetModFilesResponse::deserialize).data) {
|
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)) {
|
if (!foundMatchingVersion && addon.gameVersions.contains(version)) {
|
||||||
foundMatchingVersion = true;
|
foundMatchingVersion = true;
|
||||||
latest = null;
|
latest = null;
|
||||||
@ -61,19 +53,16 @@ public class CurseforgeFetcher extends BasePackFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (latest == null) throw new FileNotFoundException("Could not identify valid version");
|
if (!foundMatchingVersion)
|
||||||
if (!foundMatchingVersion) Resclone.LOGGER.error("Could not find matching version of " + baseUrl + ", using latest");
|
Resclone.LOGGER.error("Could not find pack for matching version, using latest");
|
||||||
return latest.downloadUrl;
|
return latest.downloadUrl;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new IOException("Could not get CurseForge download for " + baseUrl, e);
|
throw new Exception("Could not get CF download for " + baseUrl, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T GET(String suffix, ThrowingFunction<JsonReader, T, IOException> klazz) throws URISyntaxException, IOException {
|
private static <T> T GET(String suffix, Class<T> klazz) throws URISyntaxException, IOException {
|
||||||
try (Reader r = HttpClient.get("https://api.curseforge.com/v1/mods/" + suffix).header("x-api-key", API_KEY).sendReader();
|
return HttpUtils.get("https://api.curseforge.com/v1/mods/" + suffix).header("x-api-key", API_KEY).sendSerialized(klazz);
|
||||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
|
||||||
return klazz.apply(jr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] unsalt(byte[] data, int salt) {
|
private static byte[] unsalt(byte[] data, int salt) {
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
package io.gitlab.jfronny.resclone.fetchers;
|
package io.gitlab.jfronny.resclone.fetchers;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
import io.gitlab.jfronny.commons.*;
|
||||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
|
||||||
import io.gitlab.jfronny.libjf.LibJf;
|
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
import io.gitlab.jfronny.resclone.Resclone;
|
||||||
import io.gitlab.jfronny.resclone.data.github.GC_Release;
|
import io.gitlab.jfronny.resclone.data.github.*;
|
||||||
import io.gitlab.jfronny.resclone.data.github.GC_Repository;
|
|
||||||
import io.gitlab.jfronny.resclone.data.github.Release;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.net.*;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
|
|
||||||
public class GitHubFetcher extends BasePackFetcher {
|
public class GitHubFetcher extends BasePackFetcher {
|
||||||
@Override
|
@Override
|
||||||
@ -43,9 +38,8 @@ public class GitHubFetcher extends BasePackFetcher {
|
|||||||
|
|
||||||
//"user/repo/release" - Gets from the latest release.
|
//"user/repo/release" - Gets from the latest release.
|
||||||
else if (parts[2].equalsIgnoreCase("release")) {
|
else if (parts[2].equalsIgnoreCase("release")) {
|
||||||
try (Reader r = HttpClient.get("https://api.github.com/repos/" + parts[0] + "/" + parts[1] + "/releases/latest").sendReader();
|
try {
|
||||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
Release latestRelease = HttpUtils.get("https://api.github.com/repos/" + parts[0] + "/" + parts[1] + "/releases/latest").sendSerialized(Release.class);
|
||||||
Release latestRelease = GC_Release.deserialize(jr);
|
|
||||||
String res = null;
|
String res = null;
|
||||||
|
|
||||||
for (Release.Asset asset : latestRelease.assets) {
|
for (Release.Asset asset : latestRelease.assets) {
|
||||||
@ -78,9 +72,8 @@ public class GitHubFetcher extends BasePackFetcher {
|
|||||||
|
|
||||||
private String getFromBranch(String repo, @Nullable String branch) {
|
private String getFromBranch(String repo, @Nullable String branch) {
|
||||||
if (branch == null) {
|
if (branch == null) {
|
||||||
try (Reader r = HttpClient.get("https://api.github.com/repos/" + repo).sendReader();
|
try {
|
||||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
branch = HttpUtils.get("https://api.github.com/repos/" + repo).<Repository>sendSerialized(Repository.class).default_branch;
|
||||||
branch = GC_Repository.deserialize(jr).default_branch;
|
|
||||||
} catch (IOException | URISyntaxException e) {
|
} catch (IOException | URISyntaxException e) {
|
||||||
Resclone.LOGGER.error("Failed to fetch branch for " + repo + ". Choosing \"main\"", e);
|
Resclone.LOGGER.error("Failed to fetch branch for " + repo + ". Choosing \"main\"", e);
|
||||||
branch = "main";
|
branch = "main";
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
package io.gitlab.jfronny.resclone.fetchers;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
|
||||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
|
||||||
import io.gitlab.jfronny.libjf.LibJf;
|
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
|
||||||
import io.gitlab.jfronny.resclone.data.modrinth.GC_Version;
|
|
||||||
import io.gitlab.jfronny.resclone.data.modrinth.Version;
|
|
||||||
import io.gitlab.jfronny.resclone.util.ListAdaptation;
|
|
||||||
import net.minecraft.MinecraftVersion;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ModrinthFetcher extends BasePackFetcher {
|
|
||||||
@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;
|
|
||||||
|
|
||||||
List<Version> versions;
|
|
||||||
try (Reader r = HttpClient.get("https://api.modrinth.com/v2/project/" + baseUrl + "/version")
|
|
||||||
.userAgent(Resclone.USER_AGENT)
|
|
||||||
.sendReader();
|
|
||||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
|
||||||
versions = ListAdaptation.deserializeList(jr, GC_Version::deserialize);
|
|
||||||
}
|
|
||||||
for (Version ver : versions) {
|
|
||||||
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.getFirst().url;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new IOException("Could not get Modrinth download for " + baseUrl, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,39 +3,32 @@ package io.gitlab.jfronny.resclone.mixin;
|
|||||||
import io.gitlab.jfronny.resclone.Resclone;
|
import io.gitlab.jfronny.resclone.Resclone;
|
||||||
import io.gitlab.jfronny.resclone.RescloneResourcePack;
|
import io.gitlab.jfronny.resclone.RescloneResourcePack;
|
||||||
import io.gitlab.jfronny.resclone.data.PackMetaLoaded;
|
import io.gitlab.jfronny.resclone.data.PackMetaLoaded;
|
||||||
import net.minecraft.resource.*;
|
import net.minecraft.resource.FileResourcePackProvider;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.resource.ResourcePackProfile;
|
||||||
import org.spongepowered.asm.mixin.*;
|
import net.minecraft.resource.ResourcePackSource;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Mixin(FileResourcePackProvider.class)
|
@Mixin(FileResourcePackProvider.class)
|
||||||
public class FileResourcePackProviderMixin {
|
public class FileResourcePackProviderMixin {
|
||||||
@Shadow @Final private ResourcePackSource source;
|
@Shadow @Final private ResourcePackSource source;
|
||||||
@Shadow @Final private ResourceType type;
|
|
||||||
|
|
||||||
@Inject(at = @At("TAIL"), method = "register(Ljava/util/function/Consumer;)V")
|
@Inject(at = @At("TAIL"), method = "register(Ljava/util/function/Consumer;Lnet/minecraft/resource/ResourcePackProfile$Factory;)V")
|
||||||
public void registerExtra(Consumer<ResourcePackProfile> consumer, CallbackInfo info) {
|
public void registerExtra(Consumer<ResourcePackProfile> consumer, ResourcePackProfile.Factory factory, CallbackInfo info) {
|
||||||
for (PackMetaLoaded meta : Resclone.DOWNLOADED_PACKS) {
|
for (PackMetaLoaded meta : Resclone.DOWNLOADED_PACKS) {
|
||||||
ResourcePackInfo ifo = new ResourcePackInfo(
|
ResourcePackProfile resourcePackProfile = ResourcePackProfile.of(
|
||||||
"resclone/" + meta.name(),
|
"resclone/" + meta.name(),
|
||||||
Text.literal(meta.name()),
|
meta.forceEnable(),
|
||||||
source,
|
() -> new RescloneResourcePack(meta.zipPath().toFile(), meta.name()),
|
||||||
Optional.empty()
|
factory,
|
||||||
);
|
ResourcePackProfile.InsertionPosition.TOP,
|
||||||
ResourcePackProfile resourcePackProfile = ResourcePackProfile.create(
|
this.source
|
||||||
ifo,
|
|
||||||
new RescloneResourcePack.Factory(meta.zipPath().toFile(), ifo),
|
|
||||||
type,
|
|
||||||
new ResourcePackPosition(
|
|
||||||
meta.forceEnable(),
|
|
||||||
ResourcePackProfile.InsertionPosition.TOP,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
if (resourcePackProfile != null) {
|
if (resourcePackProfile != null) {
|
||||||
consumer.accept(resourcePackProfile);
|
consumer.accept(resourcePackProfile);
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package io.gitlab.jfronny.resclone.processors;
|
package io.gitlab.jfronny.resclone.processors;
|
||||||
|
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
import io.gitlab.jfronny.resclone.*;
|
||||||
import io.gitlab.jfronny.resclone.RescloneConfig;
|
import io.gitlab.jfronny.resclone.api.PackProcessor;
|
||||||
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
|
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.*;
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
public class PruneVanillaProcessor implements PackProcessor {
|
public class PruneVanillaProcessor implements PackProcessor {
|
||||||
@Override
|
@Override
|
||||||
@ -23,10 +25,7 @@ public class PruneVanillaProcessor implements PackProcessor {
|
|||||||
InputStream vn = cl.getResourceAsStream(p.getPath("/").relativize(s).toString());
|
InputStream vn = cl.getResourceAsStream(p.getPath("/").relativize(s).toString());
|
||||||
if (vn != null) {
|
if (vn != null) {
|
||||||
try (InputStream pk = Files.newInputStream(s, StandardOpenOption.READ)) {
|
try (InputStream pk = Files.newInputStream(s, StandardOpenOption.READ)) {
|
||||||
if (IOUtils.contentEquals(vn, pk)) {
|
return IOUtils.contentEquals(vn, pk);
|
||||||
if (RescloneConfig.logProcessing) Resclone.LOGGER.info("Pruning file unchanged from vanilla: {}", s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package io.gitlab.jfronny.resclone.processors;
|
package io.gitlab.jfronny.resclone.processors;
|
||||||
|
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
import io.gitlab.jfronny.resclone.*;
|
||||||
import io.gitlab.jfronny.resclone.RescloneConfig;
|
import io.gitlab.jfronny.resclone.api.PackProcessor;
|
||||||
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
|
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class RemoveEmptyProcessor implements PackProcessor {
|
public class RemoveEmptyProcessor implements PackProcessor {
|
||||||
@Override
|
@Override
|
||||||
@ -15,10 +18,7 @@ public class RemoveEmptyProcessor implements PackProcessor {
|
|||||||
Files.walkFileTree(p.getPath("/assets"), new PathPruneVisitor(s -> {
|
Files.walkFileTree(p.getPath("/assets"), new PathPruneVisitor(s -> {
|
||||||
if (Files.isDirectory(s)) {
|
if (Files.isDirectory(s)) {
|
||||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(s)) {
|
try (DirectoryStream<Path> paths = Files.newDirectoryStream(s)) {
|
||||||
if (!paths.iterator().hasNext()) {
|
return !paths.iterator().hasNext();
|
||||||
if (RescloneConfig.logProcessing) Resclone.LOGGER.info("Pruning empty directory: {}", s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Resclone.LOGGER.error("Could not check whether directory has entries", e);
|
Resclone.LOGGER.error("Could not check whether directory has entries", e);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package io.gitlab.jfronny.resclone.processors;
|
package io.gitlab.jfronny.resclone.processors;
|
||||||
|
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
import io.gitlab.jfronny.resclone.api.PackProcessor;
|
||||||
import io.gitlab.jfronny.resclone.RescloneConfig;
|
|
||||||
import io.gitlab.jfronny.resclone.util.io.MoveDirVisitor;
|
import io.gitlab.jfronny.resclone.util.io.MoveDirVisitor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -16,7 +15,6 @@ public class RootPathProcessor implements PackProcessor {
|
|||||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(root)) {
|
try (DirectoryStream<Path> paths = Files.newDirectoryStream(root)) {
|
||||||
for (Path path : paths) {
|
for (Path path : paths) {
|
||||||
if (Files.isDirectory(path) && Files.exists(path.resolve("pack.mcmeta"))) {
|
if (Files.isDirectory(path) && Files.exists(path.resolve("pack.mcmeta"))) {
|
||||||
if (RescloneConfig.logProcessing) Resclone.LOGGER.info("Moving discovered root out of: {}", path);
|
|
||||||
Files.walkFileTree(path, new MoveDirVisitor(path, root, StandardCopyOption.REPLACE_EXISTING));
|
Files.walkFileTree(path, new MoveDirVisitor(path, root, StandardCopyOption.REPLACE_EXISTING));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package io.gitlab.jfronny.resclone.util;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import io.gitlab.jfronny.commons.serialize.gson.api.*;
|
||||||
|
import io.gitlab.jfronny.resclone.Resclone;
|
||||||
|
import io.gitlab.jfronny.resclone.api.RescloneApi;
|
||||||
|
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ConfigLoader {
|
||||||
|
public static void load(RescloneApi api) {
|
||||||
|
Path configPath = api.getConfigPath().resolve("config.json");
|
||||||
|
if (!Files.exists(configPath)) {
|
||||||
|
save(api, new HashSet<>());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
StringBuilder text = new StringBuilder();
|
||||||
|
for (String s : Files.readAllLines(configPath)) {
|
||||||
|
text.append("\r\n");
|
||||||
|
text.append(s);
|
||||||
|
}
|
||||||
|
Set<PackMetaUnloaded> data = GsonHolder.getGson().fromJson(text.toString(), new TypeToken<Set<PackMetaUnloaded>>(){}.getType());
|
||||||
|
for (PackMetaUnloaded meta : data) {
|
||||||
|
api.addPack(meta.fetcher, meta.source, meta.name, meta.forceDownload, meta.forceEnable);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Resclone.LOGGER.error("Could not load config", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void save(RescloneApi api, Set<PackMetaUnloaded> data) {
|
||||||
|
Path configPath = api.getConfigPath().resolve("config.json");
|
||||||
|
Set<String> text = new HashSet<>();
|
||||||
|
text.add(GsonHolder.getGson().toJson(data));
|
||||||
|
try {
|
||||||
|
Files.write(configPath, text);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Resclone.LOGGER.error("Could not write config", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package io.gitlab.jfronny.resclone.util;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
|
||||||
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class ListAdaptation {
|
|
||||||
public static <T, TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> List<T> deserializeList(Reader reader, ThrowingFunction<Reader, T, TEx> deserializeOne) throws TEx {
|
|
||||||
List<T> result = new ArrayList<>();
|
|
||||||
reader.beginArray();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
result.add(deserializeOne.apply(reader));
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T, TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Set<T> deserializeSet(Reader reader, ThrowingFunction<Reader, T, TEx> serializeOne) throws TEx {
|
|
||||||
Set<T> result = new LinkedHashSet<>();
|
|
||||||
reader.beginArray();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
result.add(serializeOne.apply(reader));
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,10 @@
|
|||||||
package io.gitlab.jfronny.resclone.util;
|
package io.gitlab.jfronny.resclone.util;
|
||||||
|
|
||||||
import io.gitlab.jfronny.resclone.Resclone;
|
import io.gitlab.jfronny.resclone.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package io.gitlab.jfronny.resclone.util.io;
|
package io.gitlab.jfronny.resclone.util.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package io.gitlab.jfronny.resclone.util.io;
|
package io.gitlab.jfronny.resclone.util.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
|
||||||
public class RemoveDirVisitor extends SimpleFileVisitor<Path> {
|
public class RemoveDirVisitor extends SimpleFileVisitor<Path> {
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"resclone.jfconfig.title": "Resclone",
|
|
||||||
"resclone.jfconfig.packs": "Packs",
|
|
||||||
"resclone.jfconfig.packs.tooltip": "The packs to download and add",
|
|
||||||
"resclone.jfconfig.pruneUnused": "Prune Unused",
|
|
||||||
"resclone.jfconfig.pruneUnused.tooltip": "Automatically remove all downloaded packs that are not in the config to free up unneeded space",
|
|
||||||
"resclone.jfconfig.filterPacks": "Filter Packs",
|
|
||||||
"resclone.jfconfig.filterPacks.tooltip": "Whether to filter packs to remove files unchanged from vanilla and empty directories",
|
|
||||||
"resclone.jfconfig.logProcessing": "Log Processing",
|
|
||||||
"resclone.jfconfig.logProcessing.tooltip": "Log automatic processing steps applied to downloaded packs"
|
|
||||||
}
|
|
@ -1,23 +1,22 @@
|
|||||||
{
|
{
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"id": "resclone",
|
"id": "resclone",
|
||||||
"name": "Resclone",
|
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"description": "Downloads and updates resource packs",
|
"name": "Resclone",
|
||||||
"authors": ["JFronny"],
|
"description": "Downloads and updates resourcepacks.",
|
||||||
"contact": {
|
"authors": [
|
||||||
"email": "projects.contact@frohnmeyer-wds.de",
|
"JFronny"
|
||||||
"homepage": "https://jfronny.gitlab.io",
|
],
|
||||||
"issues": "https://git.frohnmeyer-wds.de/JfMods/Resclone/issues",
|
"contact": {},
|
||||||
"sources": "https://git.frohnmeyer-wds.de/JfMods/Resclone"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "assets/resclone/icon.png",
|
"icon": "assets/resclone/icon.png",
|
||||||
"environment": "*",
|
"environment": "*",
|
||||||
"entrypoints": {
|
"entrypoints": {
|
||||||
"main": ["io.gitlab.jfronny.resclone.Resclone"],
|
"main": [
|
||||||
"libjf:config": [
|
"io.gitlab.jfronny.resclone.Resclone"
|
||||||
"io.gitlab.jfronny.resclone.RescloneConfig"
|
],
|
||||||
|
"resclone": [
|
||||||
|
"io.gitlab.jfronny.resclone.RescloneEntryDefault"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mixins": [
|
"mixins": [
|
||||||
@ -27,7 +26,6 @@
|
|||||||
"environment": "client"
|
"environment": "client"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"accessWidener": "resclone.accesswidener",
|
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.12.0",
|
"fabricloader": ">=0.12.0",
|
||||||
"minecraft": "*",
|
"minecraft": "*",
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
accessWidener v2 named
|
|
||||||
|
|
||||||
accessible class net/minecraft/resource/ZipResourcePack$ZipFileWrapper
|
|
||||||
accessible method net/minecraft/resource/ZipResourcePack <init> (Lnet/minecraft/resource/ResourcePackInfo;Lnet/minecraft/resource/ZipResourcePack$ZipFileWrapper;Ljava/lang/String;)V
|
|
||||||
accessible method net/minecraft/resource/ZipResourcePack$ZipFileWrapper <init> (Ljava/io/File;)V
|
|
@ -2,6 +2,7 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"minVersion": "0.8",
|
"minVersion": "0.8",
|
||||||
"package": "io.gitlab.jfronny.resclone.mixin",
|
"package": "io.gitlab.jfronny.resclone.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_8",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"FileResourcePackProviderMixin"
|
"FileResourcePackProviderMixin"
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user