Send all Adventure packets grouped where possible

This commit is contained in:
Kieran Wallbanks 2021-03-25 13:14:09 +00:00
parent 4a0103a795
commit e02a521494
8 changed files with 192 additions and 43 deletions

View File

@ -1,6 +1,7 @@
package net.minestom.server;
import net.kyori.adventure.audience.Audience;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.utils.PacketUtils;
@ -91,7 +92,7 @@ public interface Viewable {
* @return the audience
*/
default @NotNull Audience getViewersAsAudience() {
return Audience.audience(this.getViewersAsAudiences());
return PacketGroupingAudience.of(this.getViewers());
}
/**

View File

@ -10,10 +10,12 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerDisconnectEvent;
import net.minestom.server.network.packet.server.play.BossBarPacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static net.minestom.server.network.packet.server.play.BossBarPacket.Action.*;
@ -50,13 +52,13 @@ public class BossBarManager implements BossBar.Listener {
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);
@ -65,6 +67,49 @@ public class BossBarManager implements BossBar.Listener {
}
}
/**
* Adds the specified players to the boss bar's viewers and spawns the boss bar, registering the
* boss bar if needed.
*
* @param players the players
* @param bar the boss bar
*/
public void addBossBar(@NotNull Collection<Player> players, @NotNull BossBar bar) {
Holder holder = this.getOrCreateHandler(bar);
Collection<Player> addedPlayers = new ArrayList<>();
for (Player player : players) {
if (holder.players.add(player.getUuid())) {
addedPlayers.add(player);
}
}
if (!addedPlayers.isEmpty()) {
PacketUtils.sendGroupedPacket(players, holder.createAddPacket());
}
}
/**
* Removes the specified players from the boss bar's viewers and despawns the boss bar.
*
* @param players the intended viewers
* @param bar the boss bar to hide
*/
public void removeBossBar(@NotNull Collection<Player> players, @NotNull BossBar bar) {
Holder holder = this.getOrCreateHandler(bar);
Collection<Player> removedPlayers = new ArrayList<>();
for (Player player : players) {
if (holder.players.remove(player.getUuid())) {
removedPlayers.add(player);
}
}
if (!removedPlayers.isEmpty()) {
PacketUtils.sendGroupedPacket(players, holder.createRemovePacket());
}
}
@Override
public void bossBarNameChanged(@NotNull BossBar bar, @NotNull Component oldName, @NotNull Component newName) {
Holder holder = this.bars.get(bar);
@ -100,10 +145,11 @@ public class BossBarManager implements BossBar.Listener {
* in the connection manager.
*
* @param packet the packet
* @param players the players
* @param uuids the players
*/
private void updatePlayers(BossBarPacket packet, Set<UUID> players) {
Iterator<UUID> iterator = players.iterator();
private void updatePlayers(BossBarPacket packet, Set<UUID> uuids) {
Iterator<UUID> iterator = uuids.iterator();
Collection<Player> players = new ArrayList<>();
while (iterator.hasNext()) {
Player player = MinecraftServer.getConnectionManager().getPlayer(iterator.next());
@ -111,9 +157,11 @@ public class BossBarManager implements BossBar.Listener {
if (player == null) {
iterator.remove();
} else {
player.getPlayerConnection().sendPacket(packet);
players.add(player);
}
}
PacketUtils.sendGroupedPacket(players, packet);
}
/**

View File

@ -0,0 +1,108 @@
package net.minestom.server.adventure.audience;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.sound.SoundStop;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket;
import net.minestom.server.network.packet.server.play.TitlePacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* An audience implementation that sends grouped packets if possible.
*/
public interface PacketGroupingAudience extends ForwardingAudience {
/**
* Creates a packet grouping audience that wraps a collection of players.
* @param players the players
* @return the audience
*/
static PacketGroupingAudience of(Collection<Player> players) {
return new PacketGroupingAudience() {
@Override
public @NotNull Collection<Player> getPlayers() {
return players;
}
@Override
public @NotNull Iterable<? extends Audience> audiences() {
return players;
}
};
}
/**
* Gets an collection of players this audience contains.
* @return the connections
*/
@NotNull Collection<Player> getPlayers();
@Override
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid()));
}
@Override
default void sendActionBar(@NotNull Component message) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, message));
}
@Override
default void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new PlayerListHeaderAndFooterPacket(header, footer));
}
@Override
default void showTitle(@NotNull Title title) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.SET_TITLE, title.title()));
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.SET_SUBTITLE, title.subtitle()));
}
@Override
default void clearTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.HIDE));
}
@Override
default void resetTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.RESET));
}
@Override
default void showBossBar(@NotNull BossBar bar) {
MinecraftServer.getBossBarManager().addBossBar(this.getPlayers(), bar);
}
@Override
default void hideBossBar(@NotNull BossBar bar) {
MinecraftServer.getBossBarManager().removeBossBar(this.getPlayers(), bar);
}
@Override
default void playSound(@NotNull Sound sound, double x, double y, double z) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundPacket(sound, x, y, z));
}
@Override
default void stopSound(@NotNull SoundStop stop) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundStopPacket(stop));
}
@Override
default @NotNull Iterable<? extends Audience> audiences() {
return this.getPlayers();
}
}

View File

@ -8,6 +8,7 @@ import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.minestom.server.MinecraftServer;
import net.minestom.server.UpdateManager;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.Entity;
@ -58,7 +59,7 @@ import java.util.function.Consumer;
* you need to be sure to signal the {@link UpdateManager} of the changes using
* {@link UpdateManager#signalChunkLoad(Instance, int, int)} and {@link UpdateManager#signalChunkUnload(Instance, int, int)}.
*/
public abstract class Instance implements BlockModifier, EventHandler, DataContainer, ForwardingAudience {
public abstract class Instance implements BlockModifier, EventHandler, DataContainer, PacketGroupingAudience {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
@ -455,6 +456,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
*
* @return an unmodifiable {@link Set} containing all the players in the instance
*/
@Override
@NotNull
public Set<Player> getPlayers() {
return Collections.unmodifiableSet(players);
@ -1105,9 +1107,4 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
public PFInstanceSpace getInstanceSpace() {
return instanceSpace;
}
@Override
public @NotNull Iterable<? extends Audience> audiences() {
return this.getPlayers();
}
}

View File

@ -6,6 +6,8 @@ import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.audience.Audiences;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.fakeplayer.FakePlayer;
@ -33,11 +35,12 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Manages the connected clients.
*/
public final class ConnectionManager implements ForwardingAudience {
public final class ConnectionManager implements PacketGroupingAudience {
private static final long KEEP_ALIVE_DELAY = 10_000;
private static final long KEEP_ALIVE_KICK = 30_000;
@ -145,7 +148,7 @@ public final class ConnectionManager implements ForwardingAudience {
* @param jsonMessage the message to send, probably a {@link net.minestom.server.chat.ColoredText} or {@link net.minestom.server.chat.RichMessage}
* @param condition the condition to receive the message
*
* @deprecated Use {@link Audience#sendMessage(Component)} on {@link #audiences(PlayerValidator)}
* @deprecated Use {@link Audiences#players(Predicate)}
*/
@Deprecated
public void broadcastMessage(@NotNull JsonMessage jsonMessage, @Nullable PlayerValidator condition) {
@ -554,30 +557,7 @@ public final class ConnectionManager implements ForwardingAudience {
}
@Override
public @NotNull Iterable<? extends Audience> audiences() {
public @NotNull Collection<Player> getPlayers() {
return this.getOnlinePlayers();
}
/**
* Gets the audiences of players who match a given validator.
*
* @param validator the validator
*
* @return the audience
*/
public @NotNull Iterable<? extends Audience> audiences(@Nullable PlayerValidator validator) {
if (validator == null) {
return this.audiences();
}
List<Player> validatedPlayers = new ArrayList<>();
for (Player onlinePlayer : this.getOnlinePlayers()) {
if (validator.isValid(onlinePlayer)) {
validatedPlayers.add(onlinePlayer);
}
}
return validatedPlayers;
}
}

View File

@ -4,16 +4,19 @@ import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.text.Component;
import net.minestom.server.Viewable;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket;
import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket;
import net.minestom.server.network.packet.server.play.UpdateScorePacket;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* This interface represents all scoreboard of Minecraft.
*/
public interface Scoreboard extends Viewable, ForwardingAudience {
public interface Scoreboard extends Viewable, PacketGroupingAudience {
/**
* Creates a creation objective packet.
@ -101,7 +104,7 @@ public interface Scoreboard extends Viewable, ForwardingAudience {
String getObjectiveName();
@Override
@NotNull default Iterable<? extends Audience> audiences() {
@NotNull default Collection<Player> getPlayers() {
return this.getViewers();
}
}

View File

@ -7,6 +7,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.LivingEntity;
@ -18,6 +19,7 @@ import net.minestom.server.network.packet.server.play.TeamsPacket.NameTagVisibil
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
@ -25,7 +27,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
/**
* This object represents a team on a scoreboard that has a common display theme and other properties.
*/
public class Team implements ForwardingAudience {
public class Team implements PacketGroupingAudience {
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
@ -570,7 +572,7 @@ public class Team implements ForwardingAudience {
}
@Override
public @NotNull Iterable<? extends Audience> audiences() {
public @NotNull Collection<Player> getPlayers() {
if (!this.isPlayerMembersUpToDate) {
this.playerMembers.clear();

View File

@ -2,11 +2,13 @@ package net.minestom.server.utils.callback.validator;
import org.jetbrains.annotations.NotNull;
import java.util.function.Predicate;
/**
* Interface used when a value needs to be validated dynamically.
*/
@FunctionalInterface
public interface Validator<T> {
public interface Validator<T> extends Predicate<T> {
/**
* Gets if a value is valid based on a condition.
@ -16,4 +18,12 @@ public interface Validator<T> {
*/
boolean isValid(@NotNull T value);
@Override
default boolean test(T t) {
if (t == null) {
return false;
} else {
return this.isValid(t);
}
}
}