diff --git a/src/main/java/net/earthcomputer/clientcommands/command/BookCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/BookCommand.java index 71738c8a..4dad4602 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/BookCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/BookCommand.java @@ -16,6 +16,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.component.WritableBookContent; +import net.minecraft.world.item.component.WrittenBookContent; import java.util.ArrayList; import java.util.List; @@ -26,35 +27,40 @@ import static com.mojang.brigadier.arguments.IntegerArgumentType.*; import static com.mojang.brigadier.arguments.LongArgumentType.*; +import static com.mojang.brigadier.arguments.StringArgumentType.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommands.*; public class BookCommand { private static final SimpleCommandExceptionType NO_BOOK = new SimpleCommandExceptionType(Component.translatable("commands.cbook.commandException")); + private static final SimpleCommandExceptionType TITLE_TOO_LONG = new SimpleCommandExceptionType(Component.translatable("commands.cbook.titleTooLong", WrittenBookContent.TITLE_MAX_LENGTH)); private static final int DEFAULT_LIMIT = 50; public static void register(CommandDispatcher dispatcher) { - if (MultiVersionCompat.INSTANCE.getProtocolVersion() >= MultiVersionCompat.V1_15) { - return; // chunk savestate fixed in 1.15 - } - dispatcher.register(literal("cbook") - .then(literal("fill") - .executes(ctx -> fillBook(ctx.getSource(), fill(), DEFAULT_LIMIT)) - .then(argument("limit", integer(0, getMaxLimit())) - .executes(ctx -> fillBook(ctx.getSource(), fill(), getInteger(ctx, "limit"))))) - .then(literal("random") - .executes(ctx -> fillBook(ctx.getSource(), random(new Random()), DEFAULT_LIMIT)) - .then(argument("limit", integer(0, getMaxLimit())) - .executes(ctx -> fillBook(ctx.getSource(), random(new Random()), getInteger(ctx, "limit"))) - .then(argument("seed", longArg()) - .executes(ctx -> fillBook(ctx.getSource(), random(new Random(getLong(ctx, "seed"))), getInteger(ctx, "limit")))))) - .then(literal("ascii") - .executes(ctx -> fillBook(ctx.getSource(), ascii(new Random()), DEFAULT_LIMIT)) - .then(argument("limit", integer(0, getMaxLimit())) - .executes(ctx -> fillBook(ctx.getSource(), ascii(new Random()), getInteger(ctx, "limit"))) - .then(argument("seed", longArg()) - .executes(ctx -> fillBook(ctx.getSource(), ascii(new Random(getLong(ctx, "seed"))), getInteger(ctx, "limit"))))))); + .then(literal("sign") + .executes(ctx -> signBook(ctx.getSource(), "")) + .then(argument("title", greedyString()) + .executes(ctx -> signBook(ctx.getSource(), getString(ctx, "title"))))) + .then(literal("fill") + .requires(_ -> MultiVersionCompat.INSTANCE.getProtocolVersion() < MultiVersionCompat.V1_15) + .executes(ctx -> fillBook(ctx.getSource(), fill(), DEFAULT_LIMIT)) + .then(argument("limit", integer(0, getMaxLimit())) + .executes(ctx -> fillBook(ctx.getSource(), fill(), getInteger(ctx, "limit"))))) + .then(literal("random") + .requires(_ -> MultiVersionCompat.INSTANCE.getProtocolVersion() < MultiVersionCompat.V1_15) + .executes(ctx -> fillBook(ctx.getSource(), random(new Random()), DEFAULT_LIMIT)) + .then(argument("limit", integer(0, getMaxLimit())) + .executes(ctx -> fillBook(ctx.getSource(), random(new Random()), getInteger(ctx, "limit"))) + .then(argument("seed", longArg()) + .executes(ctx -> fillBook(ctx.getSource(), random(new Random(getLong(ctx, "seed"))), getInteger(ctx, "limit")))))) + .then(literal("ascii") + .requires(_ -> MultiVersionCompat.INSTANCE.getProtocolVersion() < MultiVersionCompat.V1_15) + .executes(ctx -> fillBook(ctx.getSource(), ascii(new Random()), DEFAULT_LIMIT)) + .then(argument("limit", integer(0, getMaxLimit())) + .executes(ctx -> fillBook(ctx.getSource(), ascii(new Random()), getInteger(ctx, "limit"))) + .then(argument("seed", longArg()) + .executes(ctx -> fillBook(ctx.getSource(), ascii(new Random(getLong(ctx, "seed"))), getInteger(ctx, "limit"))))))); } private static IntStream fill() { @@ -71,20 +77,12 @@ private static IntStream ascii(Random rand) { private static int fillBook(FabricClientCommandSource source, IntStream characterGenerator, int limit) throws CommandSyntaxException { LocalPlayer player = source.getPlayer(); - assert player != null; - ItemStack heldItem = player.getMainHandItem(); - InteractionHand hand = InteractionHand.MAIN_HAND; - if (heldItem.getItem() != Items.WRITABLE_BOOK) { - heldItem = player.getOffhandItem(); - hand = InteractionHand.OFF_HAND; - if (heldItem.getItem() != Items.WRITABLE_BOOK) { - throw NO_BOOK.create(); - } - } - int slot = hand == InteractionHand.MAIN_HAND ? player.getInventory().getSelectedSlot() : Inventory.SLOT_OFFHAND; + HeldBook book = getHeldWritableBook(player); - String joinedPages = characterGenerator.limit((long) getMaxLimit() * 210).mapToObj(i -> String.valueOf((char) i)).collect(Collectors.joining()); + String joinedPages = characterGenerator.limit((long) getMaxLimit() * 210) + .mapToObj(i -> String.valueOf((char) i)) + .collect(Collectors.joining()); List pages = new ArrayList<>(limit); List> filterablePages = new ArrayList<>(); @@ -95,15 +93,49 @@ private static int fillBook(FabricClientCommandSource source, IntStream characte filterablePages.add(Filterable.passThrough(page)); } - heldItem.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(filterablePages)); - player.connection.send(new ServerboundEditBookPacket(slot, pages, Optional.empty())); + book.stack().set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(filterablePages)); + player.connection.send(new ServerboundEditBookPacket(book.slot(), pages, Optional.empty())); + + source.sendFeedback(Component.translatable("commands.cbook.success")); + + return Command.SINGLE_SUCCESS; + } + + private static int signBook(FabricClientCommandSource source, String title) throws CommandSyntaxException { + LocalPlayer player = source.getPlayer(); + + if (title.length() > WrittenBookContent.TITLE_MAX_LENGTH) { + throw TITLE_TOO_LONG.create(); + } + + HeldBook book = getHeldWritableBook(player); + WritableBookContent content = book.stack().get(DataComponents.WRITABLE_BOOK_CONTENT); + List pages = content == null ? List.of() : content.getPages(false).toList(); + player.connection.send(new ServerboundEditBookPacket(book.slot(), pages, Optional.of(title))); source.sendFeedback(Component.translatable("commands.cbook.success")); return Command.SINGLE_SUCCESS; } + private static HeldBook getHeldWritableBook(LocalPlayer player) throws CommandSyntaxException { + ItemStack heldItem = player.getMainHandItem(); + InteractionHand hand = InteractionHand.MAIN_HAND; + if (heldItem.getItem() != Items.WRITABLE_BOOK) { + heldItem = player.getOffhandItem(); + hand = InteractionHand.OFF_HAND; + if (heldItem.getItem() != Items.WRITABLE_BOOK) { + throw NO_BOOK.create(); + } + } + int slot = hand == InteractionHand.MAIN_HAND ? player.getInventory().getSelectedSlot() : Inventory.SLOT_OFFHAND; + return new HeldBook(heldItem, slot); + } + private static int getMaxLimit() { return MultiVersionCompat.INSTANCE.getProtocolVersion() < MultiVersionCompat.V1_14 ? 50 : 100; } + + private record HeldBook(ItemStack stack, int slot) { + } } diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index a08fcf14..71bc76bb 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -64,6 +64,7 @@ "commands.cbook.commandException": "You are not holding a book", "commands.cbook.success": "Successfully edited book", + "commands.cbook.titleTooLong": "Book title cannot be longer than %s characters", "commands.cbuildinfo.success": "Running clientcommands on version %s (%s@{%s})",