diff --git a/patches/server/0388-Paper-dumpitem-command.patch b/patches/server/0388-Paper-dumpitem-command.patch index 2b08d26ec6..fc0d3fd7a8 100644 --- a/patches/server/0388-Paper-dumpitem-command.patch +++ b/patches/server/0388-Paper-dumpitem-command.patch @@ -19,32 +19,47 @@ index 69d093d3450931038ac3d27d7874060d13dc2225..27775df10a490ff75ca377e837393173 .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) diff --git a/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java new file mode 100644 -index 0000000000000000000000000000000000000000..5b97a873d20821836820ce2bde54771dc3b86226 +index 0000000000000000000000000000000000000000..efc4ec58ab4b3ceefd66b714d286a6187babc724 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java -@@ -0,0 +1,62 @@ +@@ -0,0 +1,94 @@ +package io.papermc.paper.command.subcommands; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.command.PaperSubcommand; -+import java.util.Objects; ++import java.util.ArrayList; ++import java.util.List; ++import java.util.Map; ++import java.util.Optional; +import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.event.ClickEvent; ++import net.kyori.adventure.text.ComponentLike; ++import net.kyori.adventure.text.JoinConfiguration; ++import net.kyori.adventure.text.TextComponent; ++import net.minecraft.core.Registry; ++import net.minecraft.core.RegistryAccess; ++import net.minecraft.core.component.DataComponentPatch; ++import net.minecraft.core.component.DataComponentType; +import net.minecraft.core.registries.Registries; -+import net.minecraft.nbt.CompoundTag; ++import net.minecraft.nbt.NbtOps; ++import net.minecraft.nbt.NbtUtils; ++import net.minecraft.nbt.Tag; ++import net.minecraft.resources.RegistryOps; +import net.minecraft.world.item.ItemStack; -+import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; -+import org.bukkit.craftbukkit.CraftWorld; -+import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; -+import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; + ++import static net.kyori.adventure.text.Component.join; +import static net.kyori.adventure.text.Component.text; ++import static net.kyori.adventure.text.Component.textOfChildren; ++import static net.kyori.adventure.text.event.ClickEvent.copyToClipboard; ++import static net.kyori.adventure.text.format.NamedTextColor.AQUA; +import static net.kyori.adventure.text.format.NamedTextColor.GRAY; ++import static net.kyori.adventure.text.format.NamedTextColor.RED; ++import static net.kyori.adventure.text.format.NamedTextColor.WHITE; +import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; +import static net.kyori.adventure.text.format.TextDecoration.ITALIC; + @@ -57,31 +72,48 @@ index 0000000000000000000000000000000000000000..5b97a873d20821836820ce2bde54771d + } + + private void doDumpItem(final CommandSender sender) { -+ if (true) throw new UnsupportedOperationException("FIXME"); // TODO -+ /* -+ if (!(sender instanceof Player)) { ++ if (!(sender instanceof final Player player)) { + sender.sendMessage("Only players can use this command"); + return; + } -+ final ItemStack itemStack = CraftItemStack.asNMSCopy(((CraftPlayer) sender).getItemInHand()); -+ final @Nullable CompoundTag tag = itemStack.getTag(); -+ final @Nullable Component nbtComponent = tag == null ? null : PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag)); -+ final String itemId = Objects.requireNonNull(((CraftWorld) ((CraftPlayer) sender).getWorld()).getHandle().registryAccess() -+ .registryOrThrow(Registries.ITEM).getKey(itemStack.getItem())).toString(); -+ final Component message = text() -+ .append(text(itemId, YELLOW)) -+ .apply(b -> { -+ if (nbtComponent != null) { -+ b.append(nbtComponent); ++ final ItemStack itemStack = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand()); ++ final TextComponent.Builder visualOutput = Component.text(); ++ final StringBuilder itemCommandBuilder = new StringBuilder(); ++ final String itemName = itemStack.getItemHolder().unwrapKey().orElseThrow().location().toString(); ++ itemCommandBuilder.append(itemName); ++ visualOutput.append(text(itemName, YELLOW)); // item type ++ final DataComponentPatch patch = itemStack.getComponentsPatch(); ++ ++ final RegistryAccess.Frozen access = ((CraftServer) sender.getServer()).getServer().registryAccess(); ++ final RegistryOps ops = access.createSerializationContext(NbtOps.INSTANCE); ++ final Registry> registry = access.registryOrThrow(Registries.DATA_COMPONENT_TYPE); ++ if (!patch.isEmpty()) { ++ visualOutput.append(text("[", WHITE)); ++ itemCommandBuilder.append("["); ++ final List componentComponents = new ArrayList<>(); ++ final List commandComponents = new ArrayList<>(); ++ for (final Map.Entry, Optional> entry : patch.entrySet()) { ++ final String path = registry.getResourceKey(entry.getKey()).orElseThrow().location().getPath(); ++ if (entry.getValue().isEmpty()) { ++ componentComponents.add(text().append(text('!', RED), text(path, AQUA))); ++ commandComponents.add("!" + path); ++ } else { ++ final Tag serialized = (Tag) ((DataComponentType) entry.getKey()).codecOrThrow().encodeStart(ops, entry.getValue().get()).getOrThrow(); ++ componentComponents.add(textOfChildren( ++ text(path, AQUA), ++ text("=", WHITE), ++ PaperAdventure.asAdventure(NbtUtils.toPrettyComponent(serialized)) ++ )); ++ commandComponents.add(path + "=" + serialized.getAsString()); + } -+ }) -+ .build(); -+ Bukkit.getConsoleSender().sendMessage(message); -+ sender.sendMessage(message); -+ sender.sendMessage(text().content(" Click to copy item to clipboard") -+ .color(GRAY) -+ .decorate(ITALIC) -+ .clickEvent(ClickEvent.copyToClipboard(tag == null ? itemId : (itemId + tag)))); -+ */ ++ ++ } ++ visualOutput ++ .append(join(JoinConfiguration.separator(text(",", WHITE)), componentComponents)) ++ .append(text("]", WHITE)); ++ itemCommandBuilder.append(String.join(",", commandComponents)).append("]"); ++ } ++ final Component hoverMsg = text("Click to copy item definition to clipboard for use with /pgive", GRAY, ITALIC); ++ player.sendMessage(visualOutput.build().compact().hoverEvent(hoverMsg).clickEvent(copyToClipboard(itemCommandBuilder.toString()))); + } +} diff --git a/patches/server/1042-Add-experimental-improved-give-command.patch b/patches/server/1042-Add-experimental-improved-give-command.patch index 670050a507..b7a7ef41c0 100644 --- a/patches/server/1042-Add-experimental-improved-give-command.patch +++ b/patches/server/1042-Add-experimental-improved-give-command.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Add experimental improved give command Supports removing data components from itemstacks diff --git a/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java b/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java -index d76296c6d53065aecb010d8ea682c9acd7365f17..4c3749deebb7d5c35f3977814f1d7b0307198b1e 100644 +index d76296c6d53065aecb010d8ea682c9acd7365f17..9314a94764786982eff0974411f8341bb0353ecf 100644 --- a/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java +++ b/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java @@ -16,7 +16,12 @@ public class ItemArgument implements ArgumentType { @@ -23,6 +23,15 @@ index d76296c6d53065aecb010d8ea682c9acd7365f17..4c3749deebb7d5c35f3977814f1d7b03 } public static ItemArgument item(CommandBuildContext commandRegistryAccess) { +@@ -25,7 +30,7 @@ public class ItemArgument implements ArgumentType { + + public ItemInput parse(StringReader stringReader) throws CommandSyntaxException { + ItemParser.ItemResult itemResult = this.parser.parse(stringReader); +- return new ItemInput(itemResult.item(), itemResult.components()); ++ return new ItemInput(itemResult.item(), itemResult.components(), itemResult.patch()); // Paper - support component removals + } + + public static ItemInput getItem(CommandContext context, String name) { diff --git a/src/main/java/net/minecraft/commands/arguments/item/ItemInput.java b/src/main/java/net/minecraft/commands/arguments/item/ItemInput.java index 3d24fbca90bc7d8bdbac1be2176555c15ae75039..94ea5f0b1913ffa03794d231a6768dd786dc9697 100644 --- a/src/main/java/net/minecraft/commands/arguments/item/ItemInput.java @@ -59,7 +68,7 @@ index 3d24fbca90bc7d8bdbac1be2176555c15ae75039..94ea5f0b1913ffa03794d231a6768dd7 throw ERROR_STACK_TOO_BIG.create(this.getItemName(), itemStack.getMaxStackSize()); } else { diff --git a/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java b/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java -index 5347a96be3bfbbd2963747ba4b5f222215d80371..f306c27f3242084f2cc39393e302bbdd0b228db2 100644 +index 5347a96be3bfbbd2963747ba4b5f222215d80371..fa431de18de902c580855e9c4419125519b6176b 100644 --- a/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java +++ b/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java @@ -59,8 +59,15 @@ public class ItemParser { @@ -153,9 +162,9 @@ index 5347a96be3bfbbd2963747ba4b5f222215d80371..f306c27f3242084f2cc39393e302bbdd + return this.suggestComponentAssignment(builder, true); + } + private CompletableFuture suggestComponentAssignment(SuggestionsBuilder builder, boolean suggestRemove) { -+ if (suggestRemove) builder.suggest("!", Component.literal("Remove a data component")); -+ // Paper end - support component removals String string = builder.getRemaining().toLowerCase(Locale.ROOT); ++ if (suggestRemove && string.isBlank()) builder.suggest("!", Component.literal("Remove a data component")); ++ // Paper end - support component removals SharedSuggestionProvider.filterResources(BuiltInRegistries.DATA_COMPONENT_TYPE.entrySet(), string, entry -> entry.getKey().location(), entry -> { DataComponentType dataComponentType = entry.getValue(); if (dataComponentType.codec() != null) {