diff --git a/build.gradle b/build.gradle index 18b5c18..6535965 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,9 @@ dependencies { modImplementation "com.terraformersmc:modmenu:2.0.2" modApi("me.shedaniel.cloth:cloth-config-fabric:5.0.34") - modImplementation("grondag:canvas-mc117-1.17:+") { + + modCompileOnly "grondag:frex-mc117:+" + modRuntime("grondag:canvas-mc117-1.17:+") { exclude(group: "me.shedaniel.cloth") } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java b/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java index 8f856d4..e1be4a1 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java @@ -22,7 +22,6 @@ public class GuiFactory { for (Map.Entry> in : source.getValue().entrySet()) { ConfigEntry entry = in.getValue(); String entryName = ("".equals(namePrefix) ? "" : namePrefix + ".") + in.getKey(); - Respackopts.LOGGER.info(entryName); String translationPrefix = (source.getVersion() < 3 ? "respackopts." + entry.getEntryType() + "." : "rpo.") + screenId; config.accept(entry.buildEntry(entryBuilder, getText(entryName, translationPrefix), diff --git a/src/main/java/io/gitlab/jfronny/respackopts/data/entry/ConfigBooleanEntry.java b/src/main/java/io/gitlab/jfronny/respackopts/data/entry/ConfigBooleanEntry.java index 2589507..47c3e69 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/data/entry/ConfigBooleanEntry.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/data/entry/ConfigBooleanEntry.java @@ -10,6 +10,7 @@ import java.util.function.Supplier; public class ConfigBooleanEntry extends ConfigEntry { public ConfigBooleanEntry(boolean v) { setValue(v); + setDefault(v); } @Override diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/FileOpenProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/FileOpenProvider.java deleted file mode 100644 index b6a4649..0000000 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/FileOpenProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.gitlab.jfronny.respackopts.filters; - -import java.io.IOException; -import java.io.InputStream; - -public interface FileOpenProvider { - InputStream open(String file) throws IOException; -} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/FilterProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/FilterProvider.java deleted file mode 100644 index e466cbb..0000000 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/FilterProvider.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.gitlab.jfronny.respackopts.filters; - -import io.gitlab.jfronny.respackopts.filters.conditions.ResourcePackFilter; -import io.gitlab.jfronny.respackopts.filters.fallback.FallbackFilter; -import net.minecraft.resource.ResourceNotFoundException; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.function.Predicate; - -public class FilterProvider { - ResourcePackFilter rpo; - FallbackFilter fbt; - Predicate containsFileBase; - boolean containsFileWasFallback = false; - public FilterProvider(Predicate containsFileBase, FileOpenProvider openFileBase) { - this.containsFileBase = containsFileBase; - rpo = new ResourcePackFilter(containsFileBase, openFileBase); - fbt = new FallbackFilter(containsFileBase, openFileBase); - } - - public void openFile(String name, File base, CallbackInfoReturnable info) throws IOException { - if (containsFileBase.test(name) && containsFileWasFallback) { - info.setReturnValue(fbt.getReplacement(name, new ResourceNotFoundException(base, name))); - } - } - - public void containsFile(String name, CallbackInfoReturnable info) { - containsFileWasFallback = false; - if (info.getReturnValueZ()) { - if (rpo.fileHidden(name)) { - if (fbt.fileVisible(name)) { - containsFileWasFallback = true; - } else { - info.setReturnValue(false); - } - } - } - else { - if (fbt.fileVisible(name)) { - containsFileWasFallback = true; - info.setReturnValue(true); - } - } - } - - public void findResources(String namespace, CallbackInfoReturnable> info) { - Collection ret = info.getReturnValue(); - ret.removeIf(s -> rpo.fileHidden(s.getPath()) && !fbt.fileVisible(namespace)); - fbt.addFallbackResources(ret, namespace); - } -} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/WrappedPack.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/WrappedPack.java new file mode 100644 index 0000000..8d0d483 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/WrappedPack.java @@ -0,0 +1,123 @@ +package io.gitlab.jfronny.respackopts.filters; + +import io.gitlab.jfronny.respackopts.filters.lambda.FileContainedProvider; +import io.gitlab.jfronny.respackopts.filters.lambda.Tuple; +import io.gitlab.jfronny.respackopts.filters.conditions.ResourcePackFilter; +import io.gitlab.jfronny.respackopts.filters.fallback.FallbackFilter; +import io.gitlab.jfronny.respackopts.filters.lambda.FileOpenProvider; +import io.gitlab.jfronny.respackopts.filters.lambda.PathSplitter; +import net.minecraft.resource.ResourcePack; +import net.minecraft.resource.ResourceType; +import net.minecraft.resource.metadata.ResourceMetadataReader; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.Set; +import java.util.function.Predicate; + +public class WrappedPack implements ResourcePack { + public WrappedPack(ResourcePack pack) { + this.pack = pack; + PathSplitter pathGetter = s -> { + String[] s1 = s.split("/", 3); + if (s1.length != 3) { + throw new IOException("Could not split path string into resource type and ID due to insufficient length: " + s); + } + ResourceType rt = switch (s1[0]) { + case "assets" -> ResourceType.CLIENT_RESOURCES; + case "data" -> ResourceType.SERVER_DATA; + default -> throw new IllegalStateException("Unexpected value for resource type: " + s1[0] + " in: " + s); + }; + return new Tuple<>(rt, new Identifier(s1[1], s1[2])); + }; + FileContainedProvider contains = s -> { + Tuple p = pathGetter.split(s); + return this.pack.contains(p.left(), p.right()); + }; + FileOpenProvider open = s -> { + Tuple p = pathGetter.split(s); + return this.pack.open(p.left(), p.right()); + }; + rpo = new ResourcePackFilter(contains, open); + fbt = new FallbackFilter(contains, open); + } + + ResourcePack pack; + boolean containsFileWasFallback = false; + ResourcePackFilter rpo; + FallbackFilter fbt; + + @Nullable + @Override + public InputStream openRoot(String fileName) throws IOException { + return pack.openRoot(fileName); + } + + @Override + public InputStream open(ResourceType type, Identifier id) throws IOException { + if (pack.contains(type, id) && containsFileWasFallback) { + return fbt.getReplacement(getFilename(type, id), new FileNotFoundException()); + } + return pack.open(type, id); + } + + @Override + public Collection findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate pathFilter) { + Collection ret = pack.findResources(type, namespace, prefix, maxDepth, pathFilter); + ret.removeIf(s -> rpo.fileHidden(s.getPath()) && !fbt.fileVisible(namespace)); + fbt.addFallbackResources(ret, namespace); + return ret; + } + + @Override + public boolean contains(ResourceType type, Identifier id) { + containsFileWasFallback = false; + String name = getFilename(type, id); + if (pack.contains(type, id)) { + if (rpo.fileHidden(name)) { + if (fbt.fileVisible(name)) { + containsFileWasFallback = true; + } else { + return false; + } + } + return true; + } + else { + if (pack.contains(type, new Identifier(id.getNamespace(), id.getPath() + ".rpo")) && fbt.fileVisible(name)) { // only try to check fbt if .rpo exists + containsFileWasFallback = true; + return true; + } + return false; + } + } + + @Override + public Set getNamespaces(ResourceType type) { + return pack.getNamespaces(type); + } + + @Nullable + @Override + public T parseMetadata(ResourceMetadataReader metaReader) throws IOException { + return pack.parseMetadata(metaReader); + } + + @Override + public String getName() { + return pack.getName(); + } + + @Override + public void close() { + pack.close(); + } + + private static String getFilename(ResourceType type, Identifier id) { + return String.format("%s/%s/%s", type.getDirectory(), id.getNamespace(), id.getPath()); + } +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/conditions/ResourcePackFilter.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/conditions/ResourcePackFilter.java index 5a3cf6b..f342633 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/conditions/ResourcePackFilter.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/conditions/ResourcePackFilter.java @@ -2,17 +2,18 @@ package io.gitlab.jfronny.respackopts.filters.conditions; import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.data.Rpo; -import io.gitlab.jfronny.respackopts.filters.FileOpenProvider; +import io.gitlab.jfronny.respackopts.filters.lambda.FileContainedProvider; +import io.gitlab.jfronny.respackopts.filters.lambda.FileOpenProvider; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; -import java.util.function.Predicate; public class ResourcePackFilter { - Predicate containsFileBase; + FileContainedProvider containsFileBase; FileOpenProvider openFileBase; - public ResourcePackFilter(Predicate containsFileBase, FileOpenProvider openFileBase) { + public ResourcePackFilter(FileContainedProvider containsFileBase, FileOpenProvider openFileBase) { this.containsFileBase = containsFileBase; this.openFileBase = openFileBase; } @@ -20,8 +21,13 @@ public class ResourcePackFilter { public boolean fileHidden(String name) { if (name.endsWith(Respackopts.FILE_EXTENSION)) return false; - if (!containsFileBase.test(name + Respackopts.FILE_EXTENSION)) + try { + if (!containsFileBase.contains(name + Respackopts.FILE_EXTENSION)) + return false; + } catch (IOException e) { + Respackopts.LOGGER.error(e); return false; + } try (InputStream stream = openFileBase.open(name + Respackopts.FILE_EXTENSION); Reader w = new InputStreamReader(stream)) { Rpo rpo = Respackopts.GSON.fromJson(w, Rpo.class); if (rpo.conditions == null) diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/fallback/FallbackFilter.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/fallback/FallbackFilter.java index e746210..e5c4a37 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/fallback/FallbackFilter.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/fallback/FallbackFilter.java @@ -2,8 +2,8 @@ package io.gitlab.jfronny.respackopts.filters.fallback; import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.data.Rpo; -import io.gitlab.jfronny.respackopts.filters.FileOpenProvider; -import net.minecraft.resource.ResourceNotFoundException; +import io.gitlab.jfronny.respackopts.filters.lambda.FileContainedProvider; +import io.gitlab.jfronny.respackopts.filters.lambda.FileOpenProvider; import net.minecraft.util.Identifier; import java.io.IOException; @@ -12,12 +12,11 @@ import java.io.InputStreamReader; import java.io.Reader; import java.util.Collection; import java.util.List; -import java.util.function.Predicate; public class FallbackFilter { - Predicate containsFileBase; + FileContainedProvider containsFileBase; FileOpenProvider openFileBase; - public FallbackFilter(Predicate containsFileBase, FileOpenProvider openFileBase) { + public FallbackFilter(FileContainedProvider containsFileBase, FileOpenProvider openFileBase) { this.containsFileBase = containsFileBase; this.openFileBase = openFileBase; } @@ -26,32 +25,32 @@ public class FallbackFilter { if (name.endsWith(Respackopts.FILE_EXTENSION)) return false; String fbt = name + Respackopts.FILE_EXTENSION; - if (containsFileBase.test(fbt)) { - try (InputStream stream = openFileBase.open(fbt); Reader w = new InputStreamReader(stream)) { + try (InputStream stream = openFileBase.open(fbt); Reader w = new InputStreamReader(stream)) { + if (containsFileBase.contains(fbt)) { Rpo rpo = Respackopts.GSON.fromJson(w, Rpo.class); if (rpo.fallbacks != null) { List arr = rpo.fallbacks; for (String s : arr) { - if (containsFileBase.test(s)) + if (containsFileBase.contains(s)) return true; } } } - catch (IOException e) { - Respackopts.LOGGER.error("Could not determine visibility of " + name + e); - } + } + catch (IOException e) { + Respackopts.LOGGER.error("Could not determine visibility of " + name, e); } return false; } - public InputStream getReplacement(String name, ResourceNotFoundException ex) throws ResourceNotFoundException { + public InputStream getReplacement(String name, IOException ex) throws IOException { String fbt = name + Respackopts.FILE_EXTENSION; try (InputStream stream = openFileBase.open(fbt); Reader w = new InputStreamReader(stream)) { Rpo rpo = Respackopts.GSON.fromJson(w, Rpo.class); if (rpo.fallbacks != null) { List arr = rpo.fallbacks; for (String s : arr) { - if (containsFileBase.test(s)) + if (containsFileBase.contains(s)) return openFileBase.open(s); } } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/FileContainedProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/FileContainedProvider.java new file mode 100644 index 0000000..f8b0d68 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/FileContainedProvider.java @@ -0,0 +1,7 @@ +package io.gitlab.jfronny.respackopts.filters.lambda; + +import java.io.IOException; + +public interface FileContainedProvider { + boolean contains(String name) throws IOException; +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/FileOpenProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/FileOpenProvider.java new file mode 100644 index 0000000..259e049 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/FileOpenProvider.java @@ -0,0 +1,8 @@ +package io.gitlab.jfronny.respackopts.filters.lambda; + +import java.io.IOException; +import java.io.InputStream; + +public interface FileOpenProvider { + InputStream open(String name) throws IOException; +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/PathSplitter.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/PathSplitter.java new file mode 100644 index 0000000..d7d30fe --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/PathSplitter.java @@ -0,0 +1,10 @@ +package io.gitlab.jfronny.respackopts.filters.lambda; + +import net.minecraft.resource.ResourceType; +import net.minecraft.util.Identifier; + +import java.io.IOException; + +public interface PathSplitter { + Tuple split(String name) throws IOException; +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/Tuple.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/Tuple.java new file mode 100644 index 0000000..1d75532 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/lambda/Tuple.java @@ -0,0 +1,4 @@ +package io.gitlab.jfronny.respackopts.filters.lambda; + +public record Tuple(T1 left, T2 right) { +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ReloadableResourceManagerImplMixin.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ReloadableResourceManagerImplMixin.java new file mode 100644 index 0000000..1b8fefb --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ReloadableResourceManagerImplMixin.java @@ -0,0 +1,20 @@ +package io.gitlab.jfronny.respackopts.mixin; + +import io.gitlab.jfronny.respackopts.filters.WrappedPack; +import net.minecraft.resource.AbstractFileResourcePack; +import net.minecraft.resource.ReloadableResourceManagerImpl; +import net.minecraft.resource.ResourcePack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(ReloadableResourceManagerImpl.class) +public class ReloadableResourceManagerImplMixin { + @ModifyVariable(method = "addPack(Lnet/minecraft/resource/ResourcePack;)V", at = @At("HEAD"), argsOnly = true, ordinal = 0) + private ResourcePack modifyPack(ResourcePack pack) { + if (pack instanceof AbstractFileResourcePack) { + return new WrappedPack(pack); + } + return pack; + } +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/conditions/DirectoryResourcePackMixin.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/conditions/DirectoryResourcePackMixin.java deleted file mode 100644 index c89296e..0000000 --- a/src/main/java/io/gitlab/jfronny/respackopts/mixin/conditions/DirectoryResourcePackMixin.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.gitlab.jfronny.respackopts.mixin.conditions; - -import io.gitlab.jfronny.respackopts.filters.FilterProvider; -import net.minecraft.resource.AbstractFileResourcePack; -import net.minecraft.resource.DirectoryResourcePack; -import net.minecraft.resource.ResourceType; -import net.minecraft.util.Identifier; -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.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.Set; -import java.util.function.Predicate; - -@Mixin(DirectoryResourcePack.class) -public abstract class DirectoryResourcePackMixin extends AbstractFileResourcePack { - public DirectoryResourcePackMixin(File base) { - super(base); - } - - @Shadow protected abstract boolean containsFile(String name); - - @Shadow public abstract Set getNamespaces(ResourceType type); - - @Shadow protected abstract InputStream openFile(String name) throws IOException; - - FilterProvider rpo$filter = new FilterProvider(this::containsFile, this::openFile); - - @Inject(at = @At("HEAD"), method = "openFile(Ljava/lang/String;)Ljava/io/InputStream;", cancellable = true) - protected void openFile(String name, CallbackInfoReturnable info) throws IOException { - rpo$filter.openFile(name, base, info); - } - - @Inject(at = @At("TAIL"), method = "containsFile(Ljava/lang/String;)Z", cancellable = true) - protected void containsFile(String name, CallbackInfoReturnable info) { - rpo$filter.containsFile(name, info); - } - - @Inject(at = @At("TAIL"), method = "findResources(Lnet/minecraft/resource/ResourceType;Ljava/lang/String;Ljava/lang/String;ILjava/util/function/Predicate;)Ljava/util/Collection;") - private void findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate pathFilter, CallbackInfoReturnable> info) { - rpo$filter.findResources(namespace, info); - } -} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/conditions/ZipResourcePackMixin.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/conditions/ZipResourcePackMixin.java deleted file mode 100644 index fe23030..0000000 --- a/src/main/java/io/gitlab/jfronny/respackopts/mixin/conditions/ZipResourcePackMixin.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.gitlab.jfronny.respackopts.mixin.conditions; - -import io.gitlab.jfronny.respackopts.filters.FilterProvider; -import net.minecraft.resource.AbstractFileResourcePack; -import net.minecraft.resource.ResourceType; -import net.minecraft.resource.ZipResourcePack; -import net.minecraft.util.Identifier; -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.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.Set; -import java.util.function.Predicate; - -@Mixin(ZipResourcePack.class) -public abstract class ZipResourcePackMixin extends AbstractFileResourcePack { - public ZipResourcePackMixin(File base) { - super(base); - } - - @Shadow public abstract boolean containsFile(String name); - - @Shadow public abstract Set getNamespaces(ResourceType type); - - @Shadow protected abstract InputStream openFile(String name) throws IOException; - - FilterProvider rpo$filter = new FilterProvider(this::containsFile, this::openFile); - - @Inject(at = @At("HEAD"), method = "openFile(Ljava/lang/String;)Ljava/io/InputStream;", cancellable = true) - protected void openFile(String name, CallbackInfoReturnable info) throws IOException { - rpo$filter.openFile(name, base, info); - } - - @Inject(at = @At("TAIL"), method = "containsFile(Ljava/lang/String;)Z", cancellable = true) - protected void containsFile(String name, CallbackInfoReturnable info) { - rpo$filter.containsFile(name, info); - } - - @Inject(at = @At("TAIL"), method = "findResources(Lnet/minecraft/resource/ResourceType;Ljava/lang/String;Ljava/lang/String;ILjava/util/function/Predicate;)Ljava/util/Collection;") - private void findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate pathFilter, CallbackInfoReturnable> info) { - rpo$filter.findResources(namespace, info); - } -} diff --git a/src/main/resources/respackopts.mixins.json b/src/main/resources/respackopts.mixins.json index 8d43651..ef83d74 100644 --- a/src/main/resources/respackopts.mixins.json +++ b/src/main/resources/respackopts.mixins.json @@ -4,11 +4,10 @@ "package": "io.gitlab.jfronny.respackopts.mixin", "compatibilityLevel": "JAVA_8", "mixins": [ - "conditions.DirectoryResourcePackMixin", - "conditions.ZipResourcePackMixin" ], "client": [ "OptionsScreenMixin", + "ReloadableResourceManagerImplMixin", "ResourcePackEntryMixin", "ResourcePackManagerMixin" ],