From 52ce8027f249f521d53ad9cdafa89819440481d0 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 12 Mar 2021 15:33:19 +0000 Subject: [PATCH] Simplify component translation --- .../server/adventure/BossBarManager.java | 20 +-- .../server/adventure/ComponentHolder.java | 39 +++++ .../adventure/LocalizablePacketSender.java | 158 ------------------ .../adventure/SerializationManager.java | 45 ++++- .../net/minestom/server/entity/Player.java | 17 +- .../server/item/metadata/WrittenBookMeta.java | 4 +- .../server/network/ConnectionManager.java | 9 +- .../server/ComponentHoldingServerPacket.java | 8 + .../server/login/LoginDisconnectPacket.java | 18 +- .../server/play/AdvancementsPacket.java | 35 +++- .../packet/server/play/BossBarPacket.java | 40 ++++- .../packet/server/play/ChatMessagePacket.java | 16 +- .../packet/server/play/CombatEventPacket.java | 30 +++- .../packet/server/play/DisconnectPacket.java | 17 +- .../packet/server/play/MapDataPacket.java | 44 ++++- .../packet/server/play/PlayerInfoPacket.java | 85 +++++++++- .../play/PlayerListHeaderAndFooterPacket.java | 24 ++- .../play/ScoreboardObjectivePacket.java | 30 +++- .../packet/server/play/TabCompletePacket.java | 68 +++++++- .../packet/server/play/TeamsPacket.java | 37 +++- .../packet/server/play/TitlePacket.java | 23 ++- .../network/player/NettyPlayerConnection.java | 7 + .../minestom/server/utils/PacketUtils.java | 10 +- 23 files changed, 559 insertions(+), 225 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/ComponentHolder.java delete mode 100644 src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java create mode 100644 src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java index a1574b2bc..67e6db2a1 100644 --- a/src/main/java/net/minestom/server/adventure/BossBarManager.java +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -47,10 +47,7 @@ public class BossBarManager implements BossBar.Listener { Holder holder = this.getOrCreateHandler(bar); if (holder.players.add(player.getUuid())) { - BossBarPacket packet = holder.createAddPacket(); - packet.title = MinecraftServer.getSerializationManager().prepare(packet.title, player); - - player.getPlayerConnection().sendPacket(packet); + player.getPlayerConnection().sendPacket(holder.createAddPacket()); } } /** @@ -108,27 +105,12 @@ public class BossBarManager implements BossBar.Listener { private void updatePlayers(BossBarPacket packet, Set players) { Iterator iterator = players.iterator(); - // check if we need to translate the bossbar - Component rawTitle = packet.title; - boolean translate = false; - if (packet.action == UPDATE_TITLE || packet.action == ADD) { - Component rootTitle = MinecraftServer.getSerializationManager().prepare(rawTitle, MinecraftServer.getSerializationManager().getDefaultLocale()); - - if (!rawTitle.equals(rootTitle)) { - translate = true; - } - } - while (iterator.hasNext()) { Player player = MinecraftServer.getConnectionManager().getPlayer(iterator.next()); if (player == null) { iterator.remove(); } else { - if (translate) { - packet.title = MinecraftServer.getSerializationManager().prepare(rawTitle, player); - } - player.getPlayerConnection().sendPacket(packet); } } diff --git a/src/main/java/net/minestom/server/adventure/ComponentHolder.java b/src/main/java/net/minestom/server/adventure/ComponentHolder.java new file mode 100644 index 000000000..baa08da60 --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/ComponentHolder.java @@ -0,0 +1,39 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +/** + * Represents an object that holds some amount of components. + * @param the holding class + */ +public interface ComponentHolder { + + /** + * Gets the components held by this object. + * @return the components + */ + @NotNull Collection components(); + + /** + * Returns a copy of this object. For each component this object holds, the operator + * is applied to the copy before returning. + * @param operator the operator + * @return the copy + */ + @NotNull T copyWithOperator(@NotNull UnaryOperator operator); + + /** + * Visits each component held by this object. + * @param visitor the visitor + */ + default void visitComponents(@NotNull Consumer visitor) { + for (Component component : this.components()) { + visitor.accept(component); + } + } +} diff --git a/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java b/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java deleted file mode 100644 index 4abf4808a..000000000 --- a/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java +++ /dev/null @@ -1,158 +0,0 @@ -package net.minestom.server.adventure; - -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.title.Title; -import net.minestom.server.MinecraftServer; -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 org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -/** - * Utility class for sending packets with translatable components. All functions in this - * class will send grouped packets if the components do not contain any translatable - * components. In the case that they do, the components are translated and send individually. - */ -public class LocalizablePacketSender { - - /** - * Sends a title to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param title the title - */ - public static void sendGroupedTitle(@NotNull Collection players, @NotNull Title title) { - Component preparedTitle = MinecraftServer.getSerializationManager().prepare(title.title(), MinecraftServer.getSerializationManager().getDefaultLocale()), - preparedSubtitle = MinecraftServer.getSerializationManager().prepare(title.subtitle(), MinecraftServer.getSerializationManager().getDefaultLocale()); - Collection rootPacket = TitlePacket.of(Title.title(preparedTitle, preparedSubtitle, title.times())); - - if (title.title().equals(preparedTitle) && title.subtitle().equals(preparedSubtitle)) { - for (TitlePacket packet : rootPacket) { - PacketUtils.sendGroupedPacket(players, packet); - } - } else { - for (Player player : players) { - Collection packets; - - if (player.getLocale() == null) { - packets = rootPacket; - } else { - packets = TitlePacket.of(Title.title(MinecraftServer.getSerializationManager().prepare(title.title(), player), - MinecraftServer.getSerializationManager().prepare(title.subtitle(), player), title.times())); - } - - for (TitlePacket packet : packets) { - player.getPlayerConnection().sendPacket(packet); - } - } - } - } - - /** - * Sends an action bar to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param component the component - */ - public static void sendGroupedActionBar(@NotNull Collection players, @NotNull Component component) { - Component preparedComponent = MinecraftServer.getSerializationManager().prepare(component, MinecraftServer.getSerializationManager().getDefaultLocale()); - TitlePacket rootPacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, preparedComponent); - - if (component.equals(preparedComponent)) { - PacketUtils.sendGroupedPacket(players, rootPacket); - } else { - for (Player player : players) { - TitlePacket packet; - - if (player.getLocale() == null) { - packet = rootPacket; - } else { - packet = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().prepare(component, player)); - } - - player.getPlayerConnection().sendPacket(packet); - } - } - } - - /** - * Sends a player list to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param header the header - * @param footer the footer - */ - public static void sendGroupedPlayerList(@NotNull Collection players, @Nullable Component header, @Nullable Component footer) { - // empty check first - if (header == null) { - header = Component.empty(); - } - - if (footer == null) { - footer = Component.empty(); - } - - // now back to the packets - Component preparedHeader = MinecraftServer.getSerializationManager().prepare(header, MinecraftServer.getSerializationManager().getDefaultLocale()), - preparedFooter = MinecraftServer.getSerializationManager().prepare(footer, MinecraftServer.getSerializationManager().getDefaultLocale()); - PlayerListHeaderAndFooterPacket rootPacket = new PlayerListHeaderAndFooterPacket(preparedHeader, preparedFooter); - - if (header.equals(preparedHeader) && footer.equals(preparedFooter)) { - PacketUtils.sendGroupedPacket(players, rootPacket); - } else { - for (Player player : players) { - PlayerListHeaderAndFooterPacket packet; - - if (player.getLocale() == null) { - packet = rootPacket; - } else { - packet = new PlayerListHeaderAndFooterPacket(MinecraftServer.getSerializationManager().prepare(header, player), - MinecraftServer.getSerializationManager().prepare(footer, player)); - } - - player.getPlayerConnection().sendPacket(packet); - } - } - } - - /** - * Sends a message to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param source the source of the message - * @param message the message - * @param messageType the type of the message - */ - public static void sendGroupedMessage(@NotNull Collection players, @NotNull Identity source, @NotNull Component message, @NotNull MessageType messageType) { - ChatMessagePacket.Position position = ChatMessagePacket.Position.fromMessageType(messageType); - Component preparedMessage = MinecraftServer.getSerializationManager().prepare(message, MinecraftServer.getSerializationManager().getDefaultLocale()); - ChatMessagePacket rootPacket = new ChatMessagePacket(preparedMessage, position, source.uuid()); - - if (message.equals(preparedMessage)) { - PacketUtils.sendGroupedPacket(players, rootPacket); - } else { - for (Player player : players) { - ChatMessagePacket packet; - - if (player.getLocale() == null) { - packet = rootPacket; - } else { - packet = new ChatMessagePacket(MinecraftServer.getSerializationManager().prepare(message, player), position, source.uuid()); - } - - player.getPlayerConnection().sendPacket(packet); - } - } - } -} diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java index e171cda88..ad2f71f6f 100644 --- a/src/main/java/net/minestom/server/adventure/SerializationManager.java +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -8,6 +8,7 @@ import net.kyori.adventure.translation.TranslationRegistry; import net.kyori.adventure.translation.Translator; import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.Locale; import java.util.Objects; import java.util.function.Function; @@ -51,7 +52,7 @@ public class SerializationManager { /** * Gets the default locale used to translate {@link TranslatableComponent} if, when - * {@link #prepare(Component, Localizable)} is called with a localizable that + * {@link #translate(Component, Localizable)} is called with a localizable that * does not have a locale. * * @return the default locale @@ -62,7 +63,7 @@ public class SerializationManager { /** * Sets the default locale used to translate {@link TranslatableComponent} if, when - * {@link #prepare(Component, Localizable)} is called with a localizable that + * {@link #translate(Component, Localizable)} is called with a localizable that * does not have a locale. * * @param defaultLocale the new default locale @@ -90,7 +91,7 @@ public class SerializationManager { * * @return the prepared component */ - public @NotNull Component prepare(@NotNull Component component, @NotNull Localizable localizable) { + public @NotNull Component translate(@NotNull Component component, @NotNull Localizable localizable) { return GlobalTranslator.renderer().render(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); } @@ -103,7 +104,7 @@ public class SerializationManager { * * @return the prepared component */ - public @NotNull Component prepare(@NotNull Component component, @NotNull Locale locale) { + public @NotNull Component translate(@NotNull Component component, @NotNull Locale locale) { return GlobalTranslator.renderer().render(component, locale); } @@ -126,8 +127,8 @@ public class SerializationManager { * * @return the string */ - public String prepareAndSerialize(@NotNull Component component, @NotNull Localizable localizable) { - return this.prepareAndSerialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); + public String translateAndSerialize(@NotNull Component component, @NotNull Localizable localizable) { + return this.translateAndSerialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); } /** @@ -138,7 +139,35 @@ public class SerializationManager { * * @return the string */ - public String prepareAndSerialize(@NotNull Component component, @NotNull Locale locale) { - return this.serialize(this.prepare(component, locale)); + public String translateAndSerialize(@NotNull Component component, @NotNull Locale locale) { + return this.serialize(this.translate(component, locale)); + } + + /** + * Checks if a component can be translated server-side. This is done by running the + * component through the translator and seeing if the translated component is equal + * to the non translated component. + * @param component the component + * @return {@code true} if the component can be translated server-side, + * {@code false} otherwise + */ + public boolean isTranslatable(@NotNull Component component) { + return !component.equals(this.translate(component, this.getDefaultLocale())); + } + + /** + * Checks if any of a series of components are translatable server-side. + * @param components the components + * @return {@code true} if any of the components can be translated server-side, + * {@code false} otherwise + */ + public boolean areAnyTranslatable(@NotNull Collection components) { + for (Component component : components) { + if (this.isTranslatable(component)) { + return true; + } + } + + return false; } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 09037085d..1340c6fb0 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -508,7 +508,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // #buildDeathScreenText can return null, check here if (deathText != null) { - CombatEventPacket deathPacket = CombatEventPacket.death(this, null, MinecraftServer.getSerializationManager().prepare(deathText, this)); + CombatEventPacket deathPacket = CombatEventPacket.death(this, null, deathText); playerConnection.sendPacket(deathPacket); } @@ -806,7 +806,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().prepare(message, this), ChatMessagePacket.Position.fromMessageType(type), source.uuid()); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid()); playerConnection.sendPacket(chatMessagePacket); } @@ -1009,8 +1009,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) { - PlayerListHeaderAndFooterPacket packet - = new PlayerListHeaderAndFooterPacket(MinecraftServer.getSerializationManager().prepare(header, this), MinecraftServer.getSerializationManager().prepare(footer, this)); + PlayerListHeaderAndFooterPacket packet = new PlayerListHeaderAndFooterPacket(header, footer); playerConnection.sendPacket(packet); } @@ -1084,9 +1083,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void showTitle(@NotNull Title title) { - Component preparedTitle = MinecraftServer.getSerializationManager().prepare(title.title(), this), - preparedSubtitle = MinecraftServer.getSerializationManager().prepare(title.subtitle(), this); - Collection packet = TitlePacket.of(Title.title(preparedTitle, preparedSubtitle, title.times())); + Collection packet = TitlePacket.of(Title.title(title.title(), title.subtitle(), title.times())); for (TitlePacket titlePacket : packet) { playerConnection.sendPacket(titlePacket); @@ -1095,7 +1092,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void sendActionBar(@NotNull Component message) { - TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().prepare(message, this)); + TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, message); playerConnection.sendPacket(titlePacket); } @@ -1924,9 +1921,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // Packet type depends on the current player connection state final ServerPacket disconnectPacket; if (connectionState == ConnectionState.LOGIN) { - disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().prepare(component, this)); + disconnectPacket = new LoginDisconnectPacket(component); } else { - disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().prepare(component, this)); + disconnectPacket = new DisconnectPacket(component); } if (playerConnection instanceof NettyPlayerConnection) { diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java index b160bd59d..1d339fc76 100644 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java @@ -209,8 +209,8 @@ public class WrittenBookMeta extends ItemMeta { WrittenBookMeta meta = new WrittenBookMeta(); meta.resolved = false; meta.generation = WrittenBookGeneration.ORIGINAL; - meta.author = MinecraftServer.getSerializationManager().prepareAndSerialize(book.author(), localizable); - meta.title = MinecraftServer.getSerializationManager().prepareAndSerialize(book.title(), localizable); + meta.author = MinecraftServer.getSerializationManager().translateAndSerialize(book.author(), localizable); + meta.title = MinecraftServer.getSerializationManager().translateAndSerialize(book.title(), localizable); meta.pages = new ArrayList<>(); for (Component page : book.pages()) { diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 6c715fd37..1201b885a 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -3,12 +3,9 @@ package net.minestom.server.network; import io.netty.channel.Channel; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.adventure.LocalizablePacketSender; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.entity.fakeplayer.FakePlayer; @@ -155,8 +152,9 @@ public final class ConnectionManager implements ForwardingAudience { final Collection recipients = getRecipients(condition); if (!recipients.isEmpty()) { - final String jsonText = jsonMessage.toString(); - LocalizablePacketSender.sendGroupedMessage(recipients, Identity.nil(), jsonMessage.asComponent(), MessageType.CHAT); + for (Player recipient : recipients) { + recipient.sendMessage(jsonMessage); + } } } @@ -489,7 +487,6 @@ public final class ConnectionManager implements ForwardingAudience { for (Player player : getOnlinePlayers()) { final PlayerConnection playerConnection = player.getPlayerConnection(); if (playerConnection instanceof NettyPlayerConnection) { - disconnectPacket.message = MinecraftServer.getSerializationManager().prepare(disconnectPacket.message, player); final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) playerConnection; final Channel channel = nettyPlayerConnection.getChannel(); channel.writeAndFlush(disconnectPacket); diff --git a/src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java b/src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java new file mode 100644 index 000000000..7c2d96ff0 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java @@ -0,0 +1,8 @@ +package net.minestom.server.network.packet.server; + +import net.minestom.server.adventure.ComponentHolder; + +/** + * A server packet that can hold components. + */ +public interface ComponentHoldingServerPacket extends ServerPacket, ComponentHolder { } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java index 06e12369e..ff30b48e1 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java @@ -2,14 +2,17 @@ package net.minestom.server.network.packet.server.login; import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; -import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -public class LoginDisconnectPacket implements ServerPacket { +import java.util.Collection; +import java.util.List; +import java.util.function.UnaryOperator; - private final Component kickMessage; // JSON text +public class LoginDisconnectPacket implements ComponentHoldingServerPacket { + public Component kickMessage; public LoginDisconnectPacket(@NotNull Component kickMessage) { this.kickMessage = kickMessage; @@ -33,4 +36,13 @@ public class LoginDisconnectPacket implements ServerPacket { return ServerPacketIdentifier.LOGIN_DISCONNECT; } + @Override + public @NotNull Collection components() { + return List.of(this.kickMessage); + } + + @Override + public @NotNull LoginDisconnectPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new LoginDisconnectPacket(operator.apply(this.kickMessage)); + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java index f6f8f963c..eebc1d9c8 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java @@ -3,13 +3,20 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.minestom.server.advancements.FrameType; import net.minestom.server.item.ItemStack; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.binary.Writeable; import org.jetbrains.annotations.NotNull; -public class AdvancementsPacket implements ServerPacket { +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.UnaryOperator; + +public class AdvancementsPacket implements ComponentHoldingServerPacket { public boolean resetAdvancements; public AdvancementMapping[] advancementMappings; @@ -37,6 +44,32 @@ public class AdvancementsPacket implements ServerPacket { return ServerPacketIdentifier.ADVANCEMENTS; } + @Override + public @NotNull Collection components() { + List components = new ArrayList<>(); + for (AdvancementMapping advancementMapping : advancementMappings) { + components.add(advancementMapping.value.displayData.title); + components.add(advancementMapping.value.displayData.description); + } + return components; + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + AdvancementsPacket packet = new AdvancementsPacket(); + packet.resetAdvancements = this.resetAdvancements; + packet.advancementMappings = Arrays.copyOf(this.advancementMappings, this.advancementMappings.length); + packet.identifiersToRemove = Arrays.copyOf(this.identifiersToRemove, this.identifiersToRemove.length); + packet.progressMappings = Arrays.copyOf(this.progressMappings, this.progressMappings.length); + + for (AdvancementMapping advancementMapping : packet.advancementMappings) { + advancementMapping.value.displayData.title = operator.apply(advancementMapping.value.displayData.title); + advancementMapping.value.displayData.description = operator.apply(advancementMapping.value.displayData.title); + } + + return packet; + } + /** * AdvancementMapping maps the namespaced ID to the Advancement. */ diff --git a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java index 47e4d266a..12699e8a2 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java @@ -3,14 +3,18 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.text.Component; import net.minestom.server.adventure.AdventurePacketConvertor; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.Collections; import java.util.UUID; +import java.util.function.UnaryOperator; -public class BossBarPacket implements ServerPacket { +public class BossBarPacket implements ComponentHoldingServerPacket { public UUID uuid; public Action action; @@ -58,6 +62,40 @@ public class BossBarPacket implements ServerPacket { return ServerPacketIdentifier.BOSS_BAR; } + @Override + public @NotNull Collection components() { + if (title != null) { + return Collections.singleton(title); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + switch (action) { + case UPDATE_TITLE: { + BossBarPacket packet = new BossBarPacket(); + packet.action = action; + packet.uuid = uuid; + packet.title = operator.apply(title); + return packet; + } + case ADD: { + BossBarPacket packet = new BossBarPacket(); + packet.action = action; + packet.uuid = uuid; + packet.title = operator.apply(title); + packet.health = health; + packet.overlay = overlay; + packet.color = color; + packet.flags = flags; + return packet; + } + default: return this; + } + } + public enum Action { ADD, REMOVE, diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChatMessagePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChatMessagePacket.java index 6eb2e559f..8e96bccb7 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ChatMessagePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ChatMessagePacket.java @@ -2,18 +2,22 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Collections; import java.util.UUID; +import java.util.function.UnaryOperator; /** * Represents an outgoing chat message packet. */ -public class ChatMessagePacket implements ServerPacket { +public class ChatMessagePacket implements ComponentHoldingServerPacket { private static final UUID NULL_UUID = new UUID(0, 0); public Component message; @@ -42,6 +46,16 @@ public class ChatMessagePacket implements ServerPacket { return ServerPacketIdentifier.CHAT_MESSAGE; } + @Override + public @NotNull Collection components() { + return Collections.singleton(message); + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new ChatMessagePacket(operator.apply(message), position, uuid); + } + public enum Position { CHAT(MessageType.CHAT), SYSTEM_MESSAGE(MessageType.SYSTEM), diff --git a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java index fb54ad4da..01734bb68 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java @@ -4,16 +4,21 @@ import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.Collections; +import java.util.function.UnaryOperator; + /** * Packet sent during combat to a {@link Player}. * Only death is supported for the moment (other events are ignored anyway as of 1.15.2) */ -public class CombatEventPacket implements ServerPacket { +public class CombatEventPacket implements ComponentHoldingServerPacket { private EventType type; private int duration; @@ -81,6 +86,29 @@ public class CombatEventPacket implements ServerPacket { return ServerPacketIdentifier.COMBAT_EVENT; } + @Override + public @NotNull Collection components() { + if (this.type == EventType.DEATH) { + return Collections.singleton(deathMessage); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (this.type == EventType.DEATH) { + CombatEventPacket packet = new CombatEventPacket(); + packet.type = type; + packet.playerID = playerID; + packet.opponent = opponent; + packet.deathMessage = deathMessage; + return packet; + } else { + return this; + } + } + public enum EventType { ENTER_COMBAT, END_COMBAT, // both ignored by Notchian client DEATH, diff --git a/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java index 4426e16ea..d321a879d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java @@ -2,12 +2,17 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -public class DisconnectPacket implements ServerPacket { +import java.util.Collection; +import java.util.Collections; +import java.util.function.UnaryOperator; + +public class DisconnectPacket implements ComponentHoldingServerPacket { public Component message; /** @@ -35,4 +40,14 @@ public class DisconnectPacket implements ServerPacket { public int getId() { return ServerPacketIdentifier.DISCONNECT; } + + @Override + public @NotNull Collection components() { + return Collections.singleton(message); + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new DisconnectPacket(operator.apply(message)); + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java index 446b96d34..2a7ad74ae 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java @@ -1,12 +1,16 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -public class MapDataPacket implements ServerPacket { +import java.util.*; +import java.util.function.UnaryOperator; + +public class MapDataPacket implements ComponentHoldingServerPacket { public int mapId; public byte scale; @@ -59,6 +63,44 @@ public class MapDataPacket implements ServerPacket { return ServerPacketIdentifier.MAP_DATA; } + @Override + public @NotNull Collection components() { + if (icons == null || icons.length == 0) { + return Collections.emptyList(); + } else { + List components = new ArrayList<>(); + for (Icon icon : icons) { + components.add(icon.displayName); + } + return components; + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (this.icons == null || this.icons.length == 0) { + return this; + } else { + MapDataPacket packet = new MapDataPacket(); + packet.mapId = this.mapId; + packet.scale = this.scale; + packet.trackingPosition = this.trackingPosition; + packet.locked = this.locked; + packet.columns = this.columns; + packet.rows = this.rows; + packet.x = this.x; + packet.z = this.z; + packet.data = this.data; + + packet.icons = Arrays.copyOf(this.icons, this.icons.length); + for (Icon icon : packet.icons) { + icon.displayName = operator.apply(icon.displayName); + } + + return packet; + } + } + public static class Icon { public int type; public byte x, z; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java index 4d0a9b0ec..a28500fe3 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java @@ -1,18 +1,19 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.adventure.ComponentHolder; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.GameMode; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.function.UnaryOperator; -public class PlayerInfoPacket implements ServerPacket { +public class PlayerInfoPacket implements ComponentHoldingServerPacket { public Action action; public List playerInfos; @@ -39,6 +40,40 @@ public class PlayerInfoPacket implements ServerPacket { return ServerPacketIdentifier.PLAYER_INFO; } + @Override + public @NotNull Collection components() { + switch (this.action) { + case ADD_PLAYER: + case UPDATE_DISPLAY_NAME: + List components = new ArrayList<>(); + for (PlayerInfo playerInfo : playerInfos) { + if (playerInfo instanceof ComponentHolder) { + components.addAll(((ComponentHolder) playerInfo).components()); + } + } + return components; + default: return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + switch (this.action) { + case ADD_PLAYER: + case UPDATE_DISPLAY_NAME: + PlayerInfoPacket packet = new PlayerInfoPacket(action); + packet.playerInfos = new ArrayList<>(playerInfos.size()); + for (PlayerInfo playerInfo : playerInfos) { + if (playerInfo instanceof ComponentHolder) { + playerInfos.add(((ComponentHolder) playerInfo).copyWithOperator(operator)); + } else { + playerInfos.add(playerInfo); + } + } + default: return this; + } + } + public enum Action { ADD_PLAYER(AddPlayer.class), @@ -70,7 +105,7 @@ public class PlayerInfoPacket implements ServerPacket { public abstract void write(BinaryWriter writer); } - public static class AddPlayer extends PlayerInfo { + public static class AddPlayer extends PlayerInfo implements ComponentHolder { public String name; public List properties; @@ -102,6 +137,26 @@ public class PlayerInfoPacket implements ServerPacket { writer.writeComponent(displayName); } + @Override + public @NotNull Collection components() { + if (displayName == null) { + return Collections.emptyList(); + } else { + return Collections.singleton(displayName); + } + } + + @Override + public @NotNull AddPlayer copyWithOperator(@NotNull UnaryOperator operator) { + if (displayName == null) { + return this; + } else { + AddPlayer addPlayer = new AddPlayer(uuid, name, gameMode, ping); + addPlayer.displayName = operator.apply(displayName); + return addPlayer; + } + } + public static class Property { public String name; @@ -160,7 +215,7 @@ public class PlayerInfoPacket implements ServerPacket { } } - public static class UpdateDisplayName extends PlayerInfo { + public static class UpdateDisplayName extends PlayerInfo implements ComponentHolder { public Component displayName; @@ -184,6 +239,24 @@ public class PlayerInfoPacket implements ServerPacket { if (hasDisplayName) writer.writeComponent(displayName); } + + @Override + public @NotNull Collection components() { + if (displayName == null) { + return Collections.emptyList(); + } else { + return Collections.singleton(displayName); + } + } + + @Override + public @NotNull UpdateDisplayName copyWithOperator(@NotNull UnaryOperator operator) { + if (displayName == null) { + return this; + } else { + return new UpdateDisplayName(uuid, operator.apply(displayName)); + } + } } public static class RemovePlayer extends PlayerInfo { diff --git a/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java index ae86538a1..6f6ec312b 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java @@ -1,15 +1,20 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Objects; +import java.util.function.UnaryOperator; -public class PlayerListHeaderAndFooterPacket implements ServerPacket { +public class PlayerListHeaderAndFooterPacket implements ComponentHoldingServerPacket { public Component header; public Component footer; @@ -28,4 +33,21 @@ public class PlayerListHeaderAndFooterPacket implements ServerPacket { public int getId() { return ServerPacketIdentifier.PLAYER_LIST_HEADER_AND_FOOTER; } + + @Override + public @NotNull Collection components() { + List components = new ArrayList<>(); + if (header != null) { + components.add(header); + } + if (footer != null) { + components.add(footer); + } + return components; + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new PlayerListHeaderAndFooterPacket(header == null ? null : operator.apply(header), footer == null ? null : operator.apply(footer)); + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java index 756e475ab..ac8bd077d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java @@ -1,12 +1,17 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -public class ScoreboardObjectivePacket implements ServerPacket { +import java.util.Collection; +import java.util.Collections; +import java.util.function.UnaryOperator; + +public class ScoreboardObjectivePacket implements ComponentHoldingServerPacket { /** * An unique name for the objective @@ -43,6 +48,29 @@ public class ScoreboardObjectivePacket implements ServerPacket { return ServerPacketIdentifier.SCOREBOARD_OBJECTIVE; } + @Override + public @NotNull Collection components() { + if (mode == 0 || mode == 2) { + return Collections.singleton(objectiveValue); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (mode == 0 || mode == 2) { + ScoreboardObjectivePacket packet = new ScoreboardObjectivePacket(); + packet.objectiveName = objectiveName; + packet.mode = mode; + packet.objectiveValue = operator.apply(objectiveValue); + packet.type = type; + return packet; + } else { + return this; + } + } + /** * This enumeration represents all available types for the scoreboard objective */ diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java index e78b1dadd..3d4df77b9 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java @@ -1,12 +1,20 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.adventure.ComponentHolder; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -public class TabCompletePacket implements ServerPacket { +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.UnaryOperator; + +public class TabCompletePacket implements ComponentHoldingServerPacket { public int transactionId; public int start; @@ -33,10 +41,66 @@ public class TabCompletePacket implements ServerPacket { return ServerPacketIdentifier.TAB_COMPLETE; } - public static class Match { + @Override + public @NotNull Collection components() { + if (matches == null || matches.length == 0) { + return Collections.emptyList(); + } else { + List components = new ArrayList<>(matches.length); + for (Match match : matches) { + if (match.hasTooltip) { + components.add(match.tooltip); + } + } + return components; + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (matches == null || matches.length == 0) { + return this; + } else { + TabCompletePacket packet = new TabCompletePacket(); + packet.transactionId = transactionId; + packet.start = start; + packet.length = length; + packet.matches = new Match[matches.length]; + + for (int i = 0; i < matches.length; i++) { + packet.matches[i] = matches[i].copyWithOperator(operator); + } + + return packet; + } + } + + public static class Match implements ComponentHolder { public String match; public boolean hasTooltip; public Component tooltip; + + @Override + public @NotNull Collection components() { + if (hasTooltip) { + return Collections.singleton(tooltip); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull Match copyWithOperator(@NotNull UnaryOperator operator) { + if (hasTooltip) { + Match newMatch = new Match(); + newMatch.match = match; + newMatch.hasTooltip = hasTooltip; + newMatch.tooltip = tooltip; + return newMatch; + } else { + return this; + } + } } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java index af698abb1..793a93372 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java @@ -2,15 +2,21 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.minestom.server.color.TeamColor; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.UnaryOperator; + /** * The packet creates or updates teams */ -public class TeamsPacket implements ServerPacket { +public class TeamsPacket implements ComponentHoldingServerPacket { /** * The registry name of the team @@ -100,6 +106,35 @@ public class TeamsPacket implements ServerPacket { return ServerPacketIdentifier.TEAMS; } + @Override + public @NotNull Collection components() { + if (this.action == Action.UPDATE_TEAM_INFO || this.action == Action.CREATE_TEAM) { + return List.of(teamDisplayName, teamPrefix, teamSuffix); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (this.action == Action.UPDATE_TEAM_INFO || this.action == Action.CREATE_TEAM) { + TeamsPacket packet = new TeamsPacket(); + packet.teamName = teamName; + packet.action = action; + packet.teamDisplayName = teamDisplayName == null ? null : operator.apply(teamDisplayName); + packet.friendlyFlags = friendlyFlags; + packet.nameTagVisibility = nameTagVisibility; + packet.collisionRule = collisionRule; + packet.teamColor = teamColor; + packet.teamPrefix = teamPrefix == null ? null : operator.apply(teamPrefix); + packet.teamSuffix = teamSuffix == null ? null : operator.apply(teamSuffix); + packet.entities = entities; + return packet; + } else { + return this; + } + } + /** * An enumeration which representing all actions for the packet */ diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java index 675ec785b..7820e4adb 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java @@ -2,6 +2,7 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.kyori.adventure.title.Title; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.TickUtils; @@ -11,11 +12,13 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.function.UnaryOperator; import static net.minestom.server.network.packet.server.play.TitlePacket.Action.*; -public class TitlePacket implements ServerPacket { +public class TitlePacket implements ComponentHoldingServerPacket { public Action action; @@ -90,6 +93,24 @@ public class TitlePacket implements ServerPacket { return ServerPacketIdentifier.TITLE; } + @Override + public @NotNull Collection components() { + if (action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR) { + return Collections.singleton(payload); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR) { + return new TitlePacket(action, operator.apply(payload)); + } else { + return this; + } + } + public enum Action { SET_TITLE, SET_SUBTITLE, diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 2e62cc817..c5b76d49c 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -13,6 +13,7 @@ import net.minestom.server.network.ConnectionState; import net.minestom.server.network.netty.NettyServer; import net.minestom.server.network.netty.codec.PacketCompressor; import net.minestom.server.network.netty.packet.FramedPacket; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.login.SetCompressionPacket; import net.minestom.server.utils.BufUtils; @@ -25,6 +26,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.crypto.SecretKey; +import java.awt.Component; import java.net.SocketAddress; import java.util.Map; import java.util.UUID; @@ -180,6 +182,11 @@ public class NettyPlayerConnection extends PlayerConnection { return; } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; + + if (getPlayer() != null && serverPacket instanceof ComponentHoldingServerPacket) { + serverPacket = ((ComponentHoldingServerPacket) serverPacket).copyWithOperator(component -> MinecraftServer.getSerializationManager().translate(component, getPlayer())); + } + synchronized (tickBuffer) { PacketUtils.writeFramedPacket(tickBuffer, serverPacket, false); } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index bdf4a9612..0108ecbd7 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -6,6 +6,7 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.network.netty.packet.FramedPacket; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; @@ -45,7 +46,14 @@ public final class PacketUtils { if (players.isEmpty()) return; - if (MinecraftServer.hasGroupedPacket()) { + // work out if the packet needs to be sent individually due to server-side translating + boolean needsTranslating = false; + + if (packet instanceof ComponentHoldingServerPacket) { + needsTranslating = MinecraftServer.getSerializationManager().areAnyTranslatable(((ComponentHoldingServerPacket) packet).components()); + } + + if (MinecraftServer.hasGroupedPacket() && !needsTranslating) { // Send grouped packet... final boolean success = PACKET_LISTENER_MANAGER.processServerPacket(packet, players); if (success) {