diff --git a/build.gradle.kts b/build.gradle.kts index f89857e..c9bd3bf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,10 @@ plugins { allprojects { group = "io.gitlab.jfronny" } base.archivesName = "respackopts" +loom { + accessWidenerPath.set(file("src/main/resources/respackopts.accesswidener")) +} + repositories { maven("https://maven.vram.io/") { name = "FREX/Canvas" @@ -21,13 +25,14 @@ repositories { } } -val fabricVersion = "0.87.0+1.20.1" +val fabricVersion = "0.87.1+1.20.2" val muscriptVersion = "1.4-SNAPSHOT" +val modmenuVersion = "7.2.1" jfMod { - minecraftVersion = "1.20.1" - yarn("build.10") + minecraftVersion = "23w33a" + yarn("build.7") loaderVersion = "0.14.22" - libJfVersion = "3.12.4" + libJfVersion = "3.13.0-SNAPSHOT" modrinth { projectId = "respackopts" @@ -44,6 +49,7 @@ jfMod { dependencies { modImplementation("net.fabricmc.fabric-api:fabric-api:$fabricVersion") include(modImplementation("io.gitlab.jfronny:muscript:$muscriptVersion")!!) + include(modImplementation("io.gitlab.jfronny:muscript-gson:$muscriptVersion")!!) modImplementation("io.gitlab.jfronny.libjf:libjf-data-manipulation-v0:${jfMod.libJfVersion.get()}") modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v1:${jfMod.libJfVersion.get()}") modImplementation("io.gitlab.jfronny.libjf:libjf-config-ui-tiny-v1:${jfMod.libJfVersion.get()}") @@ -52,8 +58,8 @@ dependencies { exclude("net.fabricmc") // required to work around duplicate fabric loaders } modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil:${jfMod.libJfVersion.get()}", nofabric) - modLocalRuntime("com.terraformersmc:modmenu:7.2.1", nofabric) - modClientCompileOnly("com.terraformersmc:modmenu:7.2.1", nofabric) + //modLocalRuntime("com.terraformersmc:modmenu:$modmenuVersion", nofabric) // Temporarily disabled since modmenu doesn't support snapshots + modClientCompileOnly("com.terraformersmc:modmenu:$modmenuVersion", nofabric) modClientCompileOnly("io.vram:frex-fabric:20.0.+") modClientCompileOnly("dev.notalpha:dashloader:5.0.0-beta.1+1.20.0") diff --git a/docs/additional/Migrations.md b/docs/additional/Migrations.md index e2a4c95..b32ed26 100644 --- a/docs/additional/Migrations.md +++ b/docs/additional/Migrations.md @@ -88,4 +88,9 @@ Corresponds to version 4.5.0-4.5.1 ## v12 Corresponds to version 4.6.0 -- Support for whole numbers (not integers!) \ No newline at end of file +- Support for whole numbers (not integers!) + +## v13 +Unreleased + +- Supports readString and runScript methods in muScript expressions \ No newline at end of file diff --git a/docs/setup/Debugging.md b/docs/setup/Debugging.md index c2367f0..6dce39c 100644 --- a/docs/setup/Debugging.md +++ b/docs/setup/Debugging.md @@ -1,6 +1,6 @@ # Debugging If you run into issues with respackopts and cannot find a solution here, -you may contact me through my discord server (look at the mod page) +you may contact me directly (details are at the bottom of this page) ## Look at the config file Respackopts stores configurations next to their corresponding resource packs (`some pack.zip.rpo` usually) diff --git a/src/client/java/io/gitlab/jfronny/respackopts/RpoClientCommand.java b/src/client/java/io/gitlab/jfronny/respackopts/RpoClientCommand.java index 1e50539..c419ed4 100644 --- a/src/client/java/io/gitlab/jfronny/respackopts/RpoClientCommand.java +++ b/src/client/java/io/gitlab/jfronny/respackopts/RpoClientCommand.java @@ -1,6 +1,7 @@ package io.gitlab.jfronny.respackopts; import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import io.gitlab.jfronny.commons.throwable.ThrowingConsumer; import io.gitlab.jfronny.commons.throwable.ThrowingSupplier; @@ -40,7 +41,12 @@ public class RpoClientCommand { return 1; }; Command dumpScope = ctx -> { - ctx.getSource().sendFeedback(dump(MetaCache.getScope(null).toExpr().toString(), "_root.mu")); + ctx.getSource().sendFeedback(dump(MetaCache.getScope(Respackopts.META_VERSION).toExpr().toString(), "_root.mu")); + MetaCache.forEach((id, branch) -> ctx.getSource().sendFeedback(dump(branch.executionScope().getOverrides().toExpr().toString(), branch.packId() + ".mu"))); + return 1; + }; + Command dumpScopeVersioned = ctx -> { + ctx.getSource().sendFeedback(dump(MetaCache.getScope(IntegerArgumentType.getInteger(ctx, "version")).toExpr().toString(), "_root.mu")); MetaCache.forEach((id, branch) -> ctx.getSource().sendFeedback(dump(branch.executionScope().getOverrides().toExpr().toString(), branch.packId() + ".mu"))); return 1; }; @@ -52,7 +58,7 @@ public class RpoClientCommand { Command execute = ctx -> { String snippet = StringArgumentType.getString(ctx, "snippet"); try { - String result = Parser.parse(snippet, "snippet").asStringExpr().get(MetaCache.getScope(null)); + String result = Parser.parse(snippet, "snippet").asStringExpr().get(MetaCache.getScope(IntegerArgumentType.getInteger(ctx, "version"))); ctx.getSource().sendFeedback(Text.translatable("respackopts.snippet.success", result)); } catch (LocationalException | Parser.ParseException e) { Respackopts.LOGGER.error("Could not execute snippet", e); @@ -75,10 +81,10 @@ public class RpoClientCommand { dispatcher.register(literal("rpoc").executes(getVersion) .then(literal("dump").executes(dumpConfig) .then(literal("config").executes(dumpConfig)) - .then(literal("scope").executes(dumpScope)) + .then(literal("scope").executes(dumpScope).then(argument("version", IntegerArgumentType.integer(1, Respackopts.META_VERSION)).executes(dumpScopeVersioned))) .then(literal("glsl").executes(dumpGlsl)) .then(literal("asset").then(argument("asset", IdentifierArgumentType.identifier()).executes(dumpAsset)))) - .then(literal("execute").then(argument("snippet", StringArgumentType.greedyString()).executes(execute))) + .then(literal("execute").then(argument("version", IntegerArgumentType.integer(1, Respackopts.META_VERSION)).then(argument("snippet", StringArgumentType.greedyString()).executes(execute)))) .then(literal("version").executes(getVersion)) .then(literal("reload").executes(reload))); }); diff --git a/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java b/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java index 82d8858..fe7bd1a 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java @@ -43,23 +43,6 @@ public class Respackopts implements ModInitializer, SaveHook { .setPrettyPrinting() .create(); - public static final Scope ROOT_SCOPE = StandardLib.createScope(); - - static { - ROOT_SCOPE.set("version", args -> { - if (args.size() != 2) throw new IllegalArgumentException("Expected 2 arguments on version but got " + args.size()); - VersionPredicate predicate; - try { - predicate = VersionPredicate.parse(args.get(1).asString().getValue()); - } catch (VersionParsingException e) { - throw new IllegalArgumentException("Could not parse version predicate", e); - } - return DFinal.of(FabricLoader.getInstance().getModContainer(args.get(0).asString().getValue()) - .map(c -> predicate.test(c.getMetadata().getVersion())) - .orElse(false)); - }); - } - public static final String ID = "respackopts"; public static final Logger LOGGER = Logger.forName(ID); diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/DirFilterEvents.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/DirFilterEvents.java index 5aab8c7..27f556b 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/DirFilterEvents.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/DirFilterEvents.java @@ -6,8 +6,10 @@ import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.filters.util.DirRpoResult; import io.gitlab.jfronny.respackopts.gson.AttachmentHolder; import io.gitlab.jfronny.respackopts.model.DirRpo; +import io.gitlab.jfronny.respackopts.model.cache.CacheKey; import io.gitlab.jfronny.respackopts.model.cache.CachedPackState; import io.gitlab.jfronny.respackopts.model.enums.PackCapability; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import io.gitlab.jfronny.respackopts.util.MetaCache; import net.minecraft.resource.*; import net.minecraft.util.Identifier; @@ -28,14 +30,15 @@ public enum DirFilterEvents implements UserResourceEvents.Open, UserResourceEven if (!MetaCache.hasCapability(pack, PackCapability.DirFilter)) return previous; String path = new ResourcePath(type, id).getName(); List rpo = findDirRpos(pack, parent(path)); + CacheKey key = MetaCache.getKeyByPack(pack); + RespackoptsFS fs = new RespackoptsFS(pack); //TODO use pattern matching for switch - DirRpoResult result = DirRpoResult.compute(path, rpo, MetaCache.getKeyByPack(pack)); + DirRpoResult result = DirRpoResult.compute(path, rpo, key, fs); if (result == DirRpoResult.ORIGINAL) return previous; // Using original file if (result == DirRpoResult.IGNORE) return null; // No fallback // Use fallback DirRpoResult.Replacement replacement = (DirRpoResult.Replacement) result; - ResourcePath rp = new ResourcePath(replacement.toFallback(path)); - return pack.open(rp.getType(), rp.getId()); + return fs.open(replacement.toFallback(path)); } @Override @@ -46,11 +49,13 @@ public enum DirFilterEvents implements UserResourceEvents.Open, UserResourceEven boolean dirFilterAdditive = MetaCache.hasCapability(pack, PackCapability.DirFilterAdditive); String searchPrefix = type.getDirectory() + "/" + namespace + "/" + prefix; Set additionalSearched = new HashSet<>(); + CacheKey key = MetaCache.getKeyByPack(pack); + RespackoptsFS fs = new RespackoptsFS(pack); return (identifier, value) -> { String path = path(type, identifier); //TODO use pattern matching for switch List relevantRpos = findDirRpos(pack, parent(path)); - DirRpoResult result = DirRpoResult.compute(path, relevantRpos, MetaCache.getKeyByPack(pack)); + DirRpoResult result = DirRpoResult.compute(path, relevantRpos, key, fs); if (result == DirRpoResult.ORIGINAL) { // Using original file previous.accept(identifier, value); return; @@ -65,8 +70,7 @@ public enum DirFilterEvents implements UserResourceEvents.Open, UserResourceEven } if (!dirFilterAdditive) { // Only return this single result, don't search for others - ResourcePath rp = new ResourcePath(newPath); - previous.accept(identifier, pack.open(rp.getType(), rp.getId())); + previous.accept(identifier, fs.open(newPath)); return; } // Find other files in the fallback directory @@ -110,7 +114,9 @@ public enum DirFilterEvents implements UserResourceEvents.Open, UserResourceEven private List findDirRpos(ResourcePack pack, String path) { if (path == null) return List.of(); - CachedPackState state = MetaCache.getState(MetaCache.getKeyByPack(pack)); + CacheKey key = MetaCache.getKeyByPack(pack); + RespackoptsFS fs = new RespackoptsFS(pack); + CachedPackState state = MetaCache.getState(key); var cache = state.cachedDirRPOs(); { @@ -122,13 +128,8 @@ public enum DirFilterEvents implements UserResourceEvents.Open, UserResourceEven List parentRPOs = findDirRpos(pack, parent(path)); synchronized (cache) { // This is synchronized as multiple resources might be accessed at the same time, potentially causing a CME here return cache.computeIfAbsent(path, $ -> { - ResourcePath rp; - try { - rp = new ResourcePath(path + "/" + Respackopts.FILE_EXTENSION); - } catch (Exception e) { - return parentRPOs; - } - InputSupplier is = UserResourceEvents.disable(() -> pack.open(rp.getType(), rp.getId())); + String rp = path + "/" + Respackopts.FILE_EXTENSION; + InputSupplier is = UserResourceEvents.disable(() -> fs.open(rp)); if (is == null) return parentRPOs; try (Reader w = new InputStreamReader(is.get())) { List currentRPOs = new LinkedList<>(parentRPOs); @@ -139,7 +140,7 @@ public enum DirFilterEvents implements UserResourceEvents.Open, UserResourceEven currentRPOs.add(newRPO); return currentRPOs; } catch (IOException e) { - Respackopts.LOGGER.error("Couldn't open dir rpo " + rp.getName(), e); + Respackopts.LOGGER.error("Couldn't open dir rpo " + rp, e); } return parentRPOs; }); diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/FileFilterEvents.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/FileFilterEvents.java index 0ea4ec0..8324f72 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/FileFilterEvents.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/FileFilterEvents.java @@ -4,7 +4,9 @@ import io.gitlab.jfronny.libjf.ResourcePath; import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents; import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.filters.util.*; +import io.gitlab.jfronny.respackopts.model.cache.CacheKey; import io.gitlab.jfronny.respackopts.model.enums.PackCapability; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import io.gitlab.jfronny.respackopts.util.MetaCache; import net.minecraft.resource.*; import net.minecraft.util.Identifier; @@ -25,22 +27,22 @@ public enum FileFilterEvents implements UserResourceEvents.OpenRoot, UserResourc @Override public InputSupplier openRoot(String[] fileName, InputSupplier previous, ResourcePack pack) { if (skip(pack)) return previous; - String path = String.join("/", fileName); - return switch (probe(previous != null, pack, path)) { - case MISSING -> null; - case FALLBACK -> FileFallbackProvider.getReplacement(pack, path); - case CONTAINS -> FileExpansionProvider.replace(previous, pack, path); - }; + return open(previous, pack, String.join("/", fileName)); } @Override public InputSupplier open(ResourceType type, Identifier id, InputSupplier previous, ResourcePack pack) { if (skip(pack)) return previous; - String name = new ResourcePath(type, id).getName(); - return switch (probe(previous != null, pack, name)) { + return open(previous, pack, new ResourcePath(type, id).getName()); + } + + private InputSupplier open(InputSupplier previous, ResourcePack pack, String name) { + CacheKey key = MetaCache.getKeyByPack(pack); + RespackoptsFS fs = new RespackoptsFS(pack); + return switch (probe(previous != null, key, fs, name)) { case MISSING -> null; - case FALLBACK -> FileFallbackProvider.getReplacement(pack, name); - case CONTAINS -> FileExpansionProvider.replace(previous, pack, name); + case FALLBACK -> FileFallbackProvider.getReplacement(key, fs, name); + case CONTAINS -> FileExpansionProvider.replace(previous, key, fs, name); }; } @@ -50,30 +52,28 @@ public enum FileFilterEvents implements UserResourceEvents.OpenRoot, UserResourc // Therefore, it needs to be added when calling a method that generates a ResourcePath! if (skip(pack)) return previous; Set ids = new HashSet<>(); + CacheKey key = MetaCache.getKeyByPack(pack); + RespackoptsFS fs = new RespackoptsFS(pack); return (identifier, value) -> { // Completion of the path is handled separately here String fileName = type.getDirectory() + "/" + identifier.getNamespace() + "/" + identifier.getPath(); - if (!FileExclusionProvider.fileHidden(pack, fileName) || FileFallbackProvider.fileHasFallback(pack, fileName)) { + if (!FileExclusionProvider.fileHidden(key, fs, fileName) || FileFallbackProvider.fileHasFallback(key, fs, fileName)) { ids.add(identifier); previous.accept(identifier, open(type, identifier, value, pack)); } - FileFallbackProvider.addFallbackResources(namespace, type, identifier, pack, ids, previous); + FileFallbackProvider.addFallbackResources(namespace, type, identifier, key, fs, ids, previous); }; } - private ContainsResult probe(boolean previous, ResourcePack pack, String name) { + private ContainsResult probe(boolean previous, CacheKey key, RespackoptsFS fs, String name) { if (previous) { - if (!FileExclusionProvider.fileHidden(pack, name)) return ContainsResult.CONTAINS; - else if (FileFallbackProvider.fileHasFallback(pack, name)) return ContainsResult.FALLBACK; + if (!FileExclusionProvider.fileHidden(key, fs, name)) return ContainsResult.CONTAINS; + else if (FileFallbackProvider.fileHasFallback(key, fs, name)) return ContainsResult.FALLBACK; else return ContainsResult.MISSING; } else { - boolean missing; - if (name.contains("/")) { - ResourcePath rp = new ResourcePath(name + Respackopts.FILE_EXTENSION); - missing = UserResourceEvents.disable(() -> pack.open(rp.getType(), rp.getId())) == null; - } else missing = UserResourceEvents.disable(() -> pack.openRoot(name + Respackopts.FILE_EXTENSION)) == null; - if (missing && FileFallbackProvider.fileHasFallback(pack, name)) return ContainsResult.FALLBACK; + boolean missing = UserResourceEvents.disable(() -> fs.open(name + Respackopts.FILE_EXTENSION)) == null; + if (missing && FileFallbackProvider.fileHasFallback(key, fs, name)) return ContainsResult.FALLBACK; else return ContainsResult.MISSING; } } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/DirRpoResult.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/DirRpoResult.java index 5dd2f77..8736f3a 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/DirRpoResult.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/DirRpoResult.java @@ -7,6 +7,7 @@ import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.model.Condition; import io.gitlab.jfronny.respackopts.model.DirRpo; import io.gitlab.jfronny.respackopts.model.cache.CacheKey; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import io.gitlab.jfronny.respackopts.util.MetaCache; import java.util.List; @@ -20,10 +21,10 @@ public sealed interface DirRpoResult { return new Replacement(originalPrefix, fallbackPrefix, version); } - static DirRpoResult compute(String file, List rpos, CacheKey key) { - int version = MetaCache.getState(key).metadata().version; + static DirRpoResult compute(String file, List rpos, CacheKey key, RespackoptsFS fs) { + int version = MetaCache.getMeta(key).version; if (version < 11) rpos = rpos.isEmpty() ? rpos : List.of(rpos.get(rpos.size() - 1)); - LazySupplier scope = new LazySupplier<>(() -> MetaCache.getScope(key)); + LazySupplier scope = new LazySupplier<>(() -> MetaCache.getScope(key, fs)); for (DirRpo rpo : rpos) { if (rpo.condition == null) continue; try { diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExclusionProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExclusionProvider.java index cef5b95..75a8276 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExclusionProvider.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExclusionProvider.java @@ -4,17 +4,17 @@ import io.gitlab.jfronny.muscript.debug.ObjectGraphPrinter; import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.model.Condition; import io.gitlab.jfronny.respackopts.model.cache.CacheKey; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import io.gitlab.jfronny.respackopts.util.MetaCache; import net.minecraft.resource.ResourcePack; public class FileExclusionProvider { - public static boolean fileHidden(ResourcePack pack, String file) { - return FileRpoSearchProvider.modifyWithRpo(file, pack, rpo -> { + public static boolean fileHidden(CacheKey key, RespackoptsFS fs, String file) { + return FileRpoSearchProvider.modifyWithRpo(file, key, fs, rpo -> { if (rpo.condition == null) return false; - CacheKey key = MetaCache.getKeyByPack(pack); try { - return !rpo.condition.get(MetaCache.getScope(key)); + return !rpo.condition.get(MetaCache.getScope(key, fs)); } catch (Condition.ConditionException e) { String res = "Could not evaluate condition for " + file + " (pack: " + key.packName() + ")"; try { diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExpansionProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExpansionProvider.java index 46c6107..e281f30 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExpansionProvider.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileExpansionProvider.java @@ -2,9 +2,10 @@ package io.gitlab.jfronny.respackopts.filters.util; import io.gitlab.jfronny.muscript.ast.StringExpr; import io.gitlab.jfronny.muscript.data.dynamic.Dynamic; +import io.gitlab.jfronny.respackopts.model.cache.CacheKey; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import io.gitlab.jfronny.respackopts.util.MetaCache; import net.minecraft.resource.InputSupplier; -import net.minecraft.resource.ResourcePack; import java.io.*; import java.util.Map; @@ -18,10 +19,10 @@ public class FileExpansionProvider { return new ByteArrayInputStream(s.getBytes()); } - public static InputSupplier replace(InputSupplier inputStream, ResourcePack pack, String file) { - return FileRpoSearchProvider.modifyWithRpo(file, pack, rpo -> rpo.expansions == null || rpo.expansions.isEmpty() ? inputStream : () -> { + public static InputSupplier replace(InputSupplier inputStream, CacheKey key, RespackoptsFS fs, String file) { + return FileRpoSearchProvider.modifyWithRpo(file, key, fs, rpo -> rpo.expansions == null || rpo.expansions.isEmpty() ? inputStream : () -> { try (InputStream is = inputStream.get()) { - return replace(MetaCache.getScope(MetaCache.getKeyByPack(pack)), is, rpo.expansions); + return replace(MetaCache.getScope(key, fs), is, rpo.expansions); } }, inputStream); } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java index d779c8c..4350bba 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java @@ -1,7 +1,8 @@ package io.gitlab.jfronny.respackopts.filters.util; -import io.gitlab.jfronny.libjf.ResourcePath; import io.gitlab.jfronny.respackopts.Respackopts; +import io.gitlab.jfronny.respackopts.model.cache.CacheKey; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import net.minecraft.resource.*; import net.minecraft.util.Identifier; @@ -9,25 +10,23 @@ import java.io.InputStream; import java.util.Set; public class FileFallbackProvider { - public static boolean fileHasFallback(ResourcePack pack, String file) { - return FileRpoSearchProvider.modifyWithRpo(file, pack, rpo -> { + public static boolean fileHasFallback(CacheKey key, RespackoptsFS fs, String file) { + return FileRpoSearchProvider.modifyWithRpo(file, key, fs, rpo -> { if (rpo.fallbacks != null) { for (String s : rpo.fallbacks) { - ResourcePath tmp = new ResourcePath(s); - if (pack.open(tmp.getType(), tmp.getId()) != null) return true; + if (fs.open(s) != null) return true; } } return false; }, false); } - public static InputSupplier getReplacement(ResourcePack pack, String file) { - return FileRpoSearchProvider.modifyWithRpo(file, pack, rpo -> { + public static InputSupplier getReplacement(CacheKey key, RespackoptsFS fs, String file) { + return FileRpoSearchProvider.modifyWithRpo(file, key, fs, rpo -> { try { if (rpo.fallbacks != null) { for (String s : rpo.fallbacks) { - ResourcePath tmp = new ResourcePath(s); - InputSupplier is = pack.open(tmp.getType(), tmp.getId()); + InputSupplier is = fs.open(s); if (is != null) return is; } } @@ -40,15 +39,15 @@ public class FileFallbackProvider { }, null); } - public static void addFallbackResources(String namespace, ResourceType type, Identifier identifier, ResourcePack pack, Set ids, ResourcePack.ResultConsumer out) { + public static void addFallbackResources(String namespace, ResourceType type, Identifier identifier, CacheKey key, RespackoptsFS fs, Set ids, ResourcePack.ResultConsumer out) { // Warning: the Identifiers here DON'T CONTAIN THE TYPE! Therefore, it needs to be added when calling a method that generates a ResourcePath! String path = identifier.getPath(); if (FileRpoSearchProvider.isRpo(path)) { String expectedTarget = path.substring(0, path.length() - Respackopts.FILE_EXTENSION.length()); - if (ids.stream().noneMatch(s -> s.getPath().equals(expectedTarget)) && fileHasFallback(pack, type.getDirectory() + "/" + expectedTarget)) { + if (ids.stream().noneMatch(s -> s.getPath().equals(expectedTarget)) && fileHasFallback(key, fs, type.getDirectory() + "/" + expectedTarget)) { Identifier id = new Identifier(namespace, expectedTarget); ids.add(id); - out.accept(id, pack.open(type, id)); + out.accept(id, fs.open(type, id)); } } } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileRpoSearchProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileRpoSearchProvider.java index 184d686..dce1d82 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileRpoSearchProvider.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileRpoSearchProvider.java @@ -1,13 +1,13 @@ package io.gitlab.jfronny.respackopts.filters.util; -import io.gitlab.jfronny.libjf.ResourcePath; import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.gson.AttachmentHolder; import io.gitlab.jfronny.respackopts.model.FileRpo; +import io.gitlab.jfronny.respackopts.model.cache.CacheKey; import io.gitlab.jfronny.respackopts.model.cache.CachedPackState; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import io.gitlab.jfronny.respackopts.util.MetaCache; import net.minecraft.resource.InputSupplier; -import net.minecraft.resource.ResourcePack; import java.io.*; import java.util.Map; @@ -17,23 +17,13 @@ public class FileRpoSearchProvider { return fileName.endsWith(Respackopts.FILE_EXTENSION); } - public static T modifyWithRpo(String fileName, ResourcePack pack, Action action, T defaultValue) { + public static T modifyWithRpo(String fileName, CacheKey key, RespackoptsFS fs, Action action, T defaultValue) { if (isRpo(fileName)) return defaultValue; - CachedPackState state = MetaCache.getState(MetaCache.getKeyByPack(pack)); + CachedPackState state = MetaCache.getState(key); Map rpoCache = state.cachedFileRPOs(); String rpoPathS = fileName + Respackopts.FILE_EXTENSION; if (rpoCache.containsKey(rpoPathS)) return action.run(rpoCache.get(rpoPathS)); - ResourcePath rpoPath = null; - InputSupplier is; - if (fileName.contains("/")) { - try { - rpoPath = new ResourcePath(rpoPathS); - is = pack.open(rpoPath.getType(), rpoPath.getId()); - } catch (Throwable e) { - Respackopts.LOGGER.error("Could not check file filter status", e); - return defaultValue; - } - } else is = pack.openRoot(rpoPathS); + InputSupplier is = fs.open(rpoPathS); if (is == null) return defaultValue; try (Reader w = new InputStreamReader(is.get())) { FileRpo frp = AttachmentHolder.deserialize(state.metadata().version, w, FileRpo.class); @@ -42,7 +32,7 @@ public class FileRpoSearchProvider { return action.run(frp); } catch (Exception e) { - Respackopts.LOGGER.error("Could not get replacement for " + (rpoPath == null ? fileName : rpoPath.getName()) + " in " + pack.getName(), e); + Respackopts.LOGGER.error("Could not get replacement for " + fileName + " in " + state.displayName(), e); return defaultValue; } } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/FileResourcePackProviderMixin.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/FileResourcePackProviderMixin.java index 5fc85f9..98860dd 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/mixin/FileResourcePackProviderMixin.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/mixin/FileResourcePackProviderMixin.java @@ -2,19 +2,18 @@ package io.gitlab.jfronny.respackopts.mixin; import io.gitlab.jfronny.respackopts.Respackopts; import net.minecraft.resource.FileResourcePackProvider; -import net.minecraft.resource.ResourcePackProfile; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.Redirect; -import java.nio.file.Files; -import java.nio.file.Path; +import java.io.IOException; +import java.nio.file.*; @Mixin(FileResourcePackProvider.class) public class FileResourcePackProviderMixin { - @Inject(method = "getFactory(Ljava/nio/file/Path;Z)Lnet/minecraft/resource/ResourcePackProfile$PackFactory;", at = @At("HEAD"), cancellable = true) - private static void getFactory(Path path, boolean alwaysStable, CallbackInfoReturnable cir) { - if (Files.isRegularFile(path) && path.getFileName().toString().endsWith(Respackopts.FILE_EXTENSION)) cir.setReturnValue(null); + //TODO use MixinExtras Wrap instead of redirecting + @Redirect(method = "forEachProfile(Ljava/nio/file/Path;Lnet/minecraft/util/path/SymlinkFinder;ZLjava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;newDirectoryStream(Ljava/nio/file/Path;)Ljava/nio/file/DirectoryStream;")) + private static DirectoryStream createFilteredStream(Path dir) throws IOException { + return Files.newDirectoryStream(dir, path -> !(Files.isRegularFile(path) && path.getFileName().toString().endsWith(Respackopts.FILE_EXTENSION))); } } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java index c31d9e1..058bc3e 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java @@ -50,7 +50,7 @@ public class ResourcePackManagerMixin { if (pack != null) dataLocation = pack.getParent().resolve(pack.getFileName() + Respackopts.FILE_EXTENSION); else Respackopts.LOGGER.warn("Base path of directory resource pack " + rpi.getName() + " is null. This shouldn't happen!"); } else if (rpi instanceof ZipResourcePack zrp) { - File pack = ((ZipResourcePackAccessor) zrp).getBackingZipFile(); + File pack = ((ZipFileWrapperAccessor) ((ZipResourcePackAccessor) zrp).getZipFile()).getFile(); if (pack != null) dataLocation = pack.toPath().getParent().resolve(pack.getName() + Respackopts.FILE_EXTENSION); else Respackopts.LOGGER.warn("Base path of zip resource pack " + rpi.getName() + " is null. This shouldn't happen!"); } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ZipFileWrapperAccessor.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ZipFileWrapperAccessor.java new file mode 100644 index 0000000..35e3e6d --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ZipFileWrapperAccessor.java @@ -0,0 +1,13 @@ +package io.gitlab.jfronny.respackopts.mixin; + +import net.minecraft.resource.ZipResourcePack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.io.File; + +@Mixin(ZipResourcePack.ZipFileWrapper.class) +public interface ZipFileWrapperAccessor { + @Accessor("file") + File getFile(); +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ZipResourcePackAccessor.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ZipResourcePackAccessor.java index 6940102..c49d0d4 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ZipResourcePackAccessor.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ZipResourcePackAccessor.java @@ -4,10 +4,8 @@ import net.minecraft.resource.ZipResourcePack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import java.io.File; - @Mixin(ZipResourcePack.class) public interface ZipResourcePackAccessor { - @Accessor("backingZipFile") - File getBackingZipFile(); + @Accessor("zipFile") + ZipResourcePack.ZipFileWrapper getZipFile(); } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/model/cache/CachedPackState.java b/src/main/java/io/gitlab/jfronny/respackopts/model/cache/CachedPackState.java index a16e75c..de4411a 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/model/cache/CachedPackState.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/model/cache/CachedPackState.java @@ -1,9 +1,10 @@ package io.gitlab.jfronny.respackopts.model.cache; import io.gitlab.jfronny.muscript.data.Scope; -import io.gitlab.jfronny.respackopts.Respackopts; +import io.gitlab.jfronny.muscript.data.Script; import io.gitlab.jfronny.respackopts.model.*; import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch; +import io.gitlab.jfronny.respackopts.muscript.MuScriptScope; import java.util.*; @@ -15,6 +16,8 @@ public record CachedPackState( PackMeta metadata, Map cachedFileRPOs, Map> cachedDirRPOs, // Directory RPOs, from outermost to innermost + Map cachedScripts, // Scripts, available via runScript + Map cachedFiles, // Files, read by readString Scope executionScope ) { public CachedPackState(CacheKey key, PackMeta meta, ConfigBranch branch) { @@ -26,7 +29,9 @@ public record CachedPackState( meta, new HashMap<>(), new HashMap<>(), - branch.addTo(Respackopts.ROOT_SCOPE.fork()) + new HashMap<>(), + new HashMap<>(), + branch.addTo(MuScriptScope.fork(meta.version)) ); } } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/model/tree/ConfigNumericEntry.java b/src/main/java/io/gitlab/jfronny/respackopts/model/tree/ConfigNumericEntry.java index 88ec903..62bf91e 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/model/tree/ConfigNumericEntry.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/model/tree/ConfigNumericEntry.java @@ -90,7 +90,7 @@ public class ConfigNumericEntry extends ConfigEntry implements DNumber { @Override public CategoryBuilder buildEntry(GuiEntryBuilderParam args) { double min = this.min == null ? Double.NEGATIVE_INFINITY : this.min; - double max = this.max == null ? Double.NEGATIVE_INFINITY : this.max; + double max = this.max == null ? Double.POSITIVE_INFINITY : this.max; if (integer) { return args.builder().value( args.name(), diff --git a/src/main/java/io/gitlab/jfronny/respackopts/muscript/MuScriptScope.java b/src/main/java/io/gitlab/jfronny/respackopts/muscript/MuScriptScope.java new file mode 100644 index 0000000..8f6e229 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/muscript/MuScriptScope.java @@ -0,0 +1,65 @@ +package io.gitlab.jfronny.respackopts.muscript; + +import io.gitlab.jfronny.muscript.StandardLib; +import io.gitlab.jfronny.muscript.compiler.Parser; +import io.gitlab.jfronny.muscript.compiler.SourceFS; +import io.gitlab.jfronny.muscript.data.Scope; +import io.gitlab.jfronny.muscript.data.Script; +import io.gitlab.jfronny.muscript.data.dynamic.DList; +import io.gitlab.jfronny.muscript.data.dynamic.Dynamic; +import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal; +import io.gitlab.jfronny.muscript.gson.GsonLib; +import io.gitlab.jfronny.respackopts.model.cache.CacheKey; +import io.gitlab.jfronny.respackopts.model.cache.CachedPackState; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.VersionParsingException; +import net.fabricmc.loader.api.metadata.version.VersionPredicate; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal.of; + +public class MuScriptScope { + private static final Scope SCOPE_12; + private static final Scope SCOPE_13; + + static { + SCOPE_12 = StandardLib.createScope().set("version", args -> { + if (args.size() != 2) throw new IllegalArgumentException("Expected 2 arguments on version but got " + args.size()); + VersionPredicate predicate; + try { + predicate = VersionPredicate.parse(args.get(1).asString().getValue()); + } catch (VersionParsingException e) { + throw new IllegalArgumentException("Could not parse version predicate", e); + } + return DFinal.of(FabricLoader.getInstance().getModContainer(args.get(0).asString().getValue()) + .map(c -> predicate.test(c.getMetadata().getVersion())) + .orElse(false)); + }); + SCOPE_13 = GsonLib.addTo(SCOPE_12.fork()); + } + + public static Scope fork(int version) { + if (version <= 12) return SCOPE_12.fork(); + else return SCOPE_13.fork(); + } + + public static Scope configureFS(Scope scope, CachedPackState state, SourceFS fs) { + int version = state.metadata().version; + if (version <= 12) return scope; + else return scope.set("readString", args -> { + if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for readString: expected 1 but got " + args.size()); + return DFinal.of(state.cachedFiles().computeIfAbsent(args.get(0).asString().getValue(), fs::read)); + }).set("runScript", args -> { + if (args.isEmpty()) throw new IllegalArgumentException("Invalid number of arguments for evaluateScript: expected 1 or more but got " + args.size()); + Script script = state.cachedScripts().computeIfAbsent( + args.get(0).asString().getValue(), + sourceFile -> Parser.parseMultiScript(sourceFile, fs) + ); + List l = args.getValue(); + DList innerArgs = of(l.subList(1, l.size())); + return script.bindTo(scope).call(innerArgs); + }); + } +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/muscript/RespackoptsFS.java b/src/main/java/io/gitlab/jfronny/respackopts/muscript/RespackoptsFS.java new file mode 100644 index 0000000..067323a --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/muscript/RespackoptsFS.java @@ -0,0 +1,42 @@ +package io.gitlab.jfronny.respackopts.muscript; + +import io.gitlab.jfronny.libjf.ResourcePath; +import io.gitlab.jfronny.muscript.compiler.SourceFS; +import io.gitlab.jfronny.respackopts.Respackopts; +import net.minecraft.resource.*; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.io.InputStream; + +public class RespackoptsFS implements SourceFS { + private final ResourcePack pack; + + public RespackoptsFS(ResourcePack pack) { + this.pack = pack; + } + + @Override + public @Nullable String read(String file) { + InputSupplier op = open(file); + if (op == null) return null; + try (InputStream is = op.get()) { + return new String(is.readAllBytes()); + } catch (IOException e) { + Respackopts.LOGGER.error("Could not open file", e); + return null; + } + } + + public @Nullable InputSupplier open(String file) { + String[] segments = file.split("/"); + if (segments.length < 3) return pack.openRoot(segments); + ResourcePath path = new ResourcePath(file); + return open(path.getType(), path.getId()); + } + + public @Nullable InputSupplier open(ResourceType type, Identifier id) { + return pack.open(type, id); + } +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/util/MetaCache.java b/src/main/java/io/gitlab/jfronny/respackopts/util/MetaCache.java index 7c8abb5..fb75cf4 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/util/MetaCache.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/util/MetaCache.java @@ -11,9 +11,12 @@ import io.gitlab.jfronny.respackopts.model.cache.CachedPackState; import io.gitlab.jfronny.respackopts.model.enums.ConfigSyncMode; import io.gitlab.jfronny.respackopts.model.enums.PackCapability; import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch; +import io.gitlab.jfronny.respackopts.muscript.MuScriptScope; +import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS; import net.fabricmc.api.EnvType; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.resource.ResourcePack; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; @@ -158,9 +161,19 @@ public class MetaCache { return PACK_STATES.get(key); } - public static Scope getScope(@Nullable CacheKey key) { - Scope scope = (key == null ? Respackopts.ROOT_SCOPE : MetaCache.getState(key).executionScope()).fork(); - MetaCache.forEach((id, state) -> { + public static Scope getScope(int version) { + Scope scope = MuScriptScope.fork(version); + return populate(scope); + } + + public static Scope getScope(@NotNull CacheKey key, RespackoptsFS fs) { + CachedPackState state = MetaCache.getState(key); + Scope scope = state.executionScope().fork(); + return populate(MuScriptScope.configureFS(scope, state, fs)); + } + + private static Scope populate(Scope scope) { + forEach((id, state) -> { if (!scope.has(state.packId())) { scope.set(state.packId(), state.configBranch()); } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 2f4f3b0..dbe3faa 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -30,6 +30,7 @@ "environment": "client" } ], + "accessWidener": "respackopts.accesswidener", "depends": { "fabricloader": ">=0.12.0", "fabric-api": "*", diff --git a/src/main/resources/respackopts.accesswidener b/src/main/resources/respackopts.accesswidener new file mode 100644 index 0000000..13306dc --- /dev/null +++ b/src/main/resources/respackopts.accesswidener @@ -0,0 +1,3 @@ +accessWidener v2 named + +accessible class net/minecraft/resource/ZipResourcePack$ZipFileWrapper \ No newline at end of file diff --git a/src/main/resources/respackopts.mixins.json b/src/main/resources/respackopts.mixins.json index 14d41ca..871fc6e 100644 --- a/src/main/resources/respackopts.mixins.json +++ b/src/main/resources/respackopts.mixins.json @@ -7,6 +7,7 @@ "DirectoryResourcePackAccessor", "FileResourcePackProviderMixin", "ResourcePackManagerMixin", + "ZipFileWrapperAccessor", "ZipResourcePackAccessor" ], "injectors": {