diff --git a/patches/server/0385-Paper-dumpitem-command.patch b/patches/server/0385-Paper-dumpitem-command.patch index fc0d3fd7a8..a752f44867 100644 --- a/patches/server/0385-Paper-dumpitem-command.patch +++ b/patches/server/0385-Paper-dumpitem-command.patch @@ -19,29 +19,37 @@ 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..efc4ec58ab4b3ceefd66b714d286a6187babc724 +index 0000000000000000000000000000000000000000..522609d685159c71aeadbb16ee11caa826a20ff7 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java -@@ -0,0 +1,94 @@ +@@ -0,0 +1,133 @@ +package io.papermc.paper.command.subcommands; + +import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.command.CommandUtil; +import io.papermc.paper.command.PaperSubcommand; +import java.util.ArrayList; ++import java.util.Collections; ++import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; ++import java.util.Set; ++import java.util.function.Consumer; +import net.kyori.adventure.text.Component; +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.DataComponentMap; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.core.component.DataComponentType; ++import net.minecraft.core.component.TypedDataComponent; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.NbtUtils; ++import net.minecraft.nbt.SnbtPrinterTagVisitor; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.RegistryOps; +import net.minecraft.world.item.ItemStack; @@ -50,6 +58,7 @@ index 0000000000000000000000000000000000000000..efc4ec58ab4b3ceefd66b714d286a618 +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; @@ -61,17 +70,19 @@ index 0000000000000000000000000000000000000000..efc4ec58ab4b3ceefd66b714d286a618 +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.TextColor.color; +import static net.kyori.adventure.text.format.TextDecoration.ITALIC; + +@DefaultQualifier(NonNull.class) +public final class DumpItemCommand implements PaperSubcommand { + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { -+ this.doDumpItem(sender); ++ this.doDumpItem(sender, args.length > 0 && "all".equals(args[0])); + return true; + } + -+ private void doDumpItem(final CommandSender sender) { ++ @SuppressWarnings({"unchecked", "OptionalAssignedToNull", "rawtypes"}) ++ private void doDumpItem(final CommandSender sender, final boolean includeAllComponents) { + if (!(sender instanceof final Player player)) { + sender.sendMessage("Only players can use this command"); + return; @@ -82,38 +93,66 @@ index 0000000000000000000000000000000000000000..efc4ec58ab4b3ceefd66b714d286a618 + final String itemName = itemStack.getItemHolder().unwrapKey().orElseThrow().location().toString(); + itemCommandBuilder.append(itemName); + visualOutput.append(text(itemName, YELLOW)); // item type ++ final Set> referencedComponentTypes = Collections.newSetFromMap(new IdentityHashMap<>()); + final DataComponentPatch patch = itemStack.getComponentsPatch(); ++ referencedComponentTypes.addAll(patch.entrySet().stream().map(Map.Entry::getKey).toList()); ++ final DataComponentMap prototype = itemStack.getItem().components(); ++ if (includeAllComponents) { ++ referencedComponentTypes.addAll(prototype.keySet()); ++ } + + 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()) { ++ final List componentComponents = new ArrayList<>(); ++ final List commandComponents = new ArrayList<>(); ++ for (final DataComponentType type : referencedComponentTypes) { ++ final String path = registry.getResourceKey(type).orElseThrow().location().getPath(); ++ final @Nullable Optional patchedValue = patch.get(type); ++ final @Nullable TypedDataComponent prototypeValue = prototype.getTyped(type); ++ if (patchedValue != null) { ++ if (patchedValue.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()); ++ final Tag serialized = (Tag) ((DataComponentType) type).codecOrThrow().encodeStart(ops, patchedValue.get()).getOrThrow(); ++ writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); + } -+ ++ } else if (includeAllComponents && prototypeValue != null) { ++ final Tag serialized = prototypeValue.encodeValue(ops).getOrThrow(); ++ writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); + } -+ 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()))); ++ if (!componentComponents.isEmpty()) { ++ visualOutput.append( ++ text("[", color(0x8910CE)), ++ join(JoinConfiguration.separator(text(",", GRAY)), componentComponents), ++ text("]", color(0x8910CE)) ++ ); ++ itemCommandBuilder ++ .append("[") ++ .append(String.join(",", commandComponents)) ++ .append("]"); ++ } ++ player.sendMessage(visualOutput.build().compact()); ++ final Component copyMsg = text("Click to copy item definition to clipboard for use with /pgive", GRAY, ITALIC); ++ sender.sendMessage(copyMsg.clickEvent(copyToClipboard(itemCommandBuilder.toString()))); ++ } ++ ++ private static void writeComponentValue(final Consumer visualOutput, final Consumer commandOutput, final String path, final Tag serialized) { ++ visualOutput.accept(textOfChildren( ++ text(path, color(0xFF7FD7)), ++ text("=", WHITE), ++ PaperAdventure.asAdventure(NbtUtils.toPrettyComponent(serialized)) ++ )); ++ commandOutput.accept(path + "=" + new SnbtPrinterTagVisitor("", 0, new ArrayList<>()).visit(serialized)); ++ } ++ ++ @Override ++ public List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { ++ if (args.length == 1) { ++ return CommandUtil.getListMatchingLast(sender, args, "all"); ++ } ++ return Collections.emptyList(); + } +} diff --git a/patches/server/0789-Add-paper-dumplisteners-command.patch b/patches/server/0789-Add-paper-dumplisteners-command.patch index 4170206f59..ba293d1dad 100644 --- a/patches/server/0789-Add-paper-dumplisteners-command.patch +++ b/patches/server/0789-Add-paper-dumplisteners-command.patch @@ -17,153 +17,6 @@ index c9bb2df0d884227576ed8d2e72219bbbd7ba827e..534d9c380f26d6cce3c99fa88ad2e154 return commands.entrySet().stream() .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 -index efc4ec58ab4b3ceefd66b714d286a6187babc724..fae73bac86fea722be4c9178eda0779ea88d1884 100644 ---- a/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java -+++ b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java -@@ -1,19 +1,26 @@ - package io.papermc.paper.command.subcommands; - - import io.papermc.paper.adventure.PaperAdventure; -+import io.papermc.paper.command.CommandUtil; - import io.papermc.paper.command.PaperSubcommand; - import java.util.ArrayList; -+import java.util.Collections; -+import java.util.IdentityHashMap; - import java.util.List; - import java.util.Map; - import java.util.Optional; -+import java.util.Set; -+import java.util.function.Consumer; - import net.kyori.adventure.text.Component; - 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.DataComponentMap; - import net.minecraft.core.component.DataComponentPatch; - import net.minecraft.core.component.DataComponentType; -+import net.minecraft.core.component.TypedDataComponent; - import net.minecraft.core.registries.Registries; - import net.minecraft.nbt.NbtOps; - import net.minecraft.nbt.NbtUtils; -@@ -25,6 +32,7 @@ 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; -@@ -36,17 +44,19 @@ 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.TextColor.color; - import static net.kyori.adventure.text.format.TextDecoration.ITALIC; - - @DefaultQualifier(NonNull.class) - public final class DumpItemCommand implements PaperSubcommand { - @Override - public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { -- this.doDumpItem(sender); -+ this.doDumpItem(sender, args.length > 0 && "all".equals(args[0])); - return true; - } - -- private void doDumpItem(final CommandSender sender) { -+ @SuppressWarnings({"unchecked", "OptionalAssignedToNull", "rawtypes"}) -+ private void doDumpItem(final CommandSender sender, final boolean includeAllComponents) { - if (!(sender instanceof final Player player)) { - sender.sendMessage("Only players can use this command"); - return; -@@ -57,38 +67,65 @@ public final class DumpItemCommand implements PaperSubcommand { - final String itemName = itemStack.getItemHolder().unwrapKey().orElseThrow().location().toString(); - itemCommandBuilder.append(itemName); - visualOutput.append(text(itemName, YELLOW)); // item type -+ final Set> referencedComponentTypes = Collections.newSetFromMap(new IdentityHashMap<>()); - final DataComponentPatch patch = itemStack.getComponentsPatch(); -+ referencedComponentTypes.addAll(patch.entrySet().stream().map(Map.Entry::getKey).toList()); -+ final DataComponentMap prototype = itemStack.getItem().components(); -+ if (includeAllComponents) { -+ referencedComponentTypes.addAll(prototype.keySet()); -+ } - - 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()) { -+ final List componentComponents = new ArrayList<>(); -+ final List commandComponents = new ArrayList<>(); -+ for (final DataComponentType type : referencedComponentTypes) { -+ final String path = registry.getResourceKey(type).orElseThrow().location().getPath(); -+ final @Nullable Optional patchedValue = patch.get(type); -+ final @Nullable TypedDataComponent prototypeValue = prototype.getTyped(type); -+ if (patchedValue != null) { -+ if (patchedValue.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()); -+ final Tag serialized = (Tag) ((DataComponentType) type).codecOrThrow().encodeStart(ops, patchedValue.get()).getOrThrow(); -+ writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); - } -- -+ } else if (includeAllComponents && prototypeValue != null) { -+ final Tag serialized = prototypeValue.encodeValue(ops).getOrThrow(); -+ writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); - } -- visualOutput -- .append(join(JoinConfiguration.separator(text(",", WHITE)), componentComponents)) -- .append(text("]", WHITE)); -- itemCommandBuilder.append(String.join(",", commandComponents)).append("]"); -+ } -+ if (!componentComponents.isEmpty()) { -+ visualOutput.append( -+ text("[", color(0x8910CE)), -+ join(JoinConfiguration.separator(text(",", GRAY)), componentComponents), -+ text("]", color(0x8910CE)) -+ ); -+ itemCommandBuilder -+ .append("[") -+ .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()))); - } -+ -+ private static void writeComponentValue(final Consumer visualOutput, final Consumer commandOutput, final String path, final Tag serialized) { -+ visualOutput.accept(textOfChildren( -+ text(path, color(0xFF7FD7)), -+ text("=", WHITE), -+ PaperAdventure.asAdventure(NbtUtils.toPrettyComponent(serialized)) -+ )); -+ commandOutput.accept(path + "=" + serialized.getAsString()); -+ } -+ -+ @Override -+ public List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { -+ if (args.length == 1) { -+ return CommandUtil.getListMatchingLast(sender, args, "all"); -+ } -+ return Collections.emptyList(); -+ } - } diff --git a/src/main/java/io/papermc/paper/command/subcommands/DumpListenersCommand.java b/src/main/java/io/papermc/paper/command/subcommands/DumpListenersCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..aa44d4685de3caee4131449bead7a084868ff976