package net.minecraft.commands.arguments.item; import com.mojang.brigadier.ImmutableStringReader; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.Decoder; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.Util; import net.minecraft.advancements.critereon.ItemSubPredicate; import net.minecraft.advancements.critereon.MinMaxBounds; import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; import net.minecraft.util.parsing.packrat.commands.Grammar; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; public class ItemPredicateArgument implements ArgumentType { private static final Collection EXAMPLES = Arrays.asList("stick", "minecraft:stick", "#stick", "#stick{foo:'bar'}"); static final DynamicCommandExceptionType ERROR_UNKNOWN_ITEM = new DynamicCommandExceptionType( object -> Component.translatableEscape("argument.item.id.invalid", object) ); static final DynamicCommandExceptionType ERROR_UNKNOWN_TAG = new DynamicCommandExceptionType( object -> Component.translatableEscape("arguments.item.tag.unknown", object) ); static final DynamicCommandExceptionType ERROR_UNKNOWN_COMPONENT = new DynamicCommandExceptionType( object -> Component.translatableEscape("arguments.item.component.unknown", object) ); static final Dynamic2CommandExceptionType ERROR_MALFORMED_COMPONENT = new Dynamic2CommandExceptionType( (object, object2) -> Component.translatableEscape("arguments.item.component.malformed", object, object2) ); static final DynamicCommandExceptionType ERROR_UNKNOWN_PREDICATE = new DynamicCommandExceptionType( object -> Component.translatableEscape("arguments.item.predicate.unknown", object) ); static final Dynamic2CommandExceptionType ERROR_MALFORMED_PREDICATE = new Dynamic2CommandExceptionType( (object, object2) -> Component.translatableEscape("arguments.item.predicate.malformed", object, object2) ); private static final ResourceLocation COUNT_ID = ResourceLocation.withDefaultNamespace("count"); static final Map PSEUDO_COMPONENTS = (Map)Stream.of( new ItemPredicateArgument.ComponentWrapper(COUNT_ID, itemStack -> true, MinMaxBounds.Ints.CODEC.map(ints -> itemStack -> ints.matches(itemStack.getCount()))) ) .collect(Collectors.toUnmodifiableMap(ItemPredicateArgument.ComponentWrapper::id, componentWrapper -> componentWrapper)); static final Map PSEUDO_PREDICATES = (Map)Stream.of( new ItemPredicateArgument.PredicateWrapper(COUNT_ID, MinMaxBounds.Ints.CODEC.map(ints -> itemStack -> ints.matches(itemStack.getCount()))) ) .collect(Collectors.toUnmodifiableMap(ItemPredicateArgument.PredicateWrapper::id, predicateWrapper -> predicateWrapper)); private final Grammar>> grammarWithContext; public ItemPredicateArgument(CommandBuildContext context) { ItemPredicateArgument.Context context2 = new ItemPredicateArgument.Context(context); this.grammarWithContext = ComponentPredicateParser.createGrammar(context2); } public static ItemPredicateArgument itemPredicate(CommandBuildContext context) { return new ItemPredicateArgument(context); } public ItemPredicateArgument.Result parse(StringReader reader) throws CommandSyntaxException { return Util.allOf(this.grammarWithContext.parseForCommands(reader))::test; } public static ItemPredicateArgument.Result getItemPredicate(CommandContext context, String name) { return context.getArgument(name, ItemPredicateArgument.Result.class); } @Override public CompletableFuture listSuggestions(CommandContext commandContext, SuggestionsBuilder suggestionsBuilder) { return this.grammarWithContext.parseForSuggestions(suggestionsBuilder); } @Override public Collection getExamples() { return EXAMPLES; } record ComponentWrapper(ResourceLocation id, Predicate presenceChecker, Decoder> valueChecker) { public static ItemPredicateArgument.ComponentWrapper create(ImmutableStringReader reader, ResourceLocation id, DataComponentType componentType) throws CommandSyntaxException { Codec codec = componentType.codec(); if (codec == null) { throw ItemPredicateArgument.ERROR_UNKNOWN_COMPONENT.createWithContext(reader, id); } else { return new ItemPredicateArgument.ComponentWrapper(id, itemStack -> itemStack.has(componentType), codec.map(object -> itemStack -> { T object2 = itemStack.get(componentType); return Objects.equals(object, object2); })); } } public Predicate decode(ImmutableStringReader reader, RegistryOps ops, Tag value) throws CommandSyntaxException { DataResult> dataResult = this.valueChecker.parse(ops, value); return (Predicate)dataResult.getOrThrow( string -> ItemPredicateArgument.ERROR_MALFORMED_COMPONENT.createWithContext(reader, this.id.toString(), string) ); } } static class Context implements ComponentPredicateParser.Context, ItemPredicateArgument.ComponentWrapper, ItemPredicateArgument.PredicateWrapper> { private final HolderLookup.RegistryLookup items; private final HolderLookup.RegistryLookup> components; private final HolderLookup.RegistryLookup> predicates; private final RegistryOps registryOps; Context(HolderLookup.Provider registries) { this.items = registries.lookupOrThrow(Registries.ITEM); this.components = registries.lookupOrThrow(Registries.DATA_COMPONENT_TYPE); this.predicates = registries.lookupOrThrow(Registries.ITEM_SUB_PREDICATE_TYPE); this.registryOps = registries.createSerializationContext(NbtOps.INSTANCE); } public Predicate forElementType(ImmutableStringReader reader, ResourceLocation elementType) throws CommandSyntaxException { Holder.Reference reference = (Holder.Reference)this.items .get(ResourceKey.create(Registries.ITEM, elementType)) .orElseThrow(() -> ItemPredicateArgument.ERROR_UNKNOWN_ITEM.createWithContext(reader, elementType)); return itemStack -> itemStack.is(reference); } public Predicate forTagType(ImmutableStringReader reader, ResourceLocation tagType) throws CommandSyntaxException { HolderSet holderSet = (HolderSet)this.items .get(TagKey.create(Registries.ITEM, tagType)) .orElseThrow(() -> ItemPredicateArgument.ERROR_UNKNOWN_TAG.createWithContext(reader, tagType)); return itemStack -> itemStack.is(holderSet); } public ItemPredicateArgument.ComponentWrapper lookupComponentType(ImmutableStringReader reader, ResourceLocation componentType) throws CommandSyntaxException { ItemPredicateArgument.ComponentWrapper componentWrapper = (ItemPredicateArgument.ComponentWrapper)ItemPredicateArgument.PSEUDO_COMPONENTS.get(componentType); if (componentWrapper != null) { return componentWrapper; } else { DataComponentType dataComponentType = (DataComponentType)this.components .get(ResourceKey.create(Registries.DATA_COMPONENT_TYPE, componentType)) .map(Holder::value) .orElseThrow(() -> ItemPredicateArgument.ERROR_UNKNOWN_COMPONENT.createWithContext(reader, componentType)); return ItemPredicateArgument.ComponentWrapper.create(reader, componentType, dataComponentType); } } public Predicate createComponentTest(ImmutableStringReader reader, ItemPredicateArgument.ComponentWrapper context, Tag value) throws CommandSyntaxException { return context.decode(reader, this.registryOps, value); } public Predicate createComponentTest(ImmutableStringReader reader, ItemPredicateArgument.ComponentWrapper context) { return context.presenceChecker; } public ItemPredicateArgument.PredicateWrapper lookupPredicateType(ImmutableStringReader reader, ResourceLocation predicateType) throws CommandSyntaxException { ItemPredicateArgument.PredicateWrapper predicateWrapper = (ItemPredicateArgument.PredicateWrapper)ItemPredicateArgument.PSEUDO_PREDICATES.get(predicateType); return predicateWrapper != null ? predicateWrapper : (ItemPredicateArgument.PredicateWrapper)this.predicates .get(ResourceKey.create(Registries.ITEM_SUB_PREDICATE_TYPE, predicateType)) .map(ItemPredicateArgument.PredicateWrapper::new) .orElseThrow(() -> ItemPredicateArgument.ERROR_UNKNOWN_PREDICATE.createWithContext(reader, predicateType)); } public Predicate createPredicateTest(ImmutableStringReader reader, ItemPredicateArgument.PredicateWrapper predicate, Tag value) throws CommandSyntaxException { return predicate.decode(reader, this.registryOps, value); } @Override public Stream listElementTypes() { return this.items.listElementIds().map(ResourceKey::location); } @Override public Stream listTagTypes() { return this.items.listTagIds().map(TagKey::location); } @Override public Stream listComponentTypes() { return Stream.concat( ItemPredicateArgument.PSEUDO_COMPONENTS.keySet().stream(), this.components.listElements().filter(reference -> !((DataComponentType)reference.value()).isTransient()).map(reference -> reference.key().location()) ); } @Override public Stream listPredicateTypes() { return Stream.concat(ItemPredicateArgument.PSEUDO_PREDICATES.keySet().stream(), this.predicates.listElementIds().map(ResourceKey::location)); } public Predicate negate(Predicate value) { return value.negate(); } public Predicate anyOf(List> values) { return Util.anyOf(values); } } record PredicateWrapper(ResourceLocation id, Decoder> type) { public PredicateWrapper(Holder.Reference> predicate) { this(predicate.key().location(), predicate.value().codec().map(itemSubPredicate -> itemSubPredicate::matches)); } public Predicate decode(ImmutableStringReader reader, RegistryOps ops, Tag value) throws CommandSyntaxException { DataResult> dataResult = this.type.parse(ops, value); return (Predicate)dataResult.getOrThrow( string -> ItemPredicateArgument.ERROR_MALFORMED_PREDICATE.createWithContext(reader, this.id.toString(), string) ); } } public interface Result extends Predicate { } }