Skip to content

Commit

Permalink
feat: new copy command
Browse files Browse the repository at this point in the history
fix: crashes on neoforge
  • Loading branch information
MichaelHillcox committed Jul 10, 2024
1 parent 0c15128 commit 1bbd12d
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 62 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<CommandSourceStack, ?> 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<CommandSourceStack> context) throws CommandSyntaxException {
private static CompletableFuture<Suggestions> outputTypeSuggestions(CommandContext<CommandSourceStack> 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<CommandSourceStack> 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("<item:").append(itemName).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<List<ItemStack>, HolderLookup.Provider, String> function;

OutputType(int order, String name, BiFunction<List<ItemStack>, HolderLookup.Provider, String> function) {
this.order = order;
this.name = name;
this.function = function;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -63,7 +64,7 @@ private static int print(CommandContext<CommandSourceStack> 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()) {
Expand Down
13 changes: 8 additions & 5 deletions common/src/main/java/com/sunekaer/toolkit/network/Handler.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading

0 comments on commit 1bbd12d

Please sign in to comment.