Rework boss bar system

This commit is contained in:
Kieran Wallbanks 2021-03-26 17:06:10 +00:00
parent aa2d1f6397
commit 3628c42ef2
4 changed files with 70 additions and 63 deletions

View File

@ -2,17 +2,16 @@ package net.minestom.server.adventure.bossbar;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.Viewable;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.BossBarPacket;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
import static net.minestom.server.network.packet.server.play.BossBarPacket.Action.*;
@ -24,13 +23,13 @@ import static net.minestom.server.network.packet.server.play.BossBarPacket.Actio
final class BossBarHolder implements Viewable {
final UUID uuid;
final BossBar bar;
final Set<UUID> players;
final Set<Player> players;
boolean registered;
BossBarHolder(@NotNull BossBar bar) {
this.uuid = UUID.randomUUID();
this.bar = bar;
this.players = ConcurrentHashMap.newKeySet();
this.players = new CopyOnWriteArraySet<>();
this.registered = false;
}
@ -88,26 +87,16 @@ final class BossBarHolder implements Viewable {
@Override
public boolean addViewer(@NotNull Player player) {
return this.players.add(player.getUuid());
return this.players.add(player);
}
@Override
public boolean removeViewer(@NotNull Player player) {
return this.players.remove(player.getUuid());
return this.players.remove(player);
}
@Override
public @NotNull Set<Player> getViewers() {
Set<Player> playerList = new HashSet<>();
for (UUID uuid : this.players) {
Player player = MinecraftServer.getConnectionManager().getPlayer(uuid);
if (player != null) {
playerList.add(player);
}
}
return playerList;
return Collections.unmodifiableSet(this.players);
}
}

View File

@ -3,9 +3,11 @@ package net.minestom.server.adventure.bossbar;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.function.Consumer;
/**
* A listener for boss bar updates. This class is not intended for public use and it is
@ -26,31 +28,35 @@ class BossBarListener implements BossBar.Listener {
@Override
public void bossBarNameChanged(@NotNull BossBar bar, @NotNull Component oldName, @NotNull Component newName) {
BossBarHolder holder = this.manager.bars.get(bar);
this.manager.updatePlayers(holder.createTitleUpdate(newName), holder.players);
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createTitleUpdate(newName)));
}
@Override
public void bossBarProgressChanged(@NotNull BossBar bar, float oldProgress, float newProgress) {
BossBarHolder holder = this.manager.bars.get(bar);
this.manager.updatePlayers(holder.createPercentUpdate(newProgress), holder.players);
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createPercentUpdate(newProgress)));
}
@Override
public void bossBarColorChanged(@NotNull BossBar bar, @NotNull BossBar.Color oldColor, @NotNull BossBar.Color newColor) {
BossBarHolder holder = this.manager.bars.get(bar);
this.manager.updatePlayers(holder.createColorUpdate(newColor), holder.players);
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createColorUpdate(newColor)));
}
@Override
public void bossBarOverlayChanged(@NotNull BossBar bar, BossBar.@NotNull Overlay oldOverlay, BossBar.@NotNull Overlay newOverlay) {
BossBarHolder holder = this.manager.bars.get(bar);
this.manager.updatePlayers(holder.createOverlayUpdate(newOverlay), holder.players);
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createOverlayUpdate(newOverlay)));
}
@Override
public void bossBarFlagsChanged(@NotNull BossBar bar, @NotNull Set<BossBar.Flag> flagsAdded, @NotNull Set<BossBar.Flag> flagsRemoved) {
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createFlagsUpdate()));
}
private void doIfRegistered(@NotNull BossBar bar, @NotNull Consumer<BossBarHolder> consumer) {
BossBarHolder holder = this.manager.bars.get(bar);
this.manager.updatePlayers(holder.createFlagsUpdate(), holder.players);
if (holder != null && holder.registered) {
consumer.accept(holder);
}
}
}

View File

@ -13,6 +13,8 @@ import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Manages all boss bars known to this Minestom instance. Although this class can be used
@ -27,9 +29,8 @@ import java.util.*;
* @see Audience#hideBossBar(BossBar)
*/
public class BossBarManager {
private static final int CONCURRENCY_LEVEL = 4;
private final BossBarListener listener;
private final Map<UUID, Set<BossBarHolder>> playerBars;
final Map<BossBar, BossBarHolder> bars;
/**
@ -37,9 +38,8 @@ public class BossBarManager {
*/
public BossBarManager() {
this.listener = new BossBarListener(this);
this.bars = new MapMaker().concurrencyLevel(CONCURRENCY_LEVEL).weakKeys().makeMap();
MinecraftServer.getGlobalEventHandler().addEventCallback(PlayerDisconnectEvent.class, this::onDisconnect);
this.playerBars = new ConcurrentHashMap<>();
this.bars = new ConcurrentHashMap<>();
}
/**
@ -54,6 +54,7 @@ public class BossBarManager {
if (holder.addViewer(player)) {
player.getPlayerConnection().sendPacket(holder.createAddPacket());
this.playerBars.computeIfAbsent(player.getUuid(), uuid -> new HashSet<>()).add(holder);
}
}
@ -68,6 +69,7 @@ public class BossBarManager {
if (holder.removeViewer(player)) {
player.getPlayerConnection().sendPacket(holder.createRemovePacket());
this.removePlayer(player, holder);
}
}
@ -85,6 +87,7 @@ public class BossBarManager {
for (Player player : players) {
if (holder.addViewer(player)) {
addedPlayers.add(player);
this.playerBars.computeIfAbsent(player.getUuid(), uuid -> new HashSet<>()).add(holder);
}
}
@ -106,6 +109,7 @@ public class BossBarManager {
for (Player player : players) {
if (holder.removeViewer(player)) {
removedPlayers.add(player);
this.removePlayer(player, holder);
}
}
@ -115,27 +119,37 @@ public class BossBarManager {
}
/**
* Sends the packet to all players in the set, removing them if they no longer exist
* in the connection manager.
* Completely destroys a boss bar, removing it from all players.
*
* @param packet the packet
* @param uuids the players
* @param bossBar the boss bar
*/
void updatePlayers(BossBarPacket packet, Set<UUID> uuids) {
Iterator<UUID> iterator = uuids.iterator();
Collection<Player> players = new ArrayList<>();
public void destroyBossBar(@NotNull BossBar bossBar) {
BossBarHolder holder = this.bars.remove(bossBar);
while (iterator.hasNext()) {
Player player = MinecraftServer.getConnectionManager().getPlayer(iterator.next());
if (holder != null) {
PacketUtils.sendGroupedPacket(holder.players, holder.createRemovePacket());
if (player == null) {
iterator.remove();
} else {
players.add(player);
for (Player player : holder.players) {
this.removePlayer(player, holder);
}
}
}
PacketUtils.sendGroupedPacket(players, packet);
/**
* Removes a player from all of their boss bars. Note that this method does not
* send any removal packets to the player. It is meant to be used when a player is
* disconnecting from the server.
*
* @param player the player
*/
public void removeAllBossBars(@NotNull Player player) {
Set<BossBarHolder> holders = this.playerBars.remove(player.getUuid());
if (holders != null) {
for (BossBarHolder holder : holders) {
holder.removeViewer(player);
}
}
}
/**
@ -146,25 +160,22 @@ public class BossBarManager {
* @return the handler
*/
private @NotNull BossBarHolder getOrCreateHandler(@NotNull BossBar bar) {
BossBarHolder holder = this.bars.computeIfAbsent(bar, BossBarHolder::new);
if (!holder.registered) {
return this.bars.computeIfAbsent(bar, key -> {
BossBarHolder holder = new BossBarHolder(key);
bar.addListener(this.listener);
holder.registered = true;
}
return holder;
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 (BossBarHolder holder : this.bars.values()) {
holder.players.remove(event.getPlayer().getUuid());
private void removePlayer(Player player, BossBarHolder holder) {
Set<BossBarHolder> holders = this.playerBars.get(player.getUuid());
if (holders != null) {
holders.remove(holder);
if (holders.isEmpty()) {
this.playerBars.remove(player.getUuid());
}
}
}
}

View File

@ -54,7 +54,6 @@ import net.minestom.server.network.player.NettyPlayerConnection;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.recipe.Recipe;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.Registries;
import net.minestom.server.resourcepack.ResourcePack;
import net.minestom.server.scoreboard.BelowNameTag;
import net.minestom.server.scoreboard.Team;
@ -579,6 +578,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
}
}
MinecraftServer.getBossBarManager().removeAllBossBars(this);
// Advancement tabs cache
{
Set<AdvancementTab> advancementTabs = AdvancementTab.getTabs(this);