diff --git a/src/main/java/net/minestom/server/adventure/AdventureUtils.java b/src/main/java/net/minestom/server/adventure/AdventureUtils.java new file mode 100644 index 000000000..e39ed046b --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/AdventureUtils.java @@ -0,0 +1,29 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.key.Key; +import net.minestom.server.sound.Sound; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Adventure related utilities. + */ +public class AdventureUtils { + private static final Map SOUND_MAP = + Arrays.stream(Sound.values()).collect(Collectors.toMap(Sound::getNamespaceID, sound -> sound)); + + /** + * Attempts to get an NMS sound from an Adventure key. + * + * @param name the key + * + * @return the sound, if found + */ + public static @Nullable Sound asSound(@NotNull Key name) { + return SOUND_MAP.get(name.asString()); + } +} diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 2b33dd512..0bbe81b33 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -5,14 +5,13 @@ import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.sound.SoundStop; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ComponentLike; -import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.title.Title; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; import net.minestom.server.attribute.Attribute; +import net.minestom.server.adventure.AdventureUtils; import net.minestom.server.attribute.AttributeInstance; import net.minestom.server.bossbar.BossBar; import net.minestom.server.chat.ChatParser; @@ -845,12 +844,13 @@ public class Player extends LivingEntity implements CommandSender { * @param z the effect Z * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ - public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, - int x, int y, int z, float volume, float pitch) { + @Deprecated + public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { SoundEffectPacket soundEffectPacket = new SoundEffectPacket(); soundEffectPacket.soundId = sound.getId(); - soundEffectPacket.soundCategory = soundCategory; + soundEffectPacket.soundCategory = soundCategory.ordinal(); soundEffectPacket.x = x; soundEffectPacket.y = y; soundEffectPacket.z = z; @@ -863,9 +863,10 @@ public class Player extends LivingEntity implements CommandSender { * Plays a sound from the {@link Sound} enum. * * @see #playSound(Sound, SoundCategory, int, int, int, float, float) + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ - public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, - BlockPosition position, float volume, float pitch) { + @Deprecated + public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, BlockPosition position, float volume, float pitch) { playSound(sound, soundCategory, position.getX(), position.getY(), position.getZ(), volume, pitch); } @@ -879,12 +880,13 @@ public class Player extends LivingEntity implements CommandSender { * @param z the effect Z * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ - public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, - int x, int y, int z, float volume, float pitch) { + @Deprecated + public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { NamedSoundEffectPacket namedSoundEffectPacket = new NamedSoundEffectPacket(); namedSoundEffectPacket.soundName = identifier; - namedSoundEffectPacket.soundCategory = soundCategory; + namedSoundEffectPacket.soundCategory = soundCategory.ordinal(); namedSoundEffectPacket.x = x; namedSoundEffectPacket.y = y; namedSoundEffectPacket.z = z; @@ -897,7 +899,9 @@ public class Player extends LivingEntity implements CommandSender { * Plays a sound from an identifier (represents a custom sound in a resource pack). * * @see #playSound(String, SoundCategory, int, int, int, float, float) + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ + @Deprecated public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, BlockPosition position, float volume, float pitch) { playSound(identifier, soundCategory, position.getX(), position.getY(), position.getZ(), volume, pitch); } @@ -909,7 +913,9 @@ public class Player extends LivingEntity implements CommandSender { * @param soundCategory the sound category * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound)} */ + @Deprecated public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, float volume, float pitch) { EntitySoundEffectPacket entitySoundEffectPacket = new EntitySoundEffectPacket(); entitySoundEffectPacket.entityId = getEntityId(); @@ -920,6 +926,56 @@ public class Player extends LivingEntity implements CommandSender { playerConnection.sendPacket(entitySoundEffectPacket); } + @Override + public void playSound(net.kyori.adventure.sound.@NonNull Sound sound) { + this.playSound(sound, this.position.getX(), this.position.getY(), this.position.getZ()); + } + + @Override + public void playSound(net.kyori.adventure.sound.@NonNull Sound sound, double x, double y, double z) { + Sound minestomSound = AdventureUtils.asSound(sound.name()); + + if (minestomSound == null) { + NamedSoundEffectPacket packet = new NamedSoundEffectPacket(); + packet.soundName = sound.name().asString(); + packet.soundCategory = sound.source().ordinal(); + packet.x = (int) x; + packet.y = (int) y; + packet.z = (int) z; + packet.volume = sound.volume(); + packet.pitch = sound.pitch(); + playerConnection.sendPacket(packet); + } else { + SoundEffectPacket packet = new SoundEffectPacket(); + packet.soundId = minestomSound.getId(); + packet.soundCategory = sound.source().ordinal(); + packet.x = (int) x; + packet.y = (int) y; + packet.z = (int) z; + packet.volume = sound.volume(); + packet.pitch = sound.pitch(); + playerConnection.sendPacket(packet); + } + } + + @Override + public void stopSound(@NonNull SoundStop stop) { + StopSoundPacket packet = new StopSoundPacket(); + packet.flags = 0x0; + + if (stop.source() != null) { + packet.flags |= 0x1; + packet.source = stop.source().ordinal(); + } + + if (stop.sound() != null) { + packet.flags |= 0x2; + packet.sound = stop.sound().asString(); + } + + this.playerConnection.sendPacket(packet); + } + /** * Plays a given effect at the given position for this player. * @@ -941,7 +997,9 @@ public class Player extends LivingEntity implements CommandSender { /** * Sends a {@link StopSoundPacket} packet. + * @deprecated Use {@link #stopSound(SoundStop)} with {@link SoundStop#all()} */ + @Deprecated public void stopSound() { StopSoundPacket stopSoundPacket = new StopSoundPacket(); stopSoundPacket.flags = 0x00; @@ -1129,7 +1187,28 @@ public class Player extends LivingEntity implements CommandSender { @Override public void openBook(@NonNull Book book) { - // TODO write the book + // make the book + ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1); + writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book)); + + // Set book in offhand + SetSlotPacket setBookPacket = new SetSlotPacket(); + setBookPacket.windowId = 0; + setBookPacket.slot = 45; + setBookPacket.itemStack = writtenBook; + playerConnection.sendPacket(setBookPacket); + + // Open the book + OpenBookPacket openBookPacket = new OpenBookPacket(); + openBookPacket.hand = Hand.OFF; + playerConnection.sendPacket(openBookPacket); + + // Restore the item in offhand + SetSlotPacket restoreItemPacket = new SetSlotPacket(); + restoreItemPacket.windowId = 0; + restoreItemPacket.slot = 45; + restoreItemPacket.itemStack = getItemInOffHand(); + playerConnection.sendPacket(restoreItemPacket); } @Override diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java index 575fa102a..2fc0b4201 100644 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java @@ -1,5 +1,8 @@ package net.minestom.server.item.metadata; +import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatParser; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; @@ -18,7 +21,7 @@ public class WrittenBookMeta extends ItemMeta { private WrittenBookGeneration generation; private String author; private String title; - private List pages = new ArrayList<>(); + private List pages = new ArrayList<>(); /** * Gets if the book is resolved. @@ -100,7 +103,7 @@ public class WrittenBookMeta extends ItemMeta { * * @return a modifiable {@link ArrayList} with the pages of the book */ - public List getPages() { + public List getPages() { return pages; } @@ -109,7 +112,7 @@ public class WrittenBookMeta extends ItemMeta { * * @param pages the array list containing the book pages */ - public void setPages(List pages) { + public void setPages(List pages) { this.pages = pages; } @@ -149,9 +152,7 @@ public class WrittenBookMeta extends ItemMeta { if (compound.containsKey("pages")) { final NBTList list = compound.getList("pages"); for (NBTString page : list) { - final String jsonPage = page.getValue(); - final ColoredText coloredText = ChatParser.toColoredText(jsonPage); - this.pages.add(coloredText); + this.pages.add(page.getValue()); } } } @@ -172,8 +173,8 @@ public class WrittenBookMeta extends ItemMeta { } if (!pages.isEmpty()) { NBTList list = new NBTList<>(NBTTypes.TAG_String); - for (JsonMessage page : pages) { - list.add(new NBTString(page.toString())); + for (String page : pages) { + list.add(new NBTString(page)); } compound.set("pages", list); } @@ -196,4 +197,26 @@ public class WrittenBookMeta extends ItemMeta { ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED } + /** + * Creates a written book meta from an Adventure book. This meta will not be + * resolved and the generation will default to {@link WrittenBookGeneration#ORIGINAL}. + * + * @param book the book + * + * @return the meta + */ + public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book) { + WrittenBookMeta meta = new WrittenBookMeta(); + meta.resolved = false; + meta.generation = WrittenBookGeneration.ORIGINAL; + meta.author = MinecraftServer.getSerializationManager().serialize(book.author()); + meta.title = MinecraftServer.getSerializationManager().serialize(book.title()); + meta.pages = new ArrayList<>(); + + for (Component page : book.pages()) { + meta.pages.add(MinecraftServer.getSerializationManager().serialize(page)); + } + + return meta; + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java index 0647fd297..5173054d7 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull; public class NamedSoundEffectPacket implements ServerPacket { public String soundName; - public SoundCategory soundCategory; + public int soundCategory; public int x, y, z; public float volume; public float pitch; @@ -17,7 +17,7 @@ public class NamedSoundEffectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { writer.writeSizedString(soundName); - writer.writeVarInt(soundCategory.ordinal()); + writer.writeVarInt(soundCategory); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index 4909a3106..4db791a6d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -1,5 +1,6 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.sound.Sound.Source; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.sound.Sound; @@ -11,15 +12,19 @@ import org.jetbrains.annotations.NotNull; public class SoundEffectPacket implements ServerPacket { public int soundId; - public SoundCategory soundCategory; + public int soundCategory; public int x, y, z; public float volume; public float pitch; + /** + * @deprecated Use variables + */ + @Deprecated public static SoundEffectPacket create(SoundCategory category, Sound sound, Position position, float volume, float pitch) { SoundEffectPacket packet = new SoundEffectPacket(); packet.soundId = sound.getId(); - packet.soundCategory = category; + packet.soundCategory = category.ordinal(); // *8 converts to fixed-point representation with 3 bits for fractional part packet.x = (int) position.getX(); packet.y = (int) position.getY(); @@ -32,7 +37,7 @@ public class SoundEffectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { writer.writeVarInt(soundId); - writer.writeVarInt(soundCategory.ordinal()); + writer.writeVarInt(soundCategory); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/sound/SoundCategory.java b/src/main/java/net/minestom/server/sound/SoundCategory.java index cc92bb629..c80f46131 100644 --- a/src/main/java/net/minestom/server/sound/SoundCategory.java +++ b/src/main/java/net/minestom/server/sound/SoundCategory.java @@ -1,5 +1,9 @@ package net.minestom.server.sound; +/** + * @deprecated Use {@link net.kyori.adventure.sound.Sound.Source} + */ +@Deprecated public enum SoundCategory { MASTER, MUSIC,