From 7e0fe7077f3ee684d361f889cfc3ece86fe9cba4 Mon Sep 17 00:00:00 2001 From: MattiDragon <61198884+MattiDragon@users.noreply.github.com> Date: Sat, 29 Jun 2024 17:21:31 +0300 Subject: [PATCH] Use item predicates for filtering --- changelog/4.0.0+1.21.md | 3 +- .../screen/ResourceFilterConfigScreen.java | 47 ++-- .../graph/node/base/CountNode.java | 5 +- .../graph/node/base/FilterNode.java | 5 +- .../node/fluid/filter/FilterFluidNode.java | 3 +- .../graph/node/fluid/info/FluidCountNode.java | 3 +- .../node/item/filter/FilterItemsNode.java | 3 +- .../graph/node/item/info/ItemCountNode.java | 3 +- .../misc/FilterPredicateParsing.java | 237 ++++++++++++++++++ .../misc/ResourceFilter.java | 158 ++++-------- .../advanced_networking/lang/en_us.json | 13 +- 11 files changed, 336 insertions(+), 144 deletions(-) create mode 100644 src/main/java/io/github/mattidragon/advancednetworking/misc/FilterPredicateParsing.java diff --git a/changelog/4.0.0+1.21.md b/changelog/4.0.0+1.21.md index 2d104be..9b1b94d 100644 --- a/changelog/4.0.0+1.21.md +++ b/changelog/4.0.0+1.21.md @@ -1 +1,2 @@ -* Updated to 1.21 \ No newline at end of file +* Updated to 1.21 +* Make filter nodes use proper component filtering \ No newline at end of file diff --git a/src/client/java/io/github/mattidragon/advancednetworking/client/screen/ResourceFilterConfigScreen.java b/src/client/java/io/github/mattidragon/advancednetworking/client/screen/ResourceFilterConfigScreen.java index 415b137..40827fb 100644 --- a/src/client/java/io/github/mattidragon/advancednetworking/client/screen/ResourceFilterConfigScreen.java +++ b/src/client/java/io/github/mattidragon/advancednetworking/client/screen/ResourceFilterConfigScreen.java @@ -7,9 +7,12 @@ import io.github.mattidragon.nodeflow.graph.node.Node; import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.CyclingButtonWidget; +import net.minecraft.client.gui.widget.PressableTextWidget; import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.gui.widget.TextWidget; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import net.minecraft.util.Util; public class ResourceFilterConfigScreen extends NodeConfigScreen { private final ResourceFilter filter; @@ -21,11 +24,11 @@ public ResourceFilterConfigScreen(N owner, EditorScreen parent, ResourceFilter filter.setUseRegex(value)); + .build(x + 50, 45, 100, 20, Text.translatable("node.advanced_networking.filter.use_regex"), (button1, value) -> filter.setUseRegex(value)); if (AdvancedNetworking.CONFIG.get().disableRegexFilter()) { regexButton.active = false; regexButton.setTooltip(Tooltip.of(Text.translatable("node.advanced_networking.filter.use_regex.disabled"))); @@ -35,27 +38,31 @@ protected void init() { var whitelistButton = CyclingButtonWidget.onOffBuilder(Text.translatable("node.advanced_networking.filter.mode.whitelist"), Text.translatable("node.advanced_networking.filter.mode.blacklist")) .initially(filter.isWhitelist()) .omitKeyText() - .build(x, 70, 100, 20, Text.empty(), (button1, value) -> filter.setWhitelist(value)); + .build(x + 50, 70, 100, 20, Text.empty(), (button1, value) -> filter.setWhitelist(value)); addDrawableChild(whitelistButton); + + var infoText1 = Text.translatable("node.advanced_networking.filter.syntax_info.line1").formatted(Formatting.YELLOW); + var infoText2 = Text.translatable("node.advanced_networking.filter.syntax_info.line2"); + var textWidth = Math.max(textRenderer.getWidth(infoText1), textRenderer.getWidth(infoText2)); + addDrawableChild(new TextWidget(((width - 200 - textWidth) / 2), + 95, + textWidth, + 10, + infoText1, + textRenderer)); + addDrawableChild(new PressableTextWidget(((width - 200 - textWidth) / 2), + 105, + textWidth, + 10, + infoText2, + button -> Util.getOperatingSystem().open("https://minecraft.wiki/w/Argument_types#item_predicate"), + textRenderer)); - var button = CyclingButtonWidget.builder(mode -> mode == ResourceFilter.Mode.RESOURCE ? Text.translatable("node.advanced_networking.filter.mode.resource") : Text.translatable("node.advanced_networking.filter.mode.tag")) - .values(ResourceFilter.Mode.values()) - .initially(filter.getMode()) - .build(x, 95, 100, 20, Text.translatable("node.advanced_networking.filter.mode"), (button1, value) -> filter.setMode(value)); - addDrawableChild(button); - - var idField = new TextFieldWidget(textRenderer, x, 120, 100, 20, Text.empty()); + var idField = new TextFieldWidget(textRenderer, x, 120, 200, 20, Text.empty()); idField.setMaxLength(100); - idField.setPlaceholder(Text.literal("id").formatted(Formatting.GRAY)); - idField.setText(filter.getIdFilter()); - idField.setChangedListener(filter::setIdFilter); + idField.setPlaceholder(Text.literal("filter").formatted(Formatting.GRAY)); + idField.setText(filter.getFilter()); + idField.setChangedListener(filter::setFilter); addDrawableChild(idField); - - var nbtField = new TextFieldWidget(textRenderer, x, 145, 100, 20, Text.empty()); - nbtField.setMaxLength(200); - nbtField.setPlaceholder(Text.literal("nbt").formatted(Formatting.GRAY)); - nbtField.setText(filter.getNbtFilter()); - nbtField.setChangedListener(filter::setNbtFilter); - addDrawableChild(nbtField); } } diff --git a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/CountNode.java b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/CountNode.java index 15e185b..ecc57ac 100644 --- a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/CountNode.java +++ b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/CountNode.java @@ -2,6 +2,7 @@ import com.mojang.datafixers.util.Either; import io.github.mattidragon.advancednetworking.graph.NetworkControllerContext; +import io.github.mattidragon.advancednetworking.misc.FilterPredicateParsing; import io.github.mattidragon.advancednetworking.misc.ResourceFilter; import io.github.mattidragon.nodeflow.graph.Connector; import io.github.mattidragon.nodeflow.graph.Graph; @@ -28,9 +29,9 @@ public abstract class CountNode> extends Interfa private final ResourceFilter filter; private final BlockApiLookup, @Nullable Direction> lookup; - public CountNode(NodeType type, Graph graph, Registry registry, BlockApiLookup, @Nullable Direction> lookup) { + public CountNode(NodeType type, Graph graph, Registry registry, BlockApiLookup, @Nullable Direction> lookup, FilterPredicateParsing.PredicateParser predicateParser) { super(type, List.of(NetworkControllerContext.TYPE, ContextType.SERVER_WORLD), graph); - filter = new ResourceFilter<>(registry); + filter = new ResourceFilter<>(registry, predicateParser); this.lookup = lookup; } diff --git a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/FilterNode.java b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/FilterNode.java index fd7277d..fd4912c 100644 --- a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/FilterNode.java +++ b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/base/FilterNode.java @@ -2,6 +2,7 @@ import com.mojang.datafixers.util.Either; import io.github.mattidragon.advancednetworking.graph.path.PathBundle; +import io.github.mattidragon.advancednetworking.misc.FilterPredicateParsing; import io.github.mattidragon.advancednetworking.misc.ResourceFilter; import io.github.mattidragon.nodeflow.graph.Connector; import io.github.mattidragon.nodeflow.graph.Graph; @@ -22,9 +23,9 @@ public abstract class FilterNode, T> extends Node { private final ResourceFilter filter; - public FilterNode(NodeType> type, Graph graph, Registry registry) { + public FilterNode(NodeType> type, Graph graph, Registry registry, FilterPredicateParsing.PredicateParser predicateParser) { super(type, List.of(), graph); - this.filter = new ResourceFilter<>(registry); + this.filter = new ResourceFilter<>(registry, predicateParser); } protected abstract DataType, T>> getDataType(); diff --git a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/filter/FilterFluidNode.java b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/filter/FilterFluidNode.java index 8f2f255..c7ab417 100644 --- a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/filter/FilterFluidNode.java +++ b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/filter/FilterFluidNode.java @@ -5,6 +5,7 @@ import io.github.mattidragon.advancednetworking.graph.node.base.FilterNode; import io.github.mattidragon.advancednetworking.graph.node.fluid.FluidTransformer; import io.github.mattidragon.advancednetworking.graph.path.PathBundle; +import io.github.mattidragon.advancednetworking.misc.FilterPredicateParsing; import io.github.mattidragon.nodeflow.graph.Graph; import io.github.mattidragon.nodeflow.graph.data.DataType; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; @@ -16,7 +17,7 @@ public class FilterFluidNode extends FilterNode { public FilterFluidNode(Graph graph) { - super(ModNodeTypes.FILTER_FLUID, graph, Registries.FLUID); + super(ModNodeTypes.FILTER_FLUID, graph, Registries.FLUID, FilterPredicateParsing.FLUID_PREDICATE_PARSER); } @Override diff --git a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/info/FluidCountNode.java b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/info/FluidCountNode.java index fd8c13b..5ef3789 100644 --- a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/info/FluidCountNode.java +++ b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/fluid/info/FluidCountNode.java @@ -2,6 +2,7 @@ import io.github.mattidragon.advancednetworking.graph.ModNodeTypes; import io.github.mattidragon.advancednetworking.graph.node.base.CountNode; +import io.github.mattidragon.advancednetworking.misc.FilterPredicateParsing; import io.github.mattidragon.nodeflow.graph.Graph; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; @@ -10,6 +11,6 @@ public class FluidCountNode extends CountNode { public FluidCountNode(Graph graph) { - super(ModNodeTypes.FLUID_COUNT, graph, Registries.FLUID, FluidStorage.SIDED); + super(ModNodeTypes.FLUID_COUNT, graph, Registries.FLUID, FluidStorage.SIDED, FilterPredicateParsing.FLUID_PREDICATE_PARSER); } } diff --git a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/filter/FilterItemsNode.java b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/filter/FilterItemsNode.java index e3baca3..9390224 100644 --- a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/filter/FilterItemsNode.java +++ b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/filter/FilterItemsNode.java @@ -5,6 +5,7 @@ import io.github.mattidragon.advancednetworking.graph.node.base.FilterNode; import io.github.mattidragon.advancednetworking.graph.node.item.ItemTransformer; import io.github.mattidragon.advancednetworking.graph.path.PathBundle; +import io.github.mattidragon.advancednetworking.misc.FilterPredicateParsing; import io.github.mattidragon.nodeflow.graph.Graph; import io.github.mattidragon.nodeflow.graph.data.DataType; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; @@ -16,7 +17,7 @@ public class FilterItemsNode extends FilterNode { public FilterItemsNode(Graph graph) { - super(ModNodeTypes.FILTER_ITEMS, graph, Registries.ITEM); + super(ModNodeTypes.FILTER_ITEMS, graph, Registries.ITEM, FilterPredicateParsing.ITEM_PREDICATE_PARSER); } @Override diff --git a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/info/ItemCountNode.java b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/info/ItemCountNode.java index 20aadbd..c8c1c0a 100644 --- a/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/info/ItemCountNode.java +++ b/src/main/java/io/github/mattidragon/advancednetworking/graph/node/item/info/ItemCountNode.java @@ -2,6 +2,7 @@ import io.github.mattidragon.advancednetworking.graph.ModNodeTypes; import io.github.mattidragon.advancednetworking.graph.node.base.CountNode; +import io.github.mattidragon.advancednetworking.misc.FilterPredicateParsing; import io.github.mattidragon.nodeflow.graph.Graph; import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; @@ -10,6 +11,6 @@ public class ItemCountNode extends CountNode { public ItemCountNode(Graph graph) { - super(ModNodeTypes.ITEM_COUNT, graph, Registries.ITEM, ItemStorage.SIDED); + super(ModNodeTypes.ITEM_COUNT, graph, Registries.ITEM, ItemStorage.SIDED, FilterPredicateParsing.ITEM_PREDICATE_PARSER); } } diff --git a/src/main/java/io/github/mattidragon/advancednetworking/misc/FilterPredicateParsing.java b/src/main/java/io/github/mattidragon/advancednetworking/misc/FilterPredicateParsing.java new file mode 100644 index 0000000..0b00584 --- /dev/null +++ b/src/main/java/io/github/mattidragon/advancednetworking/misc/FilterPredicateParsing.java @@ -0,0 +1,237 @@ +package io.github.mattidragon.advancednetworking.misc; + +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.*; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Decoder; +import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; +import net.minecraft.command.argument.packrat.ArgumentParser; +import net.minecraft.command.argument.packrat.PackratParsing; +import net.minecraft.component.ComponentMap; +import net.minecraft.component.ComponentMapImpl; +import net.minecraft.component.ComponentType; +import net.minecraft.fluid.Fluid; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtOps; +import net.minecraft.predicate.item.ItemSubPredicate; +import net.minecraft.registry.*; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.registry.tag.TagKey; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class FilterPredicateParsing { + private static final RegistryWrapper.WrapperLookup STATIC_LOOKUP = DynamicRegistryManager.of(Registries.REGISTRIES); + @SuppressWarnings("deprecation") + private static final ArgumentParser>>> ITEM_PARSER + = PackratParsing.createParser(new Context<>(STATIC_LOOKUP, RegistryKeys.ITEM, Item::getRegistryEntry, Item::getComponents)); + @SuppressWarnings("deprecation") + private static final ArgumentParser>>> FLUID_PARSER + = PackratParsing.createParser(new Context<>(STATIC_LOOKUP, RegistryKeys.FLUID, Fluid::getRegistryEntry, fluid -> ComponentMap.EMPTY)); + + private static final SimpleCommandExceptionType ITEM_PREDICATES_NOT_SUPPORTED = new SimpleCommandExceptionType( + Text.translatable("advanced_networking.resource_filter.predicates_not_supported") + ); + private static final DynamicCommandExceptionType INVALID_ITEM_ID_EXCEPTION = new DynamicCommandExceptionType( + id -> Text.stringifiedTranslatable("argument.item.id.invalid", id) + ); + private static final DynamicCommandExceptionType UNKNOWN_ITEM_TAG_EXCEPTION = new DynamicCommandExceptionType( + tag -> Text.stringifiedTranslatable("arguments.item.tag.unknown", tag) + ); + private static final DynamicCommandExceptionType UNKNOWN_ITEM_COMPONENT_EXCEPTION = new DynamicCommandExceptionType( + component -> Text.stringifiedTranslatable("arguments.item.component.unknown", component) + ); + private static final Dynamic2CommandExceptionType MALFORMED_ITEM_COMPONENT_EXCEPTION = new Dynamic2CommandExceptionType( + (object, object2) -> Text.stringifiedTranslatable("arguments.item.component.malformed", object, object2) + ); + private static final DynamicCommandExceptionType UNKNOWN_ITEM_PREDICATE_EXCEPTION = new DynamicCommandExceptionType( + predicate -> Text.stringifiedTranslatable("arguments.item.predicate.unknown", predicate) + ); + private static final Dynamic2CommandExceptionType MALFORMED_ITEM_PREDICATE_EXCEPTION = new Dynamic2CommandExceptionType( + (object, object2) -> Text.stringifiedTranslatable("arguments.item.predicate.malformed", object, object2) + ); + + public static final PredicateParser ITEM_PREDICATE_PARSER = reader -> { + try { + return Either.left(Util.allOf(ITEM_PARSER.parse(reader))); + } catch (CommandSyntaxException e) { + return Either.right(e); + } + }; + public static final PredicateParser FLUID_PREDICATE_PARSER = reader -> { + try { + return Either.left(Util.allOf(FLUID_PARSER.parse(reader))); + } catch (CommandSyntaxException e) { + return Either.right(e); + } + }; + + @FunctionalInterface + public interface PredicateParser { + Either>, CommandSyntaxException> parse(StringReader reader); + } + + private static class Context implements PackratParsing.Callbacks>, ComponentCheck, ItemSubPredicateCheck> { + private final Function> entryFunction; + private final Function defaultComponentGetter; + + private final RegistryWrapper.Impl items; + private final RegistryWrapper.Impl> components; + private final RegistryWrapper.Impl> subPredicateTypes; + private final RegistryKey> key; + private final RegistryOps ops; + + Context(RegistryWrapper.WrapperLookup lookup, RegistryKey> registryKey, Function> entryFunction, Function defaultComponentGetter) { + this.entryFunction = entryFunction; + this.defaultComponentGetter = defaultComponentGetter; + this.items = lookup.getWrapperOrThrow(registryKey); + this.components = lookup.getWrapperOrThrow(RegistryKeys.DATA_COMPONENT_TYPE); + this.subPredicateTypes = lookup.getWrapperOrThrow(RegistryKeys.ITEM_SUB_PREDICATE_TYPE); + this.ops = lookup.getOps(NbtOps.INSTANCE); + this.key = registryKey; + } + + + @Override + public Predicate> itemMatchPredicate(ImmutableStringReader reader, Identifier id) throws CommandSyntaxException { + var entry = items.getOptional(RegistryKey.of(key, id)) + .orElseThrow(() -> INVALID_ITEM_ID_EXCEPTION.createWithContext(reader, id)); + return variant -> entryFunction.apply(variant.getObject()) == entry; + } + + @Override + public Stream streamItemIds() { + return items.streamKeys().map(RegistryKey::getValue); + } + + @Override + public Predicate> tagMatchPredicate(ImmutableStringReader reader, Identifier id) throws CommandSyntaxException { + var tag = items.getOptional(TagKey.of(key, id)) + .orElseThrow(() -> UNKNOWN_ITEM_TAG_EXCEPTION.createWithContext(reader, id)); + return variant -> entryFunction.apply(variant.getObject()).isIn(tag.getTag()); + } + + @Override + public Stream streamTags() { + return items.streamTagKeys().map(TagKey::id); + } + + @Override + public ComponentCheck componentCheck(ImmutableStringReader reader, Identifier id) throws CommandSyntaxException { + var componentType = this.components + .getOptional(RegistryKey.of(RegistryKeys.DATA_COMPONENT_TYPE, id)) + .map(RegistryEntry::value) + .orElseThrow(() -> UNKNOWN_ITEM_COMPONENT_EXCEPTION.createWithContext(reader, id)); + return ComponentCheck.read(reader, id, componentType, defaultComponentGetter); + } + + @Override + public Stream streamComponentIds() { + return components.streamKeys().map(RegistryKey::getValue); + } + + @Override + public Predicate> componentMatchPredicate(ImmutableStringReader reader, ComponentCheck check, NbtElement nbt) throws CommandSyntaxException { + return check.createPredicate(reader, ops, nbt); + } + + @Override + public Predicate> componentPresencePredicate(ImmutableStringReader reader, ComponentCheck check) { + return check.presenceChecker; + } + + @Override + public ItemSubPredicateCheck subPredicateCheck(ImmutableStringReader reader, Identifier id) throws CommandSyntaxException { + return this.subPredicateTypes + .getOptional(RegistryKey.of(RegistryKeys.ITEM_SUB_PREDICATE_TYPE, id)) + .map(ItemSubPredicateCheck::new) + .orElseThrow(() -> UNKNOWN_ITEM_PREDICATE_EXCEPTION.createWithContext(reader, id)); + + } + + @Override + public Stream streamSubPredicateIds() { + return subPredicateTypes.streamKeys().map(RegistryKey::getValue); + } + + @SuppressWarnings("unchecked") + @Override + public Predicate> subPredicatePredicate(ImmutableStringReader reader, ItemSubPredicateCheck check, NbtElement nbt) throws CommandSyntaxException { + if (key.equals(RegistryKeys.ITEM)) { + // Unchecked cast because we know V is Item from the registry key + return (Predicate>) (Predicate) check.createPredicate(reader, ops, nbt); + } + throw ITEM_PREDICATES_NOT_SUPPORTED.createWithContext(reader); + } + + @Override + public Predicate> negate(Predicate> predicate) { + return predicate.negate(); + } + + @Override + public Predicate> anyOf(List>> predicates) { + return Util.anyOf(predicates); + } + } + + private record ComponentCheck( + Identifier id, + Predicate> presenceChecker, + Decoder>> valueChecker + ) { + public static ComponentCheck read(ImmutableStringReader reader, + Identifier id, + ComponentType type, + Function defaultComponentGetter) throws CommandSyntaxException { + var codec = type.getCodec(); + if (codec == null) { + throw UNKNOWN_ITEM_COMPONENT_EXCEPTION.createWithContext(reader, id); + } else { + return new ComponentCheck<>(id, + stack -> getComponents(stack, defaultComponentGetter).contains(type), + codec.map(expected -> stack -> Objects.equals(expected, getComponents(stack, defaultComponentGetter).get(type)))); + } + } + + public Predicate> createPredicate(ImmutableStringReader reader, RegistryOps ops, NbtElement nbt) throws CommandSyntaxException { + return this.valueChecker.parse(ops, nbt).getOrThrow( + error -> MALFORMED_ITEM_COMPONENT_EXCEPTION.createWithContext(reader, this.id.toString(), error) + ); + } + } + + private record ItemSubPredicateCheck(Identifier id, Decoder>> type) { + public ItemSubPredicateCheck(RegistryEntry.Reference> type) { + this(type.registryKey().getValue(), + type.value().codec().map(predicate -> variant -> { + @SuppressWarnings("deprecation") + var stack = new ItemStack(variant.getObject().getRegistryEntry(), 1, variant.getComponents()); + return predicate.test(stack); + })); + } + + public Predicate> createPredicate(ImmutableStringReader reader, RegistryOps ops, NbtElement nbt) throws CommandSyntaxException { + var dataResult = this.type.parse(ops, nbt); + return dataResult.getOrThrow( + error -> MALFORMED_ITEM_PREDICATE_EXCEPTION.createWithContext(reader, this.id.toString(), error) + ); + } + } + + private static ComponentMap getComponents(TransferVariant variant, Function baseGetter) { + var map = new ComponentMapImpl(baseGetter.apply(variant.getObject())); + map.applyChanges(variant.getComponents()); + return map; + } +} diff --git a/src/main/java/io/github/mattidragon/advancednetworking/misc/ResourceFilter.java b/src/main/java/io/github/mattidragon/advancednetworking/misc/ResourceFilter.java index 5ac2e33..58eb50c 100644 --- a/src/main/java/io/github/mattidragon/advancednetworking/misc/ResourceFilter.java +++ b/src/main/java/io/github/mattidragon/advancednetworking/misc/ResourceFilter.java @@ -1,85 +1,70 @@ package io.github.mattidragon.advancednetworking.misc; import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import io.github.mattidragon.advancednetworking.AdvancedNetworking; import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant; -import net.minecraft.command.argument.NbtPathArgumentType; -import net.minecraft.component.ComponentChanges; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; -import net.minecraft.nbt.NbtOps; import net.minecraft.registry.Registry; -import net.minecraft.registry.tag.TagKey; import net.minecraft.text.Text; -import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class ResourceFilter> { private final Registry registry; + private final FilterPredicateParsing.PredicateParser predicateParser; - private String idFilter = ""; - private String nbtFilter = ""; - private Mode mode = Mode.RESOURCE; - private boolean useRegex = false; + private String filter = "*"; + private boolean isRegex = false; private boolean isWhitelist = true; + + @Nullable + private Predicate cachedPredicate = null; - public ResourceFilter(Registry registry) { + public ResourceFilter(Registry registry, FilterPredicateParsing.PredicateParser predicateParser) { this.registry = registry; + this.predicateParser = predicateParser; } public List validate() { var list = new ArrayList(); - if (!idFilter.isBlank()) { - if (shouldUseRegex()) { - try { - Pattern.compile(idFilter); - } catch (PatternSyntaxException e) { - list.add(Text.translatable("node.advanced_networking.filter.invalid_id_regex", e.getDescription(), e.getIndex())); - } - } else { - var id = Identifier.tryParse(idFilter.trim()); - if (id == null) { - list.add(Text.translatable("node.advanced_networking.filter.invalid_id", idFilter)); - } else { - if (mode == Mode.RESOURCE && !registry.containsId(id)) - list.add(Text.translatable("node.advanced_networking.filter.unknown_resource", id.toString())); - if (mode == Mode.TAG && registry.streamTags().map(TagKey::id).noneMatch(id::equals)) - list.add(Text.translatable("node.advanced_networking.filter.unknown_tag", id.toString())); - } + + if (shouldUseRegex()) { + try { + var pattern = Pattern.compile(filter); + cachedPredicate = v -> pattern.matcher(Objects.requireNonNull(registry.getId(v.getObject()), "resource not registered").toString()).matches(); + } catch (PatternSyntaxException e) { + list.add(Text.translatable("node.advanced_networking.filter.invalid_id_regex", e.getDescription(), e.getIndex())); } + } else { + var parse = predicateParser.parse(new StringReader(filter.trim())); + parse.ifLeft(predicate -> this.cachedPredicate = predicate::test); + parse.ifRight(e -> list.add(Text.translatable("node.advanced_networking.filter.invalid_filter", e.getMessage()))); } - - try { - if (!nbtFilter.isBlank()) - NbtPathArgumentType.nbtPath().parse(new StringReader(nbtFilter.trim())); - } catch (CommandSyntaxException | StringIndexOutOfBoundsException e) { - list.add(Text.translatable("node.advanced_networking.filter.invalid_nbt_path", e.getMessage())); - } + return list; } public boolean isAllowed(V resource) { - NbtPathArgumentType.NbtPath nbtPath; - try { - nbtPath = nbtFilter.isBlank() ? null : NbtPathArgumentType.nbtPath().parse(new StringReader(nbtFilter.trim())); - } catch (CommandSyntaxException e) { - throw new RuntimeException("Error while building nbt path not caught in validation", e); + if (cachedPredicate == null) { + if (shouldUseRegex()) { + var pattern = Pattern.compile(filter); + cachedPredicate = v -> pattern.matcher(Objects.requireNonNull(registry.getId(v.getObject()), "resource not registered").toString()).matches(); + } else { + cachedPredicate = predicateParser.parse(new StringReader(filter.trim())) + .left() + .orElseThrow(() -> new IllegalStateException("Error in predicate not caught in validation")) + ::test; + } } - - var idMatches = idFilter.isBlank() || switch (mode) { - case RESOURCE -> checkId(registry.getId(resource.getObject())); - case TAG -> registry.getEntry(resource.getObject()) - .streamTags() - .map(TagKey::id) - .anyMatch(this::checkId); - }; - var nbtMatches = nbtPath == null || nbtPath.count(ComponentChanges.CODEC.encodeStart(NbtOps.INSTANCE, resource.getComponents()).result().orElseGet(NbtCompound::new)) > 0; - var matches = idMatches && nbtMatches; + + var matches = cachedPredicate.test(resource); if (isWhitelist) { return matches; @@ -88,70 +73,38 @@ public boolean isAllowed(V resource) { } } - private boolean checkId(Identifier id) { - if (shouldUseRegex()) { - return Pattern.matches(idFilter, id.toString()); - } else { - return id.equals(Identifier.tryParse(idFilter)); - } - } - public void readNbt(NbtCompound data) { // Can't go breaking old saves - if (data.contains("itemId", NbtElement.STRING_TYPE)) { - idFilter = data.getString("itemId"); - } else if (data.contains("fluidId", NbtElement.STRING_TYPE)) { - idFilter = data.getString("fluidId"); - } else { - idFilter = data.getString("idFilter"); - } - - // Can't go breaking old saves - if (data.contains("nbt", NbtElement.STRING_TYPE)) { - nbtFilter = data.getString("nbt"); + if (data.contains("idFilter", NbtElement.STRING_TYPE)) { + filter = data.getString("idFilter"); } else { - nbtFilter = data.getString("nbtFilter"); + filter = data.getString("filter"); } - mode = Mode.byOrdinal(data.getInt("mode")); isWhitelist = data.getBoolean("whitelist"); - useRegex = data.getBoolean("regex"); + isRegex = data.getBoolean("regex"); + + cachedPredicate = null; } public void writeNbt(NbtCompound data) { - data.putString("idFilter", idFilter); - data.putString("nbtFilter", nbtFilter); - data.putInt("mode", mode.ordinal()); + data.putString("filter", filter); data.putBoolean("whitelist", isWhitelist); - data.putBoolean("regex", useRegex); - } - - public String getIdFilter() { - return idFilter; - } - - public void setIdFilter(String idFilter) { - this.idFilter = idFilter; - } - - public String getNbtFilter() { - return nbtFilter; - } - - public void setNbtFilter(String nbtFilter) { - this.nbtFilter = nbtFilter; + data.putBoolean("regex", isRegex); } - public Mode getMode() { - return mode; + public String getFilter() { + return filter; } - public void setMode(Mode mode) { - this.mode = mode; + public void setFilter(String filter) { + this.filter = filter; + cachedPredicate = null; } public void setUseRegex(boolean useRegex) { - this.useRegex = useRegex; + this.isRegex = useRegex; + cachedPredicate = null; } public boolean isWhitelist() { @@ -163,15 +116,6 @@ public void setWhitelist(boolean whitelist) { } public boolean shouldUseRegex() { - return useRegex && !AdvancedNetworking.CONFIG.get().disableRegexFilter(); - } - - public enum Mode { - RESOURCE, TAG; - - private static Mode byOrdinal(int ordinal) { - return ordinal > 0 && ordinal < values().length ? values()[ordinal] : RESOURCE; - } + return isRegex && !AdvancedNetworking.CONFIG.get().disableRegexFilter(); } - } diff --git a/src/main/resources/assets/advanced_networking/lang/en_us.json b/src/main/resources/assets/advanced_networking/lang/en_us.json index 1a3205b..306c867 100644 --- a/src/main/resources/assets/advanced_networking/lang/en_us.json +++ b/src/main/resources/assets/advanced_networking/lang/en_us.json @@ -12,7 +12,8 @@ "screen.advanced_networking.cable_config.interface_type.interface": "Interface", "screen.advanced_networking.cable_config.interface_type.blocked": "Blocked", "screen.advanced_networking.adventure_mode_access": "Adventure mode access", - + + "advanced_networking.resource_filter.predicates_not_supported": "Item predicates are not supported in this filter", "tag.item.advanced_networking.wrenches": "Wrenches", "side.advanced_networking.north": "North", @@ -87,16 +88,12 @@ "node.advanced_networking.interface.loading": "Loading interfaces...", "node.advanced_networking.interface.no_interfaces": "No interfaces found", - "node.advanced_networking.filter.invalid_id": "Invalid identifier: %s", + "node.advanced_networking.filter.invalid_filter": "Invalid filter: %s", "node.advanced_networking.filter.invalid_id_regex": "Invalid regex: %s near index %s", - "node.advanced_networking.filter.unknown_resource": "Unknown resource: %s", - "node.advanced_networking.filter.unknown_tag": "Unknown tag: #%s", - "node.advanced_networking.filter.invalid_nbt_path": "Invalid nbt filter: %s", "node.advanced_networking.filter.use_regex": "Regex", + "node.advanced_networking.filter.syntax_info.line1": "\uD83D\uDEC8 Item predicate syntax", + "node.advanced_networking.filter.syntax_info.line2": "Click for more info", "node.advanced_networking.filter.use_regex.disabled": "Regex filter is disabled in the config", - "node.advanced_networking.filter.mode": "Mode", - "node.advanced_networking.filter.mode.resource": "Resource", - "node.advanced_networking.filter.mode.tag": "Tag", "node.advanced_networking.filter.mode.whitelist": "Whitelist", "node.advanced_networking.filter.mode.blacklist": "Blacklist",