diff --git a/CHANGELOG.md b/CHANGELOG.md index 40d7499..4f3f1f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## 87.0.1 + +### Changed + +- Added more copy types for the `/tk copy` command. You can now use the following types. Please note that `plain` is the default if no type is provided. Plain is the old default. + - `KUBEJS` + - `KUBEJS_NATIVE` + - `JSON` + - `SNBT` + - `NBT` + - `CRAFTTWEAKER` + - `PLAIN` + - `CSV` + +### Fixed + +- The mod works again on NeoForge + ## 87.0.0 ### Changed diff --git a/common/src/main/java/com/sunekaer/toolkit/commands/inventory/CopyCommand.java b/common/src/main/java/com/sunekaer/toolkit/commands/inventory/CopyCommand.java index 9dee832..295e53e 100644 --- a/common/src/main/java/com/sunekaer/toolkit/commands/inventory/CopyCommand.java +++ b/common/src/main/java/com/sunekaer/toolkit/commands/inventory/CopyCommand.java @@ -1,5 +1,6 @@ package com.sunekaer.toolkit.commands.inventory; +import com.google.gson.Gson; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.context.CommandContext; @@ -9,66 +10,254 @@ import com.sunekaer.toolkit.network.Handler; import com.sunekaer.toolkit.network.SetCopy; import com.sunekaer.toolkit.utils.CommandUtils; -import net.minecraft.ChatFormatting; +import dev.architectury.networking.NetworkManager; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; -import net.minecraft.core.Registry; +import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.ClickEvent; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.SnbtPrinterTagVisitor; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.network.chat.Style; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.TagKey; import net.minecraft.world.item.ItemStack; -import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; -import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; +import java.util.function.BiFunction; +import java.util.stream.Stream; public class CopyCommand { public static ArgumentBuilder register() { return Commands.literal("copy") - .then(Commands.argument("type", StringArgumentType.word()).suggests(InventoryCollector::suggestions).executes(CopyCommand::copy)); + .then( + Commands.argument("type", StringArgumentType.word()) + .suggests(InventoryCollector::suggestions) + .executes(ctx -> copy(ctx, null)) + .then(Commands.argument("outputType", StringArgumentType.word()) + .suggests(CopyCommand::outputTypeSuggestions) + .executes(ctx -> copy(ctx, StringArgumentType.getString(ctx, "outputType"))) + ) + ); + } - private static int copy(CommandContext context) throws CommandSyntaxException { + private static CompletableFuture outputTypeSuggestions(CommandContext context, SuggestionsBuilder suggestionsBuilder) { + var sortedTypes = Stream.of(OutputType.values()).sorted(Comparator.comparingInt(a -> a.order)).toList(); + for (OutputType value : sortedTypes) { + suggestionsBuilder.suggest(value.name); + } + + return suggestionsBuilder.buildFuture(); + } + + private static int copy(CommandContext context, @Nullable String outputType) throws CommandSyntaxException { var source = context.getSource(); var type = InventoryCollector.fromString(StringArgumentType.getString(context, "type")); + var computedOutputType = OutputType.PLAIN; + if (outputType != null) { + try { + computedOutputType = OutputType.valueOf(outputType.toUpperCase()); + } catch (IllegalArgumentException e) { + source.sendFailure(Component.literal("Invalid output type")); + return 0; + } + } + if (type == null) { - // TODO: Move to correct exception source.sendFailure(Component.literal("Invalid type")); return 0; } var player = source.getPlayerOrException(); var itemCollection = type.itemCollector.apply(player); + var nonEmptyItems = itemCollection.stream().filter(stack -> !stack.isEmpty()).toList(); + + var outputString = computedOutputType.function.apply(nonEmptyItems, source.registryAccess()); + + source.sendSuccess(() -> Component.translatable("commands.toolkit.clipboard.copied"), true); + NetworkManager.sendToPlayer(player, new SetCopy(outputString)); + + return 1; + } + + enum OutputType { + KUBEJS(4,"kubejs", (items, lookup) -> { + StringBuilder builder = new StringBuilder(); + builder.append("[").append(CommandUtils.NEW_LINE); + + final String tab = " "; + + for (ItemStack stack : items) { + String itemName = Objects.requireNonNull(BuiltInRegistries.ITEM.getKey(stack.getItem())).toString(); + + String withNBT = ""; + CompoundTag nbt = (CompoundTag) stack.save(lookup); + if (nbt.contains("components")) { + withNBT += nbt.get("components"); + } + + builder.append(tab).append("{").append(CommandUtils.NEW_LINE); + builder.append(tab).append(tab).append("item: ").append('"').append(itemName).append('"').append(",").append(CommandUtils.NEW_LINE); + if (!withNBT.isEmpty()) { + builder.append(tab).append(tab).append("nbt: ").append('"').append(withNBT).append('"').append(",").append(CommandUtils.NEW_LINE); + } + + if (stack != items.get(items.size() - 1)) { + builder.append(tab).append("},"); + } else { + builder.append(tab).append("}"); + } - StringBuilder clipboard = new StringBuilder(); - for (ItemStack stack : itemCollection) { - if (stack.isEmpty()) { - continue; + builder.append(CommandUtils.NEW_LINE); } - String itemName = Objects.requireNonNull(BuiltInRegistries.ITEM.getKey(stack.getItem())).toString(); + builder.append("]"); + return builder.toString(); + }), + KUBEJS_NATIVE(5,"kubejs_native", (items, lookup) -> { + StringBuilder builder = new StringBuilder(); + builder.append("[").append(CommandUtils.NEW_LINE); - String withNBT = ""; - CompoundTag nbt = (CompoundTag) stack.save(context.getSource().registryAccess()); - if (nbt.contains("components")) { - withNBT += nbt.get("components"); + final String tab = " "; + + for (ItemStack stack : items) { + String itemName = Objects.requireNonNull(BuiltInRegistries.ITEM.getKey(stack.getItem())).toString(); + + String withNBT = ""; + CompoundTag nbt = (CompoundTag) stack.save(lookup); + if (nbt.contains("components")) { + withNBT += nbt.get("components"); + } + + String itemString = String.format("%s%s", stack.getCount() > 1 ? stack.getCount() + "x " : "", itemName); + if (withNBT.isEmpty()) { + builder.append(tab).append("\"").append(itemString).append("\"").append(","); + } + + if (!withNBT.isEmpty()) { + builder.append(tab).append("Item.of(\"").append(itemName).append("\").withNbt(").append(withNBT).append("),"); + } + + builder.append(CommandUtils.NEW_LINE); } - clipboard.append(itemName).append(withNBT).append(CommandUtils.NEW_LINE); - } + builder.append("]"); + return builder.toString(); + }), + JSON(3, "json", (items, lookup) -> { + var output = items.stream().map(stack -> { + String itemName = Objects.requireNonNull(BuiltInRegistries.ITEM.getKey(stack.getItem())).toString(); - source.sendSuccess(() -> Component.translatable("commands.toolkit.clipboard.copied"), true); - Handler.CHANNEL.sendToPlayer(player, new SetCopy(clipboard.toString())); + CompoundTag nbt = (CompoundTag) stack.save(lookup); + if (nbt.contains("components")) { + return Map.of( + "item", itemName, + "nbt", Objects.requireNonNull(nbt.get("components")) + ); + } - return 1; + return Map.of("item", itemName); + }).toList(); + + return new Gson().newBuilder().setPrettyPrinting().create().toJson(output); + }), + SNBT(2, "snbt", (items, lookup) -> { + CompoundTag tag = new CompoundTag(); + ListTag list = new ListTag(); + for (ItemStack stack : items) { + list.add(stack.save(lookup)); + } + + tag.put("items", list); + return (new SnbtPrinterTagVisitor()).visit(tag); + }), + NBT(1, "nbt", (items, lookup) -> { + CompoundTag tag = new CompoundTag(); + ListTag list = new ListTag(); + for (ItemStack stack : items) { + list.add(stack.save(lookup)); + } + + tag.put("items", list); + return tag.toString(); + }), + /** + * ZenScript compatible list + */ + CRAFTTWEAKER(5, "crafttweaker", (items, lookup) -> { + StringBuilder builder = new StringBuilder(); + builder.append("[").append(CommandUtils.NEW_LINE); + + for (ItemStack stack : items) { + String itemName = Objects.requireNonNull(BuiltInRegistries.ITEM.getKey(stack.getItem())).toString(); + + String withNBT = ""; + CompoundTag nbt = (CompoundTag) stack.save(lookup); + if (nbt.contains("components")) { + withNBT += nbt.get("components"); + } + + builder.append(" ").append(""); + if (!withNBT.isEmpty()) { + builder.append(".withTag(").append(withNBT).append(")"); + } + builder.append(",").append(CommandUtils.NEW_LINE); + } + + builder.append("]").append(CommandUtils.NEW_LINE); + return builder.toString(); + }), + PLAIN(0,"plain", (items, lookup) -> { + StringBuilder builder = new StringBuilder(); + + for (ItemStack stack : items) { + String itemName = Objects.requireNonNull(BuiltInRegistries.ITEM.getKey(stack.getItem())).toString(); + + String withNBT = ""; + CompoundTag nbt = (CompoundTag) stack.save(lookup); + if (nbt.contains("components")) { + withNBT += nbt.get("components"); + } + + builder.append(itemName).append(withNBT).append(CommandUtils.NEW_LINE); + } + + return builder.toString(); + }), + CSV(6, "csv", (items, lookup) -> { + StringBuilder builder = new StringBuilder(); + + // Header + builder.append("item,nbt").append(CommandUtils.NEW_LINE); + + for (ItemStack stack : items) { + String itemName = Objects.requireNonNull(BuiltInRegistries.ITEM.getKey(stack.getItem())).toString(); + + String withNBT = ""; + CompoundTag nbt = (CompoundTag) stack.save(lookup); + if (nbt.contains("components")) { + withNBT += nbt.get("components"); + } + + builder.append(itemName).append(",").append(withNBT).append(CommandUtils.NEW_LINE); + } + + return builder.toString(); + }); + + final int order; + final String name; + final BiFunction, HolderLookup.Provider, String> function; + + OutputType(int order, String name, BiFunction, HolderLookup.Provider, String> function) { + this.order = order; + this.name = name; + this.function = function; + } } } diff --git a/common/src/main/java/com/sunekaer/toolkit/commands/inventory/PrintCommand.java b/common/src/main/java/com/sunekaer/toolkit/commands/inventory/PrintCommand.java index 492a93d..4c003fc 100644 --- a/common/src/main/java/com/sunekaer/toolkit/commands/inventory/PrintCommand.java +++ b/common/src/main/java/com/sunekaer/toolkit/commands/inventory/PrintCommand.java @@ -6,6 +6,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.sunekaer.toolkit.network.Handler; import com.sunekaer.toolkit.network.SetCopy; +import dev.architectury.networking.NetworkManager; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; @@ -63,7 +64,7 @@ private static int print(CommandContext context, String type .withColor(ChatFormatting.YELLOW)), false); if (copyOnReply) { - Handler.CHANNEL.sendToPlayer(player, new SetCopy(combinedItemNBT)); + NetworkManager.sendToPlayer(player, new SetCopy(combinedItemNBT)); } if (tags.isEmpty()) { diff --git a/common/src/main/java/com/sunekaer/toolkit/network/Handler.java b/common/src/main/java/com/sunekaer/toolkit/network/Handler.java index f13072b..b50d9cf 100644 --- a/common/src/main/java/com/sunekaer/toolkit/network/Handler.java +++ b/common/src/main/java/com/sunekaer/toolkit/network/Handler.java @@ -1,13 +1,16 @@ package com.sunekaer.toolkit.network; -import com.sunekaer.toolkit.Toolkit; -import dev.architectury.networking.NetworkChannel; -import net.minecraft.resources.ResourceLocation; +import dev.architectury.networking.NetworkManager; +import dev.architectury.platform.Platform; +import dev.architectury.utils.Env; public class Handler { - public static final NetworkChannel CHANNEL = NetworkChannel.create(ResourceLocation.fromNamespaceAndPath(Toolkit.MOD_ID, "networking_channel")); public static void init() { - CHANNEL.register(SetCopy.class, SetCopy::encode, SetCopy::new, SetCopy::apply); + if (Platform.getEnvironment() == Env.CLIENT) { + NetworkManager.registerReceiver(NetworkManager.s2c(), SetCopy.TYPE, SetCopy.CODEC, SetCopy::handle); + } else { + NetworkManager.registerS2CPayloadType(SetCopy.TYPE, SetCopy.CODEC); + } } } diff --git a/common/src/main/java/com/sunekaer/toolkit/network/SetCopy.java b/common/src/main/java/com/sunekaer/toolkit/network/SetCopy.java index 2aee845..390f8a5 100644 --- a/common/src/main/java/com/sunekaer/toolkit/network/SetCopy.java +++ b/common/src/main/java/com/sunekaer/toolkit/network/SetCopy.java @@ -1,31 +1,32 @@ package com.sunekaer.toolkit.network; +import com.sunekaer.toolkit.Toolkit; import dev.architectury.networking.NetworkManager; +import io.netty.buffer.ByteBuf; import net.minecraft.client.Minecraft; -import net.minecraft.network.FriendlyByteBuf; - -import java.util.function.Supplier; - -public class SetCopy { - public final String toCopy; - - public SetCopy(FriendlyByteBuf buf) { - // Decode data into a message - this.toCopy = buf.readUtf(); - } - - public SetCopy(String toCopy) { - this.toCopy = toCopy; - } - - public void encode(FriendlyByteBuf buf) { - buf.writeUtf(toCopy); +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record SetCopy(String toCopy) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Toolkit.MOD_ID, "set_copy")); + + public static final StreamCodec CODEC = StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, + SetCopy::toCopy, + SetCopy::new + ); + + public static void handle(SetCopy message, NetworkManager.PacketContext context) { + context.queue(() -> + Minecraft.getInstance().keyboardHandler.setClipboard(message.toCopy) + ); } - public void apply(Supplier context) { - context.get().queue(() -> - Minecraft.getInstance().keyboardHandler.setClipboard(toCopy) - ); + @Override + public Type type() { + return TYPE; } } diff --git a/gradle.properties b/gradle.properties index 046a6c0..f43c266 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,19 +5,17 @@ minecraft_version=1.21 enabled_platforms=fabric,neoforge archives_base_name=ToolKit -mod_version=87.0.0 +mod_version=87.0.1 maven_group=com.sunekaer.mods -architectury_version=13.0.1 +architectury_version=13.0.3 fabric_loader_version=0.15.11 -fabric_api_version=0.100.3+1.21 +fabric_api_version=0.100.6+1.21 -forge_version=50.0.9 -forge_gradle_version=6.0.20 +forge_version=51.0.23 -neoforge_version=21.0.19-beta -neoforge_gradle_version=7.0.145 +neoforge_version=21.0.78-beta curseforge_id=324888 modrinth_id=fWj0tJp5