/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.mojang.brigadier.ImmutableStringReader * com.mojang.brigadier.Message * com.mojang.brigadier.StringReader * com.mojang.brigadier.exceptions.CommandSyntaxException * com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType * com.mojang.brigadier.exceptions.DynamicCommandExceptionType * com.mojang.brigadier.exceptions.SimpleCommandExceptionType * com.mojang.brigadier.suggestion.Suggestions * com.mojang.brigadier.suggestion.SuggestionsBuilder * com.mojang.serialization.DataResult * it.unimi.dsi.fastutil.objects.ReferenceArraySet * org.apache.commons.lang3.mutable.MutableObject * org.jspecify.annotations.Nullable */ package net.minecraft.commands.arguments.item; import com.mojang.brigadier.ImmutableStringReader; import com.mojang.brigadier.Message; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.serialization.DataResult; import it.unimi.dsi.fastutil.objects.ReferenceArraySet; import java.util.Locale; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.PatchedDataComponentMap; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.nbt.TagParser; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceKey; import net.minecraft.util.Unit; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.apache.commons.lang3.mutable.MutableObject; import org.jspecify.annotations.Nullable; public class ItemParser { private static final DynamicCommandExceptionType ERROR_UNKNOWN_ITEM = new DynamicCommandExceptionType(id -> Component.translatableEscape("argument.item.id.invalid", id)); private static final DynamicCommandExceptionType ERROR_UNKNOWN_COMPONENT = new DynamicCommandExceptionType(id -> Component.translatableEscape("arguments.item.component.unknown", id)); private static final Dynamic2CommandExceptionType ERROR_MALFORMED_COMPONENT = new Dynamic2CommandExceptionType((type, message) -> Component.translatableEscape("arguments.item.component.malformed", type, message)); private static final SimpleCommandExceptionType ERROR_EXPECTED_COMPONENT = new SimpleCommandExceptionType((Message)Component.translatable("arguments.item.component.expected")); private static final DynamicCommandExceptionType ERROR_REPEATED_COMPONENT = new DynamicCommandExceptionType(id -> Component.translatableEscape("arguments.item.component.repeated", id)); private static final DynamicCommandExceptionType ERROR_MALFORMED_ITEM = new DynamicCommandExceptionType(id -> Component.translatableEscape("arguments.item.malformed", id)); public static final char SYNTAX_START_COMPONENTS = '['; public static final char SYNTAX_END_COMPONENTS = ']'; public static final char SYNTAX_COMPONENT_SEPARATOR = ','; public static final char SYNTAX_COMPONENT_ASSIGNMENT = '='; public static final char SYNTAX_REMOVED_COMPONENT = '!'; private static final Function> SUGGEST_NOTHING = SuggestionsBuilder::buildFuture; private final HolderLookup.RegistryLookup items; private final RegistryOps registryOps; private final TagParser tagParser; public ItemParser(HolderLookup.Provider registries) { this.items = registries.lookupOrThrow(Registries.ITEM); this.registryOps = registries.createSerializationContext(NbtOps.INSTANCE); this.tagParser = TagParser.create(this.registryOps); } /* * Issues handling annotations - annotations may be inaccurate */ public ItemResult parse(StringReader reader) throws CommandSyntaxException { final @Nullable MutableObject itemResult = new MutableObject(); final DataComponentPatch.Builder componentsBuilder = DataComponentPatch.builder(); this.parse(reader, new Visitor(){ @Override public void visitItem(Holder item) { itemResult.setValue(item); } @Override public void visitComponent(DataComponentType type, T value) { componentsBuilder.set(type, value); } @Override public void visitRemovedComponent(DataComponentType type) { componentsBuilder.remove(type); } }); Holder item = Objects.requireNonNull((Holder)itemResult.get(), "Parser gave no item"); DataComponentPatch components = componentsBuilder.build(); ItemParser.validateComponents(reader, item, components); return new ItemResult(item, components); } private static void validateComponents(StringReader reader, Holder item, DataComponentPatch components) throws CommandSyntaxException { PatchedDataComponentMap patchedComponents = PatchedDataComponentMap.fromPatch(item.value().components(), components); DataResult result = ItemStack.validateComponents(patchedComponents); result.getOrThrow(error -> ERROR_MALFORMED_ITEM.createWithContext((ImmutableStringReader)reader, error)); } public void parse(StringReader reader, Visitor visitor) throws CommandSyntaxException { int cursor = reader.getCursor(); try { new State(reader, visitor).parse(); } catch (CommandSyntaxException e) { reader.setCursor(cursor); throw e; } } public CompletableFuture fillSuggestions(SuggestionsBuilder builder) { StringReader reader = new StringReader(builder.getInput()); reader.setCursor(builder.getStart()); SuggestionsVisitor handler = new SuggestionsVisitor(); State state = new State(reader, handler); try { state.parse(); } catch (CommandSyntaxException commandSyntaxException) { // empty catch block } return handler.resolveSuggestions(builder, reader); } public static interface Visitor { default public void visitItem(Holder item) { } default public void visitComponent(DataComponentType type, T value) { } default public void visitRemovedComponent(DataComponentType type) { } default public void visitSuggestions(Function> suggestions) { } } public record ItemResult(Holder item, DataComponentPatch components) { } private class State { private final StringReader reader; private final Visitor visitor; private State(StringReader reader, Visitor visitor) { this.reader = reader; this.visitor = visitor; } public void parse() throws CommandSyntaxException { this.visitor.visitSuggestions(this::suggestItem); this.readItem(); this.visitor.visitSuggestions(this::suggestStartComponents); if (this.reader.canRead() && this.reader.peek() == '[') { this.visitor.visitSuggestions(SUGGEST_NOTHING); this.readComponents(); } } private void readItem() throws CommandSyntaxException { int cursor = this.reader.getCursor(); Identifier id = Identifier.read(this.reader); this.visitor.visitItem((Holder)ItemParser.this.items.get(ResourceKey.create(Registries.ITEM, id)).orElseThrow(() -> { this.reader.setCursor(cursor); return ERROR_UNKNOWN_ITEM.createWithContext((ImmutableStringReader)this.reader, (Object)id); })); } private void readComponents() throws CommandSyntaxException { this.reader.expect('['); this.visitor.visitSuggestions(this::suggestComponentAssignmentOrRemoval); ReferenceArraySet visitedComponents = new ReferenceArraySet(); while (this.reader.canRead() && this.reader.peek() != ']') { this.reader.skipWhitespace(); if (this.reader.canRead() && this.reader.peek() == '!') { this.reader.skip(); this.visitor.visitSuggestions(this::suggestComponent); componentType = State.readComponentType(this.reader); if (!visitedComponents.add(componentType)) { throw ERROR_REPEATED_COMPONENT.create(componentType); } this.visitor.visitRemovedComponent(componentType); this.visitor.visitSuggestions(SUGGEST_NOTHING); this.reader.skipWhitespace(); } else { componentType = State.readComponentType(this.reader); if (!visitedComponents.add(componentType)) { throw ERROR_REPEATED_COMPONENT.create(componentType); } this.visitor.visitSuggestions(this::suggestAssignment); this.reader.skipWhitespace(); this.reader.expect('='); this.visitor.visitSuggestions(SUGGEST_NOTHING); this.reader.skipWhitespace(); this.readComponent(ItemParser.this.tagParser, ItemParser.this.registryOps, componentType); this.reader.skipWhitespace(); } this.visitor.visitSuggestions(this::suggestNextOrEndComponents); if (!this.reader.canRead() || this.reader.peek() != ',') break; this.reader.skip(); this.reader.skipWhitespace(); this.visitor.visitSuggestions(this::suggestComponentAssignmentOrRemoval); if (this.reader.canRead()) continue; throw ERROR_EXPECTED_COMPONENT.createWithContext((ImmutableStringReader)this.reader); } this.reader.expect(']'); this.visitor.visitSuggestions(SUGGEST_NOTHING); } public static DataComponentType readComponentType(StringReader reader) throws CommandSyntaxException { if (!reader.canRead()) { throw ERROR_EXPECTED_COMPONENT.createWithContext((ImmutableStringReader)reader); } int cursor = reader.getCursor(); Identifier id = Identifier.read(reader); DataComponentType component = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(id); if (component == null || component.isTransient()) { reader.setCursor(cursor); throw ERROR_UNKNOWN_COMPONENT.createWithContext((ImmutableStringReader)reader, (Object)id); } return component; } private void readComponent(TagParser tagParser, RegistryOps registryOps, DataComponentType componentType) throws CommandSyntaxException { int cursor = this.reader.getCursor(); O tag = tagParser.parseAsArgument(this.reader); DataResult result = componentType.codecOrThrow().parse(registryOps, tag); this.visitor.visitComponent(componentType, result.getOrThrow(message -> { this.reader.setCursor(cursor); return ERROR_MALFORMED_COMPONENT.createWithContext((ImmutableStringReader)this.reader, (Object)componentType.toString(), message); })); } private CompletableFuture suggestStartComponents(SuggestionsBuilder builder) { if (builder.getRemaining().isEmpty()) { builder.suggest(String.valueOf('[')); } return builder.buildFuture(); } private CompletableFuture suggestNextOrEndComponents(SuggestionsBuilder builder) { if (builder.getRemaining().isEmpty()) { builder.suggest(String.valueOf(',')); builder.suggest(String.valueOf(']')); } return builder.buildFuture(); } private CompletableFuture suggestAssignment(SuggestionsBuilder builder) { if (builder.getRemaining().isEmpty()) { builder.suggest(String.valueOf('=')); } return builder.buildFuture(); } private CompletableFuture suggestItem(SuggestionsBuilder builder) { return SharedSuggestionProvider.suggestResource(ItemParser.this.items.listElementIds().map(ResourceKey::identifier), builder); } private CompletableFuture suggestComponentAssignmentOrRemoval(SuggestionsBuilder builder) { builder.suggest(String.valueOf('!')); return this.suggestComponent(builder, String.valueOf('=')); } private CompletableFuture suggestComponent(SuggestionsBuilder builder) { return this.suggestComponent(builder, ""); } private CompletableFuture suggestComponent(SuggestionsBuilder builder, String suffix) { String contents = builder.getRemaining().toLowerCase(Locale.ROOT); SharedSuggestionProvider.filterResources(BuiltInRegistries.DATA_COMPONENT_TYPE.entrySet(), contents, entry -> ((ResourceKey)entry.getKey()).identifier(), entry -> { DataComponentType type = (DataComponentType)entry.getValue(); if (type.codec() != null) { Identifier id = ((ResourceKey)entry.getKey()).identifier(); builder.suggest(String.valueOf(id) + suffix); } }); return builder.buildFuture(); } } private static class SuggestionsVisitor implements Visitor { private Function> suggestions = SUGGEST_NOTHING; private SuggestionsVisitor() { } @Override public void visitSuggestions(Function> suggestions) { this.suggestions = suggestions; } public CompletableFuture resolveSuggestions(SuggestionsBuilder builder, StringReader reader) { return this.suggestions.apply(builder.createOffset(reader.getCursor())); } } }