Add boss bar implementations

This commit is contained in:
Kieran Wallbanks 2021-03-01 17:25:55 +00:00
parent cfa9cffe87
commit aff04c0e0d
7 changed files with 273 additions and 16 deletions

View File

@ -1,6 +1,7 @@
package net.minestom.server;
import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.adventure.BossBarManager;
import net.minestom.server.benchmark.BenchmarkManager;
import net.minestom.server.command.CommandManager;
import net.minestom.server.data.DataManager;
@ -115,6 +116,7 @@ public final class MinecraftServer {
private static DimensionTypeManager dimensionTypeManager;
private static BiomeManager biomeManager;
private static AdvancementManager advancementManager;
private static BossBarManager bossBarManager;
private static ExtensionManager extensionManager;
@ -180,6 +182,7 @@ public final class MinecraftServer {
dimensionTypeManager = new DimensionTypeManager();
biomeManager = new BiomeManager();
advancementManager = new AdvancementManager();
bossBarManager = new BossBarManager();
updateManager = new UpdateManager();
@ -427,6 +430,16 @@ public final class MinecraftServer {
return connectionManager;
}
/**
* Gets the boss bar manager.
*
* @return the boss bar manager
*/
public static BossBarManager getBossBarManager() {
checkInitStatus(bossBarManager);
return bossBarManager;
}
/**
* Gets the object handling the client packets processing.
* <p>

View File

@ -0,0 +1,223 @@
package net.minestom.server.adventure;
import com.google.common.collect.MapMaker;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.bossbar.BossBar.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.player.PlayerDisconnectEvent;
import net.minestom.server.network.packet.server.play.BossBarPacket;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Handler;
import static net.minestom.server.network.packet.server.play.BossBarPacket.Action.*;
/**
* Manages all boss bars known to this Minestom instance. This implementation is heavily
* based on <a href="https://github.com/VelocityPowered/Velocity">Velocity</a>'s
* boss bar management system.
*/
public class BossBarManager implements BossBar.Listener {
private final Map<BossBar, Holder> bars;
/**
* Creates a new boss bar manager.
*/
public BossBarManager() {
this.bars = new MapMaker().weakKeys().makeMap();
MinecraftServer.getGlobalEventHandler().addEventCallback(PlayerDisconnectEvent.class, this::onDisconnect);
}
/**
* Adds the specified player to the boss bar's viewers and spawns the boss bar, registering the
* boss bar if needed.
*
* @param player the intended viewer
* @param bar the boss bar to show
*/
public void addBossBar(@NotNull Player player, @NotNull BossBar bar) {
Holder holder = this.getOrCreateHandler(bar);
if (holder.players.add(player.getUuid())) {
player.getPlayerConnection().sendPacket(holder.createAddPacket());
}
}
/**
* Removes the specified player from the boss bar's viewers and despawns the boss bar.
*
* @param player the intended viewer
* @param bar the boss bar to hide
*/
public void removeBossBar(@NotNull Player player, @NotNull BossBar bar) {
Holder holder = this.getOrCreateHandler(bar);
if (holder.players.remove(player.getUuid())) {
player.getPlayerConnection().sendPacket(holder.createRemovePacket());
}
}
@Override
public void bossBarNameChanged(@NonNull BossBar bar, @NonNull Component oldName, @NonNull Component newName) {
Holder holder = this.bars.get(bar);
this.updatePlayers(holder.createTitleUpdate(newName), holder.players);
}
@Override
public void bossBarProgressChanged(@NonNull BossBar bar, float oldProgress, float newProgress) {
Holder holder = this.bars.get(bar);
this.updatePlayers(holder.createPercentUpdate(newProgress), holder.players);
}
@Override
public void bossBarColorChanged(@NonNull BossBar bar, @NonNull Color oldColor, @NonNull Color newColor) {
Holder holder = this.bars.get(bar);
this.updatePlayers(holder.createColorUpdate(newColor), holder.players);
}
@Override
public void bossBarOverlayChanged(@NonNull BossBar bar, BossBar.@NonNull Overlay oldOverlay, BossBar.@NonNull Overlay newOverlay) {
Holder holder = this.bars.get(bar);
this.updatePlayers(holder.createOverlayUpdate(newOverlay), holder.players);
}
@Override
public void bossBarFlagsChanged(@NonNull BossBar bar, @NonNull Set<BossBar.Flag> flagsAdded, @NonNull Set<BossBar.Flag> flagsRemoved) {
Holder holder = this.bars.get(bar);
this.updatePlayers(holder.createFlagsUpdate(), holder.players);
}
/**
* Sends the packet to all players in the set, removing them if they no longer exist
* in the connection manager.
*
* @param packet the packet
* @param players the players
*/
private void updatePlayers(BossBarPacket packet, Set<UUID> players) {
Iterator<UUID> iterator = players.iterator();
while (iterator.hasNext()) {
Player player = MinecraftServer.getConnectionManager().getPlayer(iterator.next());
if (player == null) {
iterator.remove();
} else {
player.getPlayerConnection().sendPacket(packet);
}
}
}
/**
* Gets or creates a handler for this bar.
*
* @param bar the bar
*
* @return the handler
*/
private @NotNull Holder getOrCreateHandler(@NotNull BossBar bar) {
Holder holder = this.bars.computeIfAbsent(bar, Holder::new);
if (!holder.registered) {
bar.addListener(this);
holder.registered = true;
}
return holder;
}
/**
* Called when a player disconnects. This removes the player from any boss bars they
* may be subscribed to.
*
* @param event the event
*/
private void onDisconnect(@NotNull PlayerDisconnectEvent event) {
for (Holder holder : this.bars.values()) {
holder.players.remove(event.getPlayer().getUuid());
}
}
private static class Holder {
protected final UUID uuid;
protected final BossBar bar;
protected final Set<UUID> players;
protected boolean registered;
Holder(@NotNull BossBar bar) {
this.uuid = UUID.randomUUID();
this.bar = bar;
this.players = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
this.registered = false;
}
BossBarPacket createRemovePacket() {
return this.createGenericPacket(REMOVE, packet -> {});
}
BossBarPacket createAddPacket() {
return this.createGenericPacket(ADD, packet -> {
packet.title = GsonComponentSerializer.gson().serialize(bar.name());
packet.color = bar.color().ordinal();
packet.division = bar.overlay().ordinal();
packet.health = bar.progress();
packet.flags = serializeFlags(bar.flags());
});
}
BossBarPacket createPercentUpdate(float newPercent) {
return this.createGenericPacket(UPDATE_HEALTH, packet -> packet.health = newPercent);
}
BossBarPacket createColorUpdate(@NotNull Color color) {
return this.createGenericPacket(UPDATE_STYLE, packet -> {
packet.color = color.ordinal();
packet.division = bar.overlay().ordinal();
});
}
BossBarPacket createTitleUpdate(@NotNull Component title) {
return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = GsonComponentSerializer.gson().serialize(title));
}
BossBarPacket createFlagsUpdate() {
return createFlagsUpdate(bar.flags());
}
BossBarPacket createFlagsUpdate(@NotNull Set<Flag> newFlags) {
return this.createGenericPacket(UPDATE_FLAGS, packet -> packet.flags = serializeFlags(bar.flags()));
}
BossBarPacket createOverlayUpdate(@NotNull Overlay overlay) {
return this.createGenericPacket(UPDATE_STYLE, packet -> {
packet.division = overlay.ordinal();
packet.color = bar.color().ordinal();
});
}
BossBarPacket createGenericPacket(@NotNull BossBarPacket.Action action, @NotNull Consumer<BossBarPacket> consumer) {
BossBarPacket packet = new BossBarPacket();
packet.uuid = this.uuid;
packet.action = action;
consumer.accept(packet);
return packet;
}
private static byte serializeFlags(@NotNull Set<Flag> flags) {
byte val = 0x0;
for (Flag flag : flags) {
val |= flag.ordinal();
}
return val;
}
}
}

View File

@ -2,7 +2,9 @@ package net.minestom.server.bossbar;
/**
* Represents the displayed color of a {@link BossBar}.
* @deprecated Use {@link net.kyori.adventure.bossbar.BossBar}
*/
@Deprecated
public enum BarColor {
PINK,
BLUE,

View File

@ -2,7 +2,10 @@ package net.minestom.server.bossbar;
/**
* Used to define the number of segments on a {@link BossBar}.
*
* @deprecated Use {@link net.kyori.adventure.bossbar.BossBar}
*/
@Deprecated
public enum BarDivision {
SOLID,
SEGMENT_6,

View File

@ -19,7 +19,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
* and add the {@link Player} you want using {@link #addViewer(Player)} and remove them using {@link #removeViewer(Player)}.
* <p>
* You can retrieve all the boss bars of a {@link Player} with {@link #getBossBars(Player)}.
*
* @deprecated Use {@link net.kyori.adventure.audience.Audience#showBossBar(net.kyori.adventure.bossbar.BossBar)}
*/
@Deprecated
public class BossBar implements Viewable {
private static final int MAX_BOSSBAR = 7;
@ -249,10 +252,10 @@ public class BossBar implements Viewable {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.ADD;
bossBarPacket.title = title;
bossBarPacket.title = title.toString();
bossBarPacket.health = progress;
bossBarPacket.color = color;
bossBarPacket.division = division;
bossBarPacket.color = color.ordinal();
bossBarPacket.division = division.ordinal();
bossBarPacket.flags = flags;
player.getPlayerConnection().sendPacket(bossBarPacket);
}
@ -275,7 +278,7 @@ public class BossBar implements Viewable {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.UPDATE_TITLE;
bossBarPacket.title = title;
bossBarPacket.title = title.toString();
sendPacketToViewers(bossBarPacket);
}
@ -291,7 +294,7 @@ public class BossBar implements Viewable {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.UPDATE_STYLE;
bossBarPacket.color = color;
bossBarPacket.color = color.ordinal();
sendPacketToViewers(bossBarPacket);
}
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.entity;
import com.google.common.collect.Queues;
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.text.Component;
@ -12,8 +13,10 @@ 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.attribute.AttributeInstance;
import net.minestom.server.bossbar.BossBar;
import net.minestom.server.chat.ChatParser;
import net.minestom.server.attribute.Attributes;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.chat.RichMessage;
@ -561,9 +564,9 @@ public class Player extends LivingEntity implements CommandSender {
// Boss bars cache
{
Set<BossBar> bossBars = BossBar.getBossBars(this);
Set<net.minestom.server.bossbar.BossBar> bossBars = net.minestom.server.bossbar.BossBar.getBossBars(this);
if (bossBars != null) {
for (BossBar bossBar : bossBars) {
for (net.minestom.server.bossbar.BossBar bossBar : bossBars) {
bossBar.removeViewer(this);
}
}
@ -1091,6 +1094,16 @@ public class Player extends LivingEntity implements CommandSender {
playerConnection.sendPacket(titlePacket);
}
@Override
public void showBossBar(@NonNull BossBar bar) {
MinecraftServer.getBossBarManager().addBossBar(this, bar);
}
@Override
public void hideBossBar(@NonNull BossBar bar) {
MinecraftServer.getBossBarManager().removeBossBar(this, bar);
}
/**
* Opens a book ui for the player with the given book metadata.
*

View File

@ -15,10 +15,10 @@ public class BossBarPacket implements ServerPacket {
public UUID uuid;
public Action action;
public JsonMessage title; // Only text
public String title;
public float health;
public BarColor color;
public BarDivision division;
public int color;
public int division;
public byte flags;
@ -29,10 +29,10 @@ public class BossBarPacket implements ServerPacket {
switch (action) {
case ADD:
writer.writeSizedString(title.toString());
writer.writeSizedString(title);
writer.writeFloat(health);
writer.writeVarInt(color.ordinal());
writer.writeVarInt(division.ordinal());
writer.writeVarInt(color);
writer.writeVarInt(division);
writer.writeByte(flags);
break;
case REMOVE:
@ -42,11 +42,11 @@ public class BossBarPacket implements ServerPacket {
writer.writeFloat(health);
break;
case UPDATE_TITLE:
writer.writeSizedString(title.toString());
writer.writeSizedString(title);
break;
case UPDATE_STYLE:
writer.writeVarInt(color.ordinal());
writer.writeVarInt(division.ordinal());
writer.writeVarInt(color);
writer.writeVarInt(division);
break;
case UPDATE_FLAGS:
writer.writeByte(flags);