Port to 1.19.4 and libjf-config. Also remove the unused API as mods can still use mixin
ci/woodpecker/push/jfmod Pipeline is pending Details
ci/woodpecker/tag/jfmod Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-03-14 20:58:21 +01:00
parent 79ff7b6279
commit e9dd5644e8
Signed by: Johannes
GPG Key ID: E76429612C2929F4
18 changed files with 151 additions and 219 deletions

View File

@ -6,9 +6,11 @@ plugins {
dependencies {
include(modImplementation("io.gitlab.jfronny.libjf:libjf-base:${prop("libjf_version")}")!!) // for JfCommons
include(modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v1:${prop("libjf_version")}")!!) // for JfCommons
include(modImplementation(fabricApi.module("fabric-resource-loader-v0", prop("fabric_version")))!!)
// Dev env
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny-v1:${prop("libjf_version")}")
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil:${prop("libjf_version")}")
modLocalRuntime("com.terraformersmc:modmenu:${prop("modmenu_version")}")
}

View File

@ -1,6 +1,6 @@
# https://fabricmc.net/develop
minecraft_version=1.19.4-pre1
yarn_mappings=build.6
minecraft_version=1.19.4
yarn_mappings=build.1
loader_version=0.14.17
maven_group=io.gitlab.jfronny
@ -8,6 +8,6 @@ archives_base_name=resclone
modrinth_id=resclone
fabric_version=0.75.1+1.19.4
libjf_version=3.5.0-SNAPSHOT
modmenu_version=6.1.0-beta.3
fabric_version=0.75.3+1.19.4
libjf_version=3.6.0
modmenu_version=6.1.0-rc.1

View File

@ -2,11 +2,10 @@ package io.gitlab.jfronny.resclone;
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.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.fetchers.*;
import io.gitlab.jfronny.resclone.processors.*;
import io.gitlab.jfronny.resclone.util.PackUrlCache;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
@ -20,8 +19,7 @@ import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Resclone implements ModInitializer, RescloneApi {
public static final Set<PackMetaUnloaded> CONF = new LinkedHashSet<>();
public class Resclone implements ModInitializer {
public static final Map<String, PackFetcher> FETCHER_INSTANCES = new LinkedHashMap<>();
public static final Set<PackProcessor> PROCESSORS = new LinkedHashSet<>();
public static final Set<PackMetaLoaded> DOWNLOADED_PACKS = new LinkedHashSet<>();
@ -38,7 +36,6 @@ public class Resclone implements ModInitializer, RescloneApi {
public static PackUrlCache urlCache;
public static int packCount = 0;
private boolean pruneUnused = false;
@Override
public void onInitialize() {
@ -46,75 +43,66 @@ public class Resclone implements ModInitializer, RescloneApi {
GsonHolders.registerSerializer();
urlCache = new PackUrlCache(getConfigPath().resolve("urlCache.properties"));
CONF.clear();
FETCHER_INSTANCES.clear();
PROCESSORS.clear();
DOWNLOADED_PACKS.clear();
addProcessor(new RootPathProcessor()); //This should be run before any other processor to make sure the path is valid
for (RescloneEntry entry : FabricLoader.getInstance().getEntrypoints(MOD_ID, RescloneEntry.class)) {
try {
entry.init(this);
} catch (Exception e) {
Resclone.LOGGER.error("Could not initialize resclone pack supplier", e);
}
}
addFetcher(new BasicFileFetcher());
addFetcher(new GitHubFetcher());
addFetcher(new CurseforgeFetcher());
addFetcher(new ModrinthFetcher());
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) addProcessor(new PruneVanillaProcessor());
addProcessor(new RemoveEmptyProcessor());
reload();
LOGGER.info("Installed {} resource pack{}.", packCount, packCount == 1 ? "" : "s");
}
@Override
public void addFetcher(PackFetcher fetcher) {
FETCHER_INSTANCES.put(fetcher.getSourceTypeName(), fetcher);
}
@Override
public void addProcessor(PackProcessor processor) {
PROCESSORS.add(processor);
}
@Override
public void addPack(String fetcher, String pack, String name) {
addPack(fetcher, pack, name, false);
}
@Override
public void addPack(String fetcher, String pack, String name, boolean forceRedownload) {
addPack(fetcher, pack, name, forceRedownload, false);
}
@Override
public void addPack(String fetcher, String pack, String name, boolean forceRedownload, boolean forceEnable) {
CONF.add(new PackMetaUnloaded(fetcher, pack, name, forceRedownload, forceEnable));
RescloneConfig.packs.add(new PackMetaUnloaded(fetcher, pack, name, forceRedownload, forceEnable));
}
@Override
public void reload() {
Set<PackMetaLoaded> metas = new LinkedHashSet<>();
try {
if (CONF.isEmpty()) {
LOGGER.info("No resclone pack was specified, add one");
}
else {
ExecutorService pool = Executors.newFixedThreadPool(CONF.size());
for (PackMetaUnloaded s : CONF) {
if (RescloneConfig.packs.isEmpty()) {
LOGGER.info("No resclone pack was specified, add one");
}
else {
try {
ExecutorService pool = Executors.newFixedThreadPool(RescloneConfig.packs.size());
for (PackMetaUnloaded s : RescloneConfig.packs) {
pool.submit(generateTask(s, metas));
}
pool.shutdown();
if (!pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS)) {
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();
DOWNLOADED_PACKS.clear();
DOWNLOADED_PACKS.addAll(metas);
if (pruneUnused) pruneCache();
if (RescloneConfig.pruneUnused) pruneCache();
}
private Runnable generateTask(PackMetaUnloaded meta, Set<PackMetaLoaded> metas) {
@ -175,9 +163,8 @@ public class Resclone implements ModInitializer, RescloneApi {
}
}
@Override
public Path getConfigPath() {
Path configPath = FabricLoader.getInstance().getConfigDir().resolve("resclone");
public static Path getConfigPath() {
Path configPath = FabricLoader.getInstance().getConfigDir().resolve(MOD_ID);
if (!Files.isDirectory(configPath.resolve("cache"))) {
try {
Files.createDirectories(configPath.resolve("cache"));
@ -187,9 +174,4 @@ public class Resclone implements ModInitializer, RescloneApi {
}
return configPath;
}
@Override
public void setPruneUnused(boolean pruneUnused) {
this.pruneUnused = pruneUnused;
}
}

View File

@ -0,0 +1,111 @@
package io.gitlab.jfronny.resclone;
import com.google.gson.reflect.TypeToken;
import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolders;
import io.gitlab.jfronny.gson.JsonParseException;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.libjf.config.api.v1.JfCustomConfig;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL;
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
import java.io.*;
import java.lang.reflect.Type;
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;
private static final String PACKS = "packs";
private static final String PRUNE_UNUSED = "pruneUnused";
private static final Type META_SET = new TypeToken<Set<PackMetaUnloaded>>(){}.getType();
private static void load(Path path) throws IOException {
if (!Files.exists(path)) {
packs = new HashSet<>();
pruneUnused = true;
write(path);
return;
}
boolean updateRequired = false;
try (BufferedReader br = Files.newBufferedReader(path);
JsonReader reader = GsonHolders.CONFIG.getGson().newJsonReader(br)) {
if (reader.peek() == JsonToken.BEGIN_ARRAY) {
// Legacy format compatibility
packs = GsonHolders.CONFIG.getGson().fromJson(reader, META_SET);
updateRequired = true;
} else if (reader.peek() == JsonToken.BEGIN_OBJECT) {
// New format
reader.beginObject();
Set<PackMetaUnloaded> packs = null;
Boolean pruneUnused = null;
while (reader.peek() != JsonToken.END_OBJECT) {
final String name = reader.nextName();
switch (name) {
case PACKS -> {
if (packs != null) throw new JsonParseException("Unexpected duplicate \"" + PACKS + "\" in Resclone config");
packs = GsonHolders.CONFIG.getGson().fromJson(reader, META_SET);
}
case PRUNE_UNUSED -> {
if (pruneUnused != null) throw new JsonParseException("Unexpected duplicate \"" + PRUNE_UNUSED + "\" in Resclone config");
pruneUnused = reader.nextBoolean();
}
default -> throw new JsonParseException("Unexpected element: \"" + name + "\" in Resclone config");
}
}
reader.endObject();
if (packs == null) throw new JsonParseException("Expected Resclone config object to contain packs");
if (pruneUnused == null) {
pruneUnused = true;
updateRequired = true;
}
RescloneConfig.packs = packs;
RescloneConfig.pruneUnused = pruneUnused;
} else throw new JsonParseException("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 = GsonHolders.CONFIG.getGson().newJsonWriter(bw)) {
writer.beginObject()
.comment("The packs to be loaded by resclone")
.name(PACKS);
GsonHolders.CONFIG.getGson().toJson(packs, META_SET, writer);
writer.comment("Whether to prune unused packs from the cache")
.name(PRUNE_UNUSED)
.value(pruneUnused)
.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.v1.type.Type.ofClass(META_SET), 100, () -> packs, p -> packs = p)
.value(PRUNE_UNUSED, pruneUnused, () -> pruneUnused, p -> pruneUnused = p)
).load();
}
@Override
public void register(DSL.Defaulted dsl) {
}
}

View File

@ -1,24 +0,0 @@
package io.gitlab.jfronny.resclone;
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.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) {
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);
}
}

View File

@ -1,21 +0,0 @@
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();
void setPruneUnused(boolean pruneUnused);
}

View File

@ -1,5 +0,0 @@
package io.gitlab.jfronny.resclone.api;
public interface RescloneEntry {
void init(RescloneApi api) throws Exception;
}

View File

@ -2,7 +2,6 @@ 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.*;
import java.nio.file.Files;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.resclone.api;
package io.gitlab.jfronny.resclone.fetchers;
import java.nio.file.Path;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.resclone.api;
package io.gitlab.jfronny.resclone.processors;
import java.nio.file.FileSystem;

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.resclone.processors;
import io.gitlab.jfronny.resclone.Resclone;
import io.gitlab.jfronny.resclone.api.PackProcessor;
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
import net.minecraft.server.MinecraftServer;
import org.apache.commons.io.IOUtils;

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.resclone.processors;
import io.gitlab.jfronny.resclone.Resclone;
import io.gitlab.jfronny.resclone.api.PackProcessor;
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
import java.io.IOException;

View File

@ -1,6 +1,5 @@
package io.gitlab.jfronny.resclone.processors;
import io.gitlab.jfronny.resclone.api.PackProcessor;
import io.gitlab.jfronny.resclone.util.io.MoveDirVisitor;
import java.io.IOException;

View File

@ -1,42 +0,0 @@
package io.gitlab.jfronny.resclone.util.config;
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;
import java.io.BufferedReader;
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<>(), true);
}
try (BufferedReader reader = Files.newBufferedReader(configPath)) {
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);
}
if (data.updateRequired()) {
save(api, data.packs(), data.pruneUnused());
}
} catch (IOException e) {
Resclone.LOGGER.error("Could not load config", e);
}
}
public static void save(RescloneApi api, Set<PackMetaUnloaded> packs, boolean pruneUnused) {
try (var writer = Files.newBufferedWriter(api.getConfigPath().resolve("config.json"))) {
GsonHolders.CONFIG.getGson().toJson(new RescloneConfig(packs, pruneUnused), writer);
} catch (IOException e) {
Resclone.LOGGER.error("Could not write config", e);
}
}
}

View File

@ -1,11 +0,0 @@
package io.gitlab.jfronny.resclone.util.config;
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
import java.util.Set;
public record RescloneConfig(Set<PackMetaUnloaded> packs, boolean pruneUnused, boolean updateRequired) {
public RescloneConfig(Set<PackMetaUnloaded> packs, boolean pruneUnused) {
this(packs, pruneUnused, false);
}
}

View File

@ -1,65 +0,0 @@
package io.gitlab.jfronny.resclone.util.config;
import com.google.gson.reflect.TypeToken;
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;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Set;
public class RescloneConfigTypeAdapter extends TypeAdapter<RescloneConfig> {
private static final String PACKS = "packs";
private static final String PRUNE_UNUSED = "pruneUnused";
private static final Type META_SET = new TypeToken<Set<PackMetaUnloaded>>(){}.getType();
@Override
public RescloneConfig read(JsonReader jsonReader) throws IOException {
if (jsonReader.peek() == JsonToken.BEGIN_ARRAY) {
// Legacy format compatibility
return new RescloneConfig(GsonHolders.CONFIG.getGson().fromJson(jsonReader, META_SET), false, true);
} else if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) {
// New format
jsonReader.beginObject();
Set<PackMetaUnloaded> packs = null;
Boolean pruneUnused = null;
boolean updateRequired = false;
while (jsonReader.peek() != JsonToken.END_OBJECT) {
final String name = jsonReader.nextName();
switch (name) {
case PACKS -> {
if (packs != null) throw new JsonParseException("Unexpected duplicate \"" + PACKS + "\" in Resclone config");
packs = GsonHolders.CONFIG.getGson().fromJson(jsonReader, META_SET);
}
case PRUNE_UNUSED -> {
if (pruneUnused != null) throw new JsonParseException("Unexpected duplicate \"" + PRUNE_UNUSED + "\" in Resclone config");
pruneUnused = jsonReader.nextBoolean();
}
default -> throw new JsonParseException("Unexpected element: \"" + name + "\" in Resclone config");
}
}
jsonReader.endObject();
if (packs == null) throw new JsonParseException("Expected Resclone config object to contain packs");
if (pruneUnused == null) {
pruneUnused = true;
updateRequired = true;
}
return new RescloneConfig(packs, pruneUnused, updateRequired);
} else throw new JsonParseException("Expected Resclone config to be an object or array");
}
@Override
public void write(JsonWriter jsonWriter, RescloneConfig rescloneConfig) throws IOException {
jsonWriter.beginObject()
.comment("The packs to be loaded by resclone")
.name(PACKS);
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())
.endObject();
}
}

View File

@ -0,0 +1,7 @@
{
"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"
}

View File

@ -16,7 +16,9 @@
"environment": "*",
"entrypoints": {
"main": ["io.gitlab.jfronny.resclone.Resclone"],
"resclone": ["io.gitlab.jfronny.resclone.RescloneEntryDefault"]
"libjf:config": [
"io.gitlab.jfronny.resclone.RescloneConfig"
]
},
"mixins": [
"resclone.mixins.json",