From a0a5615e6e45093edd3350613c84684e5387e20f Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Tue, 30 May 2023 18:36:16 -0700 Subject: [PATCH] Fix playing adventure sounds on World/Server (#8077) --- patches/server/Adventure.patch | 149 +++++++++++++++++++++++------ patches/server/Paper-command.patch | 2 +- 2 files changed, 121 insertions(+), 30 deletions(-) diff --git a/patches/server/Adventure.patch b/patches/server/Adventure.patch index 27d2ee75ab..85ca9657e7 100644 --- a/patches/server/Adventure.patch +++ b/patches/server/Adventure.patch @@ -748,6 +748,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; ++import java.util.Optional; ++import java.util.function.BiConsumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import net.kyori.adventure.bossbar.BossBar; @@ -769,21 +771,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.kyori.adventure.util.Codec; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.core.Holder; ++import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.locale.Language; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.ComponentUtils; ++import net.minecraft.network.protocol.Packet; ++import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; ++import net.minecraft.network.protocol.game.ClientboundSoundPacket; +import net.minecraft.resources.ResourceLocation; ++import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.BossEvent; ++import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.WrittenBookItem; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.craftbukkit.entity.CraftEntity; -+import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + @@ -839,7 +847,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + }) + .build(); + public static final AttributeKey LOCALE_ATTRIBUTE = AttributeKey.valueOf("adventure:locale"); // init after FLATTENER because classloading triggered here might create a logger -+ @Deprecated public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build(); ++ @Deprecated ++ public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build(); + private static final Codec NBT_CODEC = new Codec() { + @Override + public @NotNull CompoundTag decode(final @NotNull String encoded) throws IOException { @@ -942,7 +951,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + ); + } + -+ public static Component resolveWithContext(final @NotNull Component component, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject, final boolean bypassPermissions) throws IOException { ++ public static Component resolveWithContext(final @NotNull Component component, final @Nullable CommandSender context, final @Nullable org.bukkit.entity.Entity scoreboardSubject, final boolean bypassPermissions) throws IOException { + final CommandSourceStack css = context != null ? VanillaCommandWrapper.getListener(context) : null; + Boolean previous = null; + if (css != null && bypassPermissions) { @@ -1068,6 +1077,32 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return asVanilla(source); + } + ++ public static Packet asSoundPacket(final Sound sound, final double x, final double y, final double z, final long seed, @Nullable BiConsumer, Float> packetConsumer) { ++ final ResourceLocation name = asVanilla(sound.name()); ++ final Optional soundEvent = BuiltInRegistries.SOUND_EVENT.getOptional(name); ++ final SoundSource source = asVanilla(sound.source()); ++ ++ final Holder soundEventHolder = soundEvent.map(BuiltInRegistries.SOUND_EVENT::wrapAsHolder).orElseGet(() -> Holder.direct(SoundEvent.createVariableRangeEvent(name))); ++ final Packet packet = new ClientboundSoundPacket(soundEventHolder, source, x, y, z, sound.volume(), sound.pitch(), seed); ++ if (packetConsumer != null) { ++ packetConsumer.accept(packet, soundEventHolder.value().getRange(sound.volume())); ++ } ++ return packet; ++ } ++ ++ public static Packet asSoundPacket(final Sound sound, final Entity emitter, final long seed, @Nullable BiConsumer, Float> packetConsumer) { ++ final ResourceLocation name = asVanilla(sound.name()); ++ final Optional soundEvent = BuiltInRegistries.SOUND_EVENT.getOptional(name); ++ final SoundSource source = asVanilla(sound.source()); ++ ++ final Holder soundEventHolder = soundEvent.map(BuiltInRegistries.SOUND_EVENT::wrapAsHolder).orElseGet(() -> Holder.direct(SoundEvent.createVariableRangeEvent(name))); ++ final Packet packet = new ClientboundSoundEntityPacket(soundEventHolder, source, emitter, sound.volume(), sound.pitch(), seed); ++ if (packetConsumer != null) { ++ packetConsumer.accept(packet, soundEventHolder.value().getRange(sound.volume())); ++ } ++ return packet; ++ } ++ + // NBT + + public static @Nullable BinaryTagHolder asBinaryTagHolder(final @Nullable CompoundTag tag) { @@ -2947,6 +2982,44 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } // Spigot end + ++ // Paper start - adventure sounds ++ @Override ++ public void playSound(final net.kyori.adventure.sound.Sound sound) { ++ final long seed = sound.seed().orElseGet(this.console.overworld().getRandom()::nextLong); ++ for (ServerPlayer player : this.playerList.getPlayers()) { ++ player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player.getX(), player.getY(), player.getZ(), seed, null)); ++ } ++ } ++ ++ @Override ++ public void playSound(final net.kyori.adventure.sound.Sound sound, final double x, final double y, final double z) { ++ io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, x, y, z, sound.seed().orElseGet(this.console.overworld().getRandom()::nextLong), this.playSound0(x, y, z, this.console.getAllLevels())); ++ } ++ ++ @Override ++ public void playSound(final net.kyori.adventure.sound.Sound sound, final net.kyori.adventure.sound.Sound.Emitter emitter) { ++ final long seed = sound.seed().orElseGet(this.console.overworld().getRandom()::nextLong); ++ if (emitter == net.kyori.adventure.sound.Sound.Emitter.self()) { ++ for (ServerPlayer player : this.playerList.getPlayers()) { ++ player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player, seed, null)); ++ } ++ } else if (emitter instanceof org.bukkit.craftbukkit.entity.CraftEntity craftEntity) { ++ final net.minecraft.world.entity.Entity entity = craftEntity.getHandle(); ++ io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, entity, seed, this.playSound0(entity.getX(), entity.getY(), entity.getZ(), List.of((ServerLevel) entity.getLevel()))); ++ } else { ++ throw new IllegalArgumentException("Sound emitter must be an Entity or self(), but was: " + emitter); ++ } ++ } ++ ++ private java.util.function.BiConsumer, Float> playSound0(final double x, final double y, final double z, final Iterable levels) { ++ return (packet, distance) -> { ++ for (final ServerLevel level : levels) { ++ level.getServer().getPlayerList().broadcast(null, x, y, z, distance, level.dimension(), packet); ++ } ++ }; ++ } ++ // Paper end ++ + // Paper start + private Iterable adventure$audiences; + @Override @@ -2970,6 +3043,46 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private static final Random rand = new Random(); +@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { + entityTracker.broadcastAndSend(packet); + } + } ++ // Paper start - Adventure ++ @Override ++ public void playSound(final net.kyori.adventure.sound.Sound sound) { ++ final long seed = sound.seed().orElseGet(this.world.getRandom()::nextLong); ++ for (ServerPlayer player : this.getHandle().players()) { ++ player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player.getX(), player.getY(), player.getZ(), seed, null)); ++ } ++ } ++ ++ @Override ++ public void playSound(final net.kyori.adventure.sound.Sound sound, final double x, final double y, final double z) { ++ io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, x, y, z, sound.seed().orElseGet(this.world.getRandom()::nextLong), this.playSound0(x, y, z)); ++ } ++ ++ @Override ++ public void playSound(final net.kyori.adventure.sound.Sound sound, final net.kyori.adventure.sound.Sound.Emitter emitter) { ++ final long seed = sound.seed().orElseGet(this.getHandle().getRandom()::nextLong); ++ if (emitter == net.kyori.adventure.sound.Sound.Emitter.self()) { ++ for (ServerPlayer player : this.getHandle().players()) { ++ player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player, seed, null)); ++ } ++ } else if (emitter instanceof CraftEntity craftEntity) { ++ final net.minecraft.world.entity.Entity entity = craftEntity.getHandle(); ++ io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, entity, seed, this.playSound0(entity.getX(), entity.getY(), entity.getZ())); ++ } else { ++ throw new IllegalArgumentException("Sound emitter must be an Entity or self(), but was: " + emitter); ++ } ++ } ++ ++ private java.util.function.BiConsumer, Float> playSound0(final double x, final double y, final double z) { ++ return (packet, distance) -> this.world.getServer().getPlayerList().broadcast(null, x, y, z, distance, this.world.dimension(), packet); ++ } ++ // Paper end + + private static Map> gamerules; + public static synchronized Map> getGameRulesNMS() { @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { return ret; @@ -3868,18 +3981,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + @Override + public void playSound(final net.kyori.adventure.sound.Sound sound, final double x, final double y, final double z) { -+ final long seed = sound.seed().orElseGet(this.getHandle().getRandom()::nextLong); -+ final ResourceLocation name = io.papermc.paper.adventure.PaperAdventure.asVanilla(sound.name()); -+ final java.util.Optional event = BuiltInRegistries.SOUND_EVENT.getOptional(name); -+ -+ final Holder soundHolder; -+ if (event.isPresent()) { -+ soundHolder = BuiltInRegistries.SOUND_EVENT.wrapAsHolder(event.get()); -+ } else { -+ soundHolder = Holder.direct(SoundEvent.createVariableRangeEvent(name)); -+ } -+ -+ this.getHandle().connection.send(new ClientboundSoundPacket(soundHolder, io.papermc.paper.adventure.PaperAdventure.asVanilla(sound.source()), x, y, z, sound.volume(), sound.pitch(), seed)); ++ this.getHandle().connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, x, y, z, sound.seed().orElseGet(this.getHandle().getRandom()::nextLong), null)); + } + + @Override @@ -3887,23 +3989,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + final Entity entity; + if (emitter == net.kyori.adventure.sound.Sound.Emitter.self()) { + entity = this.getHandle(); -+ } else if (emitter instanceof org.bukkit.entity.Entity) { -+ entity = ((CraftEntity) emitter).getHandle(); ++ } else if (emitter instanceof CraftEntity craftEntity) { ++ entity = craftEntity.getHandle(); + } else { + throw new IllegalArgumentException("Sound emitter must be an Entity or self(), but was: " + emitter); + } -+ final long seed = sound.seed().orElseGet(this.getHandle().getRandom()::nextLong); -+ -+ final ResourceLocation name = io.papermc.paper.adventure.PaperAdventure.asVanilla(sound.name()); -+ final java.util.Optional event = BuiltInRegistries.SOUND_EVENT.getOptional(name); -+ final Holder soundHolder; -+ if (event.isPresent()) { -+ soundHolder = BuiltInRegistries.SOUND_EVENT.wrapAsHolder(event.get()); -+ } else { -+ soundHolder = Holder.direct(SoundEvent.createVariableRangeEvent(name)); -+ } -+ -+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundSoundEntityPacket(soundHolder, io.papermc.paper.adventure.PaperAdventure.asVanilla(sound.source()), entity, sound.volume(), sound.pitch(), seed)); ++ this.getHandle().connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, entity, sound.seed().orElseGet(this.getHandle().getRandom()::nextLong), null)); + } + + @Override diff --git a/patches/server/Paper-command.patch b/patches/server/Paper-command.patch index ef99d3a52b..019ce3bc87 100644 --- a/patches/server/Paper-command.patch +++ b/patches/server/Paper-command.patch @@ -627,7 +627,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); @@ -0,0 +0,0 @@ public final class CraftServer implements Server { - // Spigot end + // Paper end // Paper start + @SuppressWarnings({"rawtypes", "unchecked"})