diff --git a/patches/api/Adventure.patch b/patches/api/Adventure.patch index 218e874617..5dbaa509dd 100644 --- a/patches/api/Adventure.patch +++ b/patches/api/Adventure.patch @@ -2051,7 +2051,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 * Represents a player, connected or not */ -public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient { -+public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified { // Paper ++public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer { // Paper + + // Paper start + @Override @@ -2060,6 +2060,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + /** ++ * Gets an unmodifiable view of all known currently active bossbars. ++ *

++ * This currently only returns bossbars shown to the player via ++ * {@link #showBossBar(net.kyori.adventure.bossbar.BossBar)} and does not contain bukkit ++ * {@link org.bukkit.boss.BossBar} instances shown to the player. ++ * ++ * @return an unmodifiable view of all known currently active bossbars ++ * @since 4.14.0 ++ */ ++ @Override ++ @org.jetbrains.annotations.UnmodifiableView @NotNull Iterable activeBossBars(); ++ ++ /** + * Gets the "friendly" name to display of this player. + * + * @return the display name @@ -2358,25 +2371,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt); + // Paper start - /** - * Request that the player's client download and switch resource packs. - *

-@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - * @param hash The sha1 hash sum of the resource pack file which is used - * to apply a cached version of the pack directly without downloading - * if it is available. Hast to be 20 bytes long! -+ * @param prompt The optional custom prompt message to be shown to client. -+ * @throws IllegalArgumentException Thrown if the URL is null. -+ * @throws IllegalArgumentException Thrown if the URL is too long. The -+ * length restriction is an implementation specific arbitrary value. -+ * @throws IllegalArgumentException Thrown if the hash is not 20 bytes -+ * long. -+ */ -+ default void setResourcePack(@NotNull String url, byte @Nullable [] hash, net.kyori.adventure.text.@Nullable Component prompt) { -+ this.setResourcePack(url, hash, prompt, false); -+ } -+ // Paper end -+ + /** + * Request that the player's client download and switch resource packs. + *

@@ -2406,16 +2400,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + * pack correctly. + * + * -+ * @deprecated in favour of {@link #setResourcePack(String, byte[], Component, boolean)} + * @param url The URL from which the client will download the resource + * pack. The string must contain only US-ASCII characters and should + * be encoded as per RFC 1738. + * @param hash The sha1 hash sum of the resource pack file which is used + * to apply a cached version of the pack directly without downloading + * if it is available. Hast to be 20 bytes long! - * @param force If true, the client will be disconnected from the server - * when it declines to use the resource pack. - * @throws IllegalArgumentException Thrown if the URL is null. ++ * @param prompt The optional custom prompt message to be shown to client. ++ * @throws IllegalArgumentException Thrown if the URL is null. ++ * @throws IllegalArgumentException Thrown if the URL is too long. The ++ * length restriction is an implementation specific arbitrary value. ++ * @throws IllegalArgumentException Thrown if the hash is not 20 bytes ++ * long. ++ */ ++ default void setResourcePack(@NotNull String url, byte @Nullable [] hash, net.kyori.adventure.text.@Nullable Component prompt) { ++ this.setResourcePack(url, hash, prompt, false); ++ } ++ // Paper end ++ + /** + * Request that the player's client download and switch resource packs. + *

+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM + * pack correctly. + * + * ++ * @deprecated in favour of {@link #setResourcePack(String, byte[], Component, boolean)} + * @param url The URL from which the client will download the resource + * pack. The string must contain only US-ASCII characters and should + * be encoded as per RFC 1738. @@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * @throws IllegalArgumentException Thrown if the hash is not 20 bytes * long. diff --git a/patches/api/Expose-client-protocol-version-and-virtual-host.patch b/patches/api/Expose-client-protocol-version-and-virtual-host.patch index 09902ce13b..a3bfc0b0cb 100644 --- a/patches/api/Expose-client-protocol-version-and-virtual-host.patch +++ b/patches/api/Expose-client-protocol-version-and-virtual-host.patch @@ -64,8 +64,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 /** * Represents a player, connected or not */ --public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified { // Paper -+public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, com.destroystokyo.paper.network.NetworkClient { // Paper +-public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer { // Paper ++public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer, com.destroystokyo.paper.network.NetworkClient { // Paper // Paper start @Override diff --git a/patches/server/Adventure.patch b/patches/server/Adventure.patch index 7ab2c3e07a..a36b17bfe7 100644 --- a/patches/server/Adventure.patch +++ b/patches/server/Adventure.patch @@ -102,6 +102,96 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + } +} +diff --git a/src/main/java/io/papermc/paper/adventure/BossBarImplementationImpl.java b/src/main/java/io/papermc/paper/adventure/BossBarImplementationImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/adventure/BossBarImplementationImpl.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.adventure; ++ ++import com.google.common.collect.Collections2; ++import java.util.Set; ++import java.util.function.Function; ++import net.kyori.adventure.bossbar.BossBar; ++import net.kyori.adventure.bossbar.BossBarImplementation; ++import net.kyori.adventure.bossbar.BossBarViewer; ++import net.kyori.adventure.text.Component; ++import net.minecraft.network.protocol.game.ClientboundBossEventPacket; ++import net.minecraft.server.level.ServerBossEvent; ++import net.minecraft.server.level.ServerPlayer; ++import net.minecraft.world.BossEvent; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.jetbrains.annotations.NotNull; ++ ++public final class BossBarImplementationImpl implements BossBar.Listener, BossBarImplementation { ++ private final BossBar bar; ++ private ServerBossEvent vanilla; ++ ++ public BossBarImplementationImpl(final BossBar bar) { ++ this.bar = bar; ++ } ++ ++ public void playerShow(final CraftPlayer player) { ++ if (this.vanilla == null) { ++ this.vanilla = new ServerBossEvent( ++ PaperAdventure.asVanilla(this.bar.name()), ++ PaperAdventure.asVanilla(this.bar.color()), ++ PaperAdventure.asVanilla(this.bar.overlay()) ++ ); ++ this.vanilla.adventure = this.bar; ++ this.bar.addListener(this); ++ } ++ this.vanilla.addPlayer(player.getHandle()); ++ } ++ ++ public void playerHide(final CraftPlayer player) { ++ if (this.vanilla != null) { ++ this.vanilla.removePlayer(player.getHandle()); ++ if (this.vanilla.getPlayers().isEmpty()) { ++ this.bar.removeListener(this); ++ this.vanilla = null; ++ } ++ } ++ } ++ ++ @Override ++ public void bossBarNameChanged(final @NonNull BossBar bar, final @NonNull Component oldName, final @NonNull Component newName) { ++ this.maybeBroadcast(ClientboundBossEventPacket::createUpdateNamePacket); ++ } ++ ++ @Override ++ public void bossBarProgressChanged(final @NonNull BossBar bar, final float oldProgress, final float newProgress) { ++ this.maybeBroadcast(ClientboundBossEventPacket::createUpdateProgressPacket); ++ } ++ ++ @Override ++ public void bossBarColorChanged(final @NonNull BossBar bar, final BossBar.@NonNull Color oldColor, final BossBar.@NonNull Color newColor) { ++ this.maybeBroadcast(ClientboundBossEventPacket::createUpdateStylePacket); ++ } ++ ++ @Override ++ public void bossBarOverlayChanged(final @NonNull BossBar bar, final BossBar.@NonNull Overlay oldOverlay, final BossBar.@NonNull Overlay newOverlay) { ++ this.maybeBroadcast(ClientboundBossEventPacket::createUpdateStylePacket); ++ } ++ ++ @Override ++ public void bossBarFlagsChanged(final @NonNull BossBar bar, final @NonNull Set flagsAdded, final @NonNull Set flagsRemoved) { ++ this.maybeBroadcast(ClientboundBossEventPacket::createUpdatePropertiesPacket); ++ } ++ ++ @Override ++ public @NotNull Iterable viewers() { ++ return this.vanilla == null ? Set.of() : Collections2.transform(this.vanilla.getPlayers(), ServerPlayer::getBukkitEntity); ++ } ++ ++ private void maybeBroadcast(final Function fn) { ++ if (this.vanilla != null) { ++ this.vanilla.broadcast(fn); ++ } ++ } ++} diff --git a/src/main/java/io/papermc/paper/adventure/ChatDecorationProcessor.java b/src/main/java/io/papermc/paper/adventure/ChatDecorationProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 @@ -1130,56 +1220,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return ChatFormatting.getByHexValue(color.value()); + } +} -diff --git a/src/main/java/io/papermc/paper/adventure/VanillaBossBarListener.java b/src/main/java/io/papermc/paper/adventure/VanillaBossBarListener.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/adventure/VanillaBossBarListener.java -@@ -0,0 +0,0 @@ -+package io.papermc.paper.adventure; -+ -+import java.util.Set; -+import java.util.function.Consumer; -+import java.util.function.Function; -+ -+import net.kyori.adventure.bossbar.BossBar; -+import net.kyori.adventure.text.Component; -+import net.minecraft.network.protocol.game.ClientboundBossEventPacket; -+import net.minecraft.world.BossEvent; -+import org.checkerframework.checker.nullness.qual.NonNull; -+ -+public final class VanillaBossBarListener implements BossBar.Listener { -+ private final Consumer> action; -+ -+ public VanillaBossBarListener(final Consumer> action) { -+ this.action = action; -+ } -+ -+ @Override -+ public void bossBarNameChanged(final @NonNull BossBar bar, final @NonNull Component oldName, final @NonNull Component newName) { -+ this.action.accept(ClientboundBossEventPacket::createUpdateNamePacket); -+ } -+ -+ @Override -+ public void bossBarProgressChanged(final @NonNull BossBar bar, final float oldProgress, final float newProgress) { -+ this.action.accept(ClientboundBossEventPacket::createUpdateProgressPacket); -+ } -+ -+ @Override -+ public void bossBarColorChanged(final @NonNull BossBar bar, final BossBar.@NonNull Color oldColor, final BossBar.@NonNull Color newColor) { -+ this.action.accept(ClientboundBossEventPacket::createUpdateStylePacket); -+ } -+ -+ @Override -+ public void bossBarOverlayChanged(final @NonNull BossBar bar, final BossBar.@NonNull Overlay oldOverlay, final BossBar.@NonNull Overlay newOverlay) { -+ this.action.accept(ClientboundBossEventPacket::createUpdateStylePacket); -+ } -+ -+ @Override -+ public void bossBarFlagsChanged(final @NonNull BossBar bar, final @NonNull Set flagsAdded, final @NonNull Set flagsRemoved) { -+ this.action.accept(ClientboundBossEventPacket::createUpdatePropertiesPacket); -+ } -+} diff --git a/src/main/java/io/papermc/paper/adventure/WrapperAwareSerializer.java b/src/main/java/io/papermc/paper/adventure/WrapperAwareSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 @@ -1206,6 +1246,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return net.minecraft.network.chat.Component.Serializer.fromJson(GsonComponentSerializer.gson().serializer().toJsonTree(component)); + } +} +diff --git a/src/main/java/io/papermc/paper/adventure/providers/BossBarImplementationProvider.java b/src/main/java/io/papermc/paper/adventure/providers/BossBarImplementationProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/adventure/providers/BossBarImplementationProvider.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.adventure.providers; ++ ++import io.papermc.paper.adventure.BossBarImplementationImpl; ++import net.kyori.adventure.bossbar.BossBar; ++import net.kyori.adventure.bossbar.BossBarImplementation; ++import org.jetbrains.annotations.NotNull; ++ ++@SuppressWarnings("UnstableApiUsage") // permitted provider ++public class BossBarImplementationProvider implements BossBarImplementation.Provider { ++ @Override ++ public @NotNull BossBarImplementation create(final @NotNull BossBar bar) { ++ return new BossBarImplementationImpl(bar); ++ } ++} diff --git a/src/main/java/io/papermc/paper/adventure/providers/ClickCallbackProviderImpl.java b/src/main/java/io/papermc/paper/adventure/providers/ClickCallbackProviderImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 @@ -1561,48 +1621,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return builder -> builder.flattener(PaperAdventure.FLATTENER); + } +} -diff --git a/src/main/java/net/kyori/adventure/bossbar/HackyBossBarPlatformBridge.java b/src/main/java/net/kyori/adventure/bossbar/HackyBossBarPlatformBridge.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/net/kyori/adventure/bossbar/HackyBossBarPlatformBridge.java -@@ -0,0 +0,0 @@ -+package net.kyori.adventure.bossbar; -+ -+import io.papermc.paper.adventure.PaperAdventure; -+import io.papermc.paper.adventure.VanillaBossBarListener; -+import net.minecraft.server.level.ServerBossEvent; -+import org.bukkit.craftbukkit.entity.CraftPlayer; -+ -+public abstract class HackyBossBarPlatformBridge { -+ public ServerBossEvent vanilla$bar; -+ private VanillaBossBarListener vanilla$listener; -+ -+ public final void paper$playerShow(final CraftPlayer player) { -+ if (this.vanilla$bar == null) { -+ final BossBar $this = (BossBar) this; -+ this.vanilla$bar = new ServerBossEvent( -+ PaperAdventure.asVanilla($this.name()), -+ PaperAdventure.asVanilla($this.color()), -+ PaperAdventure.asVanilla($this.overlay()) -+ ); -+ this.vanilla$bar.adventure = $this; -+ this.vanilla$listener = new VanillaBossBarListener(this.vanilla$bar::broadcast); -+ $this.addListener(this.vanilla$listener); -+ } -+ this.vanilla$bar.addPlayer(player.getHandle()); -+ } -+ -+ public final void paper$playerHide(final CraftPlayer player) { -+ if (this.vanilla$bar != null) { -+ this.vanilla$bar.removePlayer(player.getHandle()); -+ if (this.vanilla$bar.getPlayers().isEmpty()) { -+ ((BossBar) this).removeListener(this.vanilla$listener); -+ this.vanilla$bar = null; -+ } -+ } -+ } -+} diff --git a/src/main/java/net/minecraft/ChatFormatting.java b/src/main/java/net/minecraft/ChatFormatting.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/ChatFormatting.java @@ -3992,14 +4010,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + // resetTitle implemented above + ++ private @Nullable Set activeBossBars; ++ ++ @Override ++ public @NotNull Iterable activeBossBars() { ++ if (this.activeBossBars != null) { ++ return java.util.Collections.unmodifiableSet(this.activeBossBars); ++ } ++ return Set.of(); ++ } ++ + @Override + public void showBossBar(final net.kyori.adventure.bossbar.BossBar bar) { -+ ((net.kyori.adventure.bossbar.HackyBossBarPlatformBridge) bar).paper$playerShow(this); ++ net.kyori.adventure.bossbar.BossBarImplementation.get(bar, io.papermc.paper.adventure.BossBarImplementationImpl.class).playerShow(this); ++ if (this.activeBossBars == null) { ++ this.activeBossBars = new HashSet<>(); ++ } ++ this.activeBossBars.add(bar); + } + + @Override + public void hideBossBar(final net.kyori.adventure.bossbar.BossBar bar) { -+ ((net.kyori.adventure.bossbar.HackyBossBarPlatformBridge) bar).paper$playerHide(this); ++ net.kyori.adventure.bossbar.BossBarImplementation.get(bar, io.papermc.paper.adventure.BossBarImplementationImpl.class).playerHide(this); ++ if (this.activeBossBars != null) { ++ this.activeBossBars.remove(bar); ++ if (this.activeBossBars.isEmpty()) { ++ this.activeBossBars = null; ++ } ++ } + } + + @Override @@ -4942,6 +4980,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 HashSet reference = new HashSet(players.size()); for (ServerPlayer player : players) { reference.add(player.getBukkitEntity()); +diff --git a/src/main/resources/META-INF/services/net.kyori.adventure.bossbar.BossBarImplementation$Provider b/src/main/resources/META-INF/services/net.kyori.adventure.bossbar.BossBarImplementation$Provider +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/resources/META-INF/services/net.kyori.adventure.bossbar.BossBarImplementation$Provider +@@ -0,0 +1 @@ ++io.papermc.paper.adventure.providers.BossBarImplementationProvider diff --git a/src/main/resources/META-INF/services/net.kyori.adventure.text.event.ClickCallback$Provider b/src/main/resources/META-INF/services/net.kyori.adventure.text.event.ClickCallback$Provider new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000