diff --git a/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java b/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java index 4c3d5712c677..779818ea2d84 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java +++ b/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java @@ -15,13 +15,10 @@ 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.core.registries.BuiltInRegistries; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.SnbtPrinterTagVisitor; @@ -29,13 +26,13 @@ import net.minecraft.resources.RegistryOps; import net.minecraft.world.item.ItemStack; import org.bukkit.command.CommandSender; -import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftRegistry; 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 java.util.Objects.requireNonNull; import static net.kyori.adventure.text.Component.join; import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.textOfChildren; @@ -56,71 +53,77 @@ public boolean execute(final CommandSender sender, final String subCommand, fina return true; } - @SuppressWarnings({"unchecked", "OptionalAssignedToNull", "rawtypes"}) + @SuppressWarnings({"unchecked", "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; } - 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().identifier().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(); + final TextComponent.Builder output = Component.text(); + final StringBuilder itemToCopy = new StringBuilder(); + final ItemStack item = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand()); + final String itemName = item.getItemHolder().unwrapKey().orElseThrow().identifier().toString(); + itemToCopy.append(itemName); + output.append(text(itemName, YELLOW)); // item type + + final Set> remainingComponents = Collections.newSetFromMap(new IdentityHashMap<>()); + final DataComponentPatch patch = item.getComponentsPatch(); + remainingComponents.addAll(patch.entrySet().stream().map(Map.Entry::getKey).toList()); + final DataComponentMap prototype = item.getPrototype(); if (includeAllComponents) { - referencedComponentTypes.addAll(prototype.keySet()); + remainingComponents.addAll(prototype.keySet()); } + remainingComponents.removeIf(DataComponentType::isTransient); - final RegistryAccess.Frozen access = ((CraftServer) sender.getServer()).getServer().registryAccess(); - final RegistryOps ops = access.createSerializationContext(NbtOps.INSTANCE); - final Registry> registry = access.lookupOrThrow(Registries.DATA_COMPONENT_TYPE); - final List componentComponents = new ArrayList<>(); - final List commandComponents = new ArrayList<>(); - for (final DataComponentType type : referencedComponentTypes) { - final String path = registry.getResourceKey(type).orElseThrow().identifier().getPath(); - final @Nullable Optional patchedValue = patch.get(type); - final @Nullable TypedDataComponent prototypeValue = prototype.getTyped(type); - if (patchedValue != null) { + final RegistryOps ops = CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE); + final List writtenComponents = new ArrayList<>(); + final List componentsToCopy = new ArrayList<>(); + for (final Map.Entry, Optional> entry : patch.entrySet()) { // patch + final DataComponentType type = entry.getKey(); + if (remainingComponents.remove(type)) { + final String path = requireNonNull(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(type)).getPath(); + final Optional patchedValue = entry.getValue(); if (patchedValue.isEmpty()) { - componentComponents.add(text().append(text('!', RED), text(path, AQUA))); - commandComponents.add("!" + path); + writtenComponents.add(text().append(text('!', RED), text(path, AQUA))); + componentsToCopy.add("!" + path); } else { final Tag serialized = (Tag) ((DataComponentType) type).codecOrThrow().encodeStart(ops, patchedValue.get()).getOrThrow(); - writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); + writeComponentValue(writtenComponents::add, componentsToCopy::add, path, serialized); } - } else if (includeAllComponents && prototypeValue != null) { - final Tag serialized = prototypeValue.encodeValue(ops).getOrThrow(); - writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); } } - if (!componentComponents.isEmpty()) { - visualOutput.append( + + if (includeAllComponents) { + for (final DataComponentType type : remainingComponents) { // prototype + final String path = requireNonNull(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(type)).getPath(); + final Tag serialized = requireNonNull(prototype.getTyped(type)).encodeValue(ops).getOrThrow(); + writeComponentValue(writtenComponents::add, componentsToCopy::add, path, serialized); + } + } + + if (!writtenComponents.isEmpty()) { + output.append( text("[", color(0x8910CE)), - join(JoinConfiguration.separator(text(",", GRAY)), componentComponents), + join(JoinConfiguration.separator(text(",", GRAY)), writtenComponents), text("]", color(0x8910CE)) ); - itemCommandBuilder + itemToCopy .append("[") - .append(String.join(",", commandComponents)) + .append(String.join(",", componentsToCopy)) .append("]"); } - player.sendMessage(visualOutput.build().compact()); + player.sendMessage(output.build().compact()); final Component copyMsg = text("Click to copy item definition to clipboard for use with /give", GRAY, ITALIC); - sender.sendMessage(copyMsg.clickEvent(copyToClipboard(itemCommandBuilder.toString()))); + sender.sendMessage(copyMsg.clickEvent(copyToClipboard(itemToCopy.toString()))); } - private static void writeComponentValue(final Consumer visualOutput, final Consumer commandOutput, final String path, final Tag serialized) { - visualOutput.accept(textOfChildren( + private static void writeComponentValue(final Consumer output, final Consumer copyOutput, final String path, final Tag serialized) { + output.accept(textOfChildren( text(path, color(0xFF7FD7)), text("=", WHITE), PaperAdventure.asAdventure(NbtUtils.toPrettyComponent(serialized)) )); - commandOutput.accept(path + "=" + new SnbtPrinterTagVisitor("", 0, new ArrayList<>()).visit(serialized)); + copyOutput.accept(path + "=" + new SnbtPrinterTagVisitor("", 0, new ArrayList<>()).visit(serialized)); } @Override diff --git a/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java b/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java index 2756ca738b99..5eb0a3e02bcf 100644 --- a/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java +++ b/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java @@ -21,7 +21,7 @@ public class TicksPerSecondCommand extends Command { public TicksPerSecondCommand(String name) { super(name); this.description = "Gets the current ticks per second for the server"; - this.usageMessage = "/tps"; + this.usageMessage = "/tps [mem]"; this.setPermission("bukkit.command.tps"); }