From 42b21a4ab8f2da01d907ca179f4f7a22a2ee3933 Mon Sep 17 00:00:00 2001 From: AnnotationVisitor <231545974+AnnotationVisitor@users.noreply.github.com> Date: Wed, 12 Nov 2025 05:31:31 +0000 Subject: [PATCH 1/4] Use book packet for both methods item stack swap technique is dumb af and outright dangerous --- .../org/bukkit/craftbukkit/entity/CraftPlayer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 94307a5ffce0..98a8ec949c00 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -2959,22 +2959,22 @@ public void openBook(ItemStack book) { Preconditions.checkArgument(book != null, "ItemStack cannot be null"); Preconditions.checkArgument(book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT), "ItemStack must have a 'written_book_content' component"); - final ItemStack previousItem = this.getInventory().getItemInMainHand(); - this.getInventory().setItemInMainHand(book); - this.getHandle().openItemGui(CraftItemStack.asNMSCopy(book), InteractionHand.MAIN_HAND); - this.getInventory().setItemInMainHand(previousItem); + sendBookOpen(book); } @Override public void openBook(final Book book) { final ItemStack mutatedItem = ItemType.WRITTEN_BOOK.createItemStack(); // dummy item mutatedItem.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, WrittenBookContent.writtenBookContent("", "").addPages(book.pages())); + sendBookOpen(mutatedItem); + } + private void sendBookOpen(ItemStack bookItem) { final net.minecraft.world.item.ItemStack selectedItem = this.getHandle().getInventory().getSelectedItem(); final int slot = this.getHandle().getInventory().getSelectedSlot(); this.getHandle().connection.send(new ClientboundBundlePacket( List.of( - new ClientboundSetPlayerInventoryPacket(slot, CraftItemStack.unwrap(mutatedItem)), + new ClientboundSetPlayerInventoryPacket(slot, CraftItemStack.unwrap(bookItem)), new ClientboundOpenBookPacket(InteractionHand.MAIN_HAND), new ClientboundSetPlayerInventoryPacket(slot, selectedItem) ) From 1cf0758eac5cb46263f6d7d9d4c8cc462d964700 Mon Sep 17 00:00:00 2001 From: AnnotationVisitor <231545974+AnnotationVisitor@users.noreply.github.com> Date: Wed, 12 Nov 2025 05:44:00 +0000 Subject: [PATCH 2/4] resolve book itemstack aswell to prevent any behaviour change --- .../org/bukkit/craftbukkit/entity/CraftPlayer.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 98a8ec949c00..d957581932ab 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -2958,23 +2958,26 @@ public void stopSound(final net.kyori.adventure.sound.SoundStop stop) { public void openBook(ItemStack book) { Preconditions.checkArgument(book != null, "ItemStack cannot be null"); Preconditions.checkArgument(book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT), "ItemStack must have a 'written_book_content' component"); - - sendBookOpen(book); + + net.minecraft.world.item.ItemStack bookItem = CraftItemStack.unwrap(book); + ServerPlayer serverPlayer = this.getHandle(); + net.minecraft.world.item.component.WrittenBookContent.resolveForItem(bookItem, serverPlayer.createCommandSourceStack(), serverPlayer); + sendBookOpen(bookItem); } @Override public void openBook(final Book book) { final ItemStack mutatedItem = ItemType.WRITTEN_BOOK.createItemStack(); // dummy item mutatedItem.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, WrittenBookContent.writtenBookContent("", "").addPages(book.pages())); - sendBookOpen(mutatedItem); + sendBookOpen(CraftItemStack.unwrap(mutatedItem)); } - private void sendBookOpen(ItemStack bookItem) { + private void sendBookOpen(net.minecraft.world.item.ItemStack bookItem) { final net.minecraft.world.item.ItemStack selectedItem = this.getHandle().getInventory().getSelectedItem(); final int slot = this.getHandle().getInventory().getSelectedSlot(); this.getHandle().connection.send(new ClientboundBundlePacket( List.of( - new ClientboundSetPlayerInventoryPacket(slot, CraftItemStack.unwrap(bookItem)), + new ClientboundSetPlayerInventoryPacket(slot, bookItem), new ClientboundOpenBookPacket(InteractionHand.MAIN_HAND), new ClientboundSetPlayerInventoryPacket(slot, selectedItem) ) From dc9247d87d53cc4085e9638aaf9321b82268d6da Mon Sep 17 00:00:00 2001 From: AnnotationVisitor <231545974+AnnotationVisitor@users.noreply.github.com> Date: Wed, 12 Nov 2025 05:45:34 +0000 Subject: [PATCH 3/4] remove space --- .../src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index d957581932ab..d9a542f35cb6 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -2958,7 +2958,6 @@ public void stopSound(final net.kyori.adventure.sound.SoundStop stop) { public void openBook(ItemStack book) { Preconditions.checkArgument(book != null, "ItemStack cannot be null"); Preconditions.checkArgument(book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT), "ItemStack must have a 'written_book_content' component"); - net.minecraft.world.item.ItemStack bookItem = CraftItemStack.unwrap(book); ServerPlayer serverPlayer = this.getHandle(); net.minecraft.world.item.component.WrittenBookContent.resolveForItem(bookItem, serverPlayer.createCommandSourceStack(), serverPlayer); From 1c5f175f2b3f8947943e5a47654020e41e4ff856 Mon Sep 17 00:00:00 2001 From: AnnotationVisitor <231545974+AnnotationVisitor@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:57:42 +0000 Subject: [PATCH 4/4] use clone --- .../main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index d9a542f35cb6..01912115641e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -2958,7 +2958,8 @@ public void stopSound(final net.kyori.adventure.sound.SoundStop stop) { public void openBook(ItemStack book) { Preconditions.checkArgument(book != null, "ItemStack cannot be null"); Preconditions.checkArgument(book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT), "ItemStack must have a 'written_book_content' component"); - net.minecraft.world.item.ItemStack bookItem = CraftItemStack.unwrap(book); + + net.minecraft.world.item.ItemStack bookItem = CraftItemStack.asNMSCopy(book); ServerPlayer serverPlayer = this.getHandle(); net.minecraft.world.item.component.WrittenBookContent.resolveForItem(bookItem, serverPlayer.createCommandSourceStack(), serverPlayer); sendBookOpen(bookItem);