diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index 057b8a2398..926790b470 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -29,6 +29,7 @@ import java.util.Optional; import java.util.UUID; import java.util.function.UnaryOperator; +import net.kyori.adventure.bossbar.BossBarViewer; import net.kyori.adventure.dialog.DialogLike; import net.kyori.adventure.identity.Identified; import net.kyori.adventure.inventory.Book; @@ -51,7 +52,8 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessageSource, ChannelMessageSink, /* Adventure-specific interfaces */ Identified, HoverEventSource, Keyed, KeyIdentifiable, Sound.Emitter, - PlayerHeadObjectContents.SkinSource { + PlayerHeadObjectContents.SkinSource, + BossBarViewer { /** * Returns the player's current username. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index d6deeaef7d..266e543005 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -145,6 +145,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; /** * Represents a player that is connected to the proxy. @@ -185,7 +186,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, private @Nullable VelocityServerConnection connectionInFlight; private @Nullable PlayerSettings settings; private @Nullable ModInfo modInfo; - private final Set bossBars = new HashSet<>(); + private @Nullable Set bossBars; private Component playerListHeader = Component.empty(); private Component playerListFooter = Component.empty(); private final InternalTabList tabList; @@ -237,8 +238,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, * Used for cleaning up resources during a disconnection. */ public void disconnected() { - for (final VelocityBossBarImplementation bar : this.bossBars) { - bar.viewerDisconnected(this); + if (this.bossBars != null) { + for (final BossBar bar : this.bossBars) { + VelocityBossBarImplementation.get(bar).viewerRemove(this); + } } } @@ -587,25 +590,41 @@ public void resetTitle() { } @Override - public void hideBossBar(@NonNull BossBar bar) { + public void showBossBar(@NonNull BossBar bar) { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_9)) { final VelocityBossBarImplementation impl = VelocityBossBarImplementation.get(bar); - if (impl.viewerRemove(this)) { - this.bossBars.remove(impl); + if (impl.viewerAdd(this)) { + if (this.bossBars == null) { + this.bossBars = new HashSet<>(); + } + this.bossBars.add(bar); } } } @Override - public void showBossBar(@NonNull BossBar bar) { + public void hideBossBar(@NonNull BossBar bar) { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_9)) { final VelocityBossBarImplementation impl = VelocityBossBarImplementation.get(bar); - if (impl.viewerAdd(this)) { - this.bossBars.add(impl); + if (impl.viewerRemove(this)) { + if (this.bossBars != null) { + this.bossBars.remove(bar); + if (this.bossBars.isEmpty()) { + this.bossBars = null; + } + } } } } + @Override + public @UnmodifiableView @org.jspecify.annotations.NonNull Iterable activeBossBars() { + if (this.bossBars == null) { + return Set.of(); + } + return Set.copyOf(this.bossBars); + } + @Override public ConnectionRequestBuilder createConnectionRequest(RegisteredServer server) { return new ConnectionRequestBuilderImpl(server, this.connectedServer);