Skip to content

Commit 97452e1

Browse files
authored
Improvements for Player#openBook (#12582)
1 parent cf02a07 commit 97452e1

File tree

3 files changed

+36
-52
lines changed

3 files changed

+36
-52
lines changed

paper-api/src/main/java/org/bukkit/entity/Player.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3490,11 +3490,12 @@ default void setNoTickViewDistance(int viewDistance) {
34903490
public void updateCommands();
34913491

34923492
/**
3493-
* Open a {@link Material#WRITTEN_BOOK} for a Player
3493+
* Open an ItemStack with {@link io.papermc.paper.datacomponent.DataComponentTypes#WRITTEN_BOOK_CONTENT} for a Player
34943494
*
3495-
* @param book The book to open for this player
3495+
* @param book the item with written book content to open for this player
3496+
* @throws IllegalArgumentException if the ItemStack is null, empty or doesn't have a {@link io.papermc.paper.datacomponent.DataComponentTypes#WRITTEN_BOOK_CONTENT}
34963497
*/
3497-
public void openBook(ItemStack book);
3498+
void openBook(ItemStack book);
34983499

34993500
/**
35003501
* Open a Sign for editing by the Player.

paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import java.util.regex.Pattern;
1818
import java.util.stream.StreamSupport;
1919
import net.kyori.adventure.bossbar.BossBar;
20-
import net.kyori.adventure.inventory.Book;
2120
import net.kyori.adventure.key.Key;
2221
import net.kyori.adventure.nbt.api.BinaryTagHolder;
2322
import net.kyori.adventure.sound.Sound;
@@ -41,7 +40,6 @@
4140
import net.minecraft.core.Holder;
4241
import net.minecraft.core.component.DataComponentPatch;
4342
import net.minecraft.core.component.DataComponentType;
44-
import net.minecraft.core.component.DataComponents;
4543
import net.minecraft.core.registries.BuiltInRegistries;
4644
import net.minecraft.locale.Language;
4745
import net.minecraft.nbt.CompoundTag;
@@ -55,13 +53,10 @@
5553
import net.minecraft.resources.RegistryOps;
5654
import net.minecraft.resources.ResourceKey;
5755
import net.minecraft.resources.ResourceLocation;
58-
import net.minecraft.server.network.Filterable;
5956
import net.minecraft.sounds.SoundEvent;
6057
import net.minecraft.sounds.SoundSource;
6158
import net.minecraft.world.BossEvent;
6259
import net.minecraft.world.entity.Entity;
63-
import net.minecraft.world.item.ItemStack;
64-
import net.minecraft.world.item.component.WrittenBookContent;
6560
import org.bukkit.command.CommandSender;
6661
import org.bukkit.craftbukkit.CraftRegistry;
6762
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
@@ -333,28 +328,6 @@ public static void setFlag(final BossBar bar, final BossBar.Flag flag, final boo
333328
}
334329
}
335330

336-
// Book
337-
338-
public static ItemStack asItemStack(final Book book, final Locale locale) {
339-
final ItemStack item = new ItemStack(net.minecraft.world.item.Items.WRITTEN_BOOK, 1);
340-
item.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(
341-
Filterable.passThrough(validateField(asPlain(book.title(), locale), WrittenBookContent.TITLE_MAX_LENGTH, "title")),
342-
asPlain(book.author(), locale),
343-
0,
344-
book.pages().stream().map(c -> Filterable.passThrough(PaperAdventure.asVanilla(c))).toList(), // TODO should we validate length?
345-
false
346-
));
347-
return item;
348-
}
349-
350-
private static String validateField(final String content, final int length, final String name) {
351-
final int actual = content.length();
352-
if (actual > length) {
353-
throw new IllegalArgumentException("Field '" + name + "' has a maximum length of " + length + " but was passed '" + content + "', which was " + actual + " characters long.");
354-
}
355-
return content;
356-
}
357-
358331
// Sounds
359332

360333
public static SoundSource asVanilla(final Sound.Source source) {

paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import io.papermc.paper.FeatureHooks;
1212
import io.papermc.paper.connection.PlayerGameConnection;
1313
import io.papermc.paper.connection.PluginMessageBridgeImpl;
14+
import io.papermc.paper.datacomponent.DataComponentTypes;
15+
import io.papermc.paper.datacomponent.item.WrittenBookContent;
1416
import io.papermc.paper.dialog.Dialog;
1517
import io.papermc.paper.dialog.PaperDialog;
1618
import io.papermc.paper.entity.LookAnchor;
@@ -48,6 +50,7 @@
4850
import java.util.stream.Collectors;
4951
import net.kyori.adventure.dialog.DialogLike;
5052
import net.kyori.adventure.identity.Identity;
53+
import net.kyori.adventure.inventory.Book;
5154
import net.kyori.adventure.pointer.PointersSupplier;
5255
import net.kyori.adventure.util.TriState;
5356
import net.md_5.bungee.api.chat.BaseComponent;
@@ -69,13 +72,15 @@
6972
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
7073
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
7174
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
75+
import net.minecraft.network.protocol.game.ClientboundBundlePacket;
7276
import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket;
7377
import net.minecraft.network.protocol.game.ClientboundCustomChatCompletionsPacket;
7478
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
7579
import net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket;
7680
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
7781
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
7882
import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
83+
import net.minecraft.network.protocol.game.ClientboundOpenBookPacket;
7984
import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket;
8085
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
8186
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
@@ -90,6 +95,7 @@
9095
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
9196
import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket;
9297
import net.minecraft.network.protocol.game.ClientboundSetHealthPacket;
98+
import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket;
9399
import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket;
94100
import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket;
95101
import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket;
@@ -108,6 +114,7 @@
108114
import net.minecraft.server.players.UserWhiteListEntry;
109115
import net.minecraft.sounds.SoundEvent;
110116
import net.minecraft.util.ProblemReporter;
117+
import net.minecraft.world.InteractionHand;
111118
import net.minecraft.world.entity.Entity;
112119
import net.minecraft.world.entity.EntitySpawnReason;
113120
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
@@ -201,6 +208,7 @@
201208
import org.bukkit.inventory.EquipmentSlot;
202209
import org.bukkit.inventory.InventoryView.Property;
203210
import org.bukkit.inventory.ItemStack;
211+
import org.bukkit.inventory.ItemType;
204212
import org.bukkit.map.MapCursor;
205213
import org.bukkit.map.MapView;
206214
import org.bukkit.metadata.MetadataValue;
@@ -2709,17 +2717,6 @@ public void updateCommands() {
27092717
this.server.getServer().getCommands().sendCommands(this.getHandle());
27102718
}
27112719

2712-
@Override
2713-
public void openBook(ItemStack book) {
2714-
Preconditions.checkArgument(book != null, "ItemStack cannot be null");
2715-
Preconditions.checkArgument(book.getType() == Material.WRITTEN_BOOK, "ItemStack Material (%s) must be Material.WRITTEN_BOOK", book.getType());
2716-
2717-
ItemStack hand = this.getInventory().getItemInMainHand();
2718-
this.getInventory().setItemInMainHand(book);
2719-
this.getHandle().openItemGui(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(book), net.minecraft.world.InteractionHand.MAIN_HAND);
2720-
this.getInventory().setItemInMainHand(hand);
2721-
}
2722-
27232720
@Override
27242721
public void openSign(@NonNull Sign sign, @NonNull Side side) {
27252722
CraftSign.openSign(sign, this, side);
@@ -2958,17 +2955,30 @@ public void stopSound(final net.kyori.adventure.sound.SoundStop stop) {
29582955
}
29592956

29602957
@Override
2961-
public void openBook(final net.kyori.adventure.inventory.Book book) {
2962-
final java.util.Locale locale = this.getHandle().adventure$locale;
2963-
final net.minecraft.world.item.ItemStack item = io.papermc.paper.adventure.PaperAdventure.asItemStack(book, locale);
2964-
final ServerPlayer player = this.getHandle();
2965-
final ServerGamePacketListenerImpl connection = player.connection;
2966-
final net.minecraft.world.entity.player.Inventory inventory = player.getInventory();
2967-
final int slot = inventory.getNonEquipmentItems().size() + inventory.getSelectedSlot();
2968-
final int stateId = getHandle().containerMenu.getStateId();
2969-
connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(0, stateId, slot, item));
2970-
connection.send(new net.minecraft.network.protocol.game.ClientboundOpenBookPacket(net.minecraft.world.InteractionHand.MAIN_HAND));
2971-
connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(0, stateId, slot, inventory.getSelectedItem()));
2958+
public void openBook(ItemStack book) {
2959+
Preconditions.checkArgument(book != null, "ItemStack cannot be null");
2960+
Preconditions.checkArgument(book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT), "ItemStack must have a 'written_book_content' component");
2961+
2962+
final ItemStack previousItem = this.getInventory().getItemInMainHand();
2963+
this.getInventory().setItemInMainHand(book);
2964+
this.getHandle().openItemGui(CraftItemStack.asNMSCopy(book), InteractionHand.MAIN_HAND);
2965+
this.getInventory().setItemInMainHand(previousItem);
2966+
}
2967+
2968+
@Override
2969+
public void openBook(final Book book) {
2970+
final ItemStack mutatedItem = ItemType.WRITTEN_BOOK.createItemStack(); // dummy item
2971+
mutatedItem.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, WrittenBookContent.writtenBookContent("", "").addPages(book.pages()));
2972+
2973+
final net.minecraft.world.item.ItemStack selectedItem = this.getHandle().getInventory().getSelectedItem();
2974+
final int slot = this.getHandle().getInventory().getSelectedSlot();
2975+
this.getHandle().connection.send(new ClientboundBundlePacket(
2976+
List.of(
2977+
new ClientboundSetPlayerInventoryPacket(slot, CraftItemStack.unwrap(mutatedItem)),
2978+
new ClientboundOpenBookPacket(InteractionHand.MAIN_HAND),
2979+
new ClientboundSetPlayerInventoryPacket(slot, selectedItem)
2980+
)
2981+
));
29722982
}
29732983

29742984
@Override

0 commit comments

Comments
 (0)