From a9d2f4e8ca7f830955e142bbcd40a022640b5e46 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Wed, 5 May 2021 18:21:38 +0100 Subject: [PATCH 1/5] Respect client chat settings --- .../audience/PacketGroupingAudience.java | 5 +- .../net/minestom/server/entity/Player.java | 32 +++-- .../server/listener/ChatMessageListener.java | 28 ++-- .../server/listener/SettingsListener.java | 2 +- .../server/message/ChatMessageType.java | 65 +++++++++ .../minestom/server/message/ChatPosition.java | 79 +++++++++++ .../minestom/server/message/Messenger.java | 126 ++++++++++++++++++ .../client/play/ClientSettingsPacket.java | 7 +- .../packet/server/play/ChatMessagePacket.java | 56 ++------ .../net/minestom/server/utils/Action.java | 17 +++ 10 files changed, 341 insertions(+), 76 deletions(-) create mode 100644 src/main/java/net/minestom/server/message/ChatMessageType.java create mode 100644 src/main/java/net/minestom/server/message/ChatPosition.java create mode 100644 src/main/java/net/minestom/server/message/Messenger.java create mode 100644 src/main/java/net/minestom/server/utils/Action.java diff --git a/src/main/java/net/minestom/server/adventure/audience/PacketGroupingAudience.java b/src/main/java/net/minestom/server/adventure/audience/PacketGroupingAudience.java index f391c8765..2a2329374 100644 --- a/src/main/java/net/minestom/server/adventure/audience/PacketGroupingAudience.java +++ b/src/main/java/net/minestom/server/adventure/audience/PacketGroupingAudience.java @@ -12,7 +12,8 @@ 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.message.ChatPosition; +import net.minestom.server.message.Messenger; import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket; import net.minestom.server.network.packet.server.play.TitlePacket; import net.minestom.server.utils.PacketUtils; @@ -46,7 +47,7 @@ public interface PacketGroupingAudience extends ForwardingAudience { @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())); + Messenger.sendMessage(this.getPlayers(), message, ChatPosition.fromMessageType(type), source.uuid()); } @Override diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 4ed2c950d..dce6aab68 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -38,12 +38,15 @@ import net.minestom.server.event.player.*; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.instance.block.CustomBlock; +import net.minestom.server.message.ChatMessageType; +import net.minestom.server.message.ChatPosition; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.PlayerInventory; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.minestom.server.item.metadata.WrittenBookMeta; import net.minestom.server.listener.PlayerDiggingListener; +import net.minestom.server.message.Messenger; import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.PlayerProvider; @@ -69,7 +72,6 @@ import net.minestom.server.utils.entity.EntityUtils; import net.minestom.server.utils.identity.NamedAndIdentified; import net.minestom.server.utils.instance.InstanceUtils; import net.minestom.server.utils.inventory.PlayerInventoryUtils; -import net.minestom.server.utils.player.PlayerUtils; import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; @@ -739,8 +741,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(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid()); - playerConnection.sendPacket(chatMessagePacket); + Messenger.sendMessage(this, message, ChatPosition.fromMessageType(type), source.uuid()); } /** @@ -2599,6 +2600,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable, RIGHT } + /** + * @deprecated See {@link ChatMessageType} + */ + @Deprecated public enum ChatMode { ENABLED, COMMANDS_ONLY, @@ -2609,7 +2614,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private String locale; private byte viewDistance; - private ChatMode chatMode; + private ChatMessageType chatMessageType; private boolean chatColors; private byte displayedSkinParts; private MainHand mainHand; @@ -2638,7 +2643,16 @@ public class Player extends LivingEntity implements CommandSender, Localizable, * @return the player chat mode */ public ChatMode getChatMode() { - return chatMode; + return ChatMode.values()[chatMessageType.ordinal()]; + } + + /** + * Gets the messages this player wants to receive. + * + * @return the messages + */ + public @NotNull ChatMessageType getChatMessageType() { + return chatMessageType; } /** @@ -2670,19 +2684,19 @@ public class Player extends LivingEntity implements CommandSender, Localizable, * * @param locale the player locale * @param viewDistance the player view distance - * @param chatMode the player chat mode - * @param chatColors the player chat colors + * @param chatMessageType the chat messages the player wishes to receive + * @param chatColors if chat colors should be displayed * @param displayedSkinParts the player displayed skin parts * @param mainHand the player main hand */ - public void refresh(String locale, byte viewDistance, ChatMode chatMode, boolean chatColors, + public void refresh(String locale, byte viewDistance, ChatMessageType chatMessageType, boolean chatColors, byte displayedSkinParts, MainHand mainHand) { final boolean viewDistanceChanged = this.viewDistance != viewDistance; this.locale = locale; this.viewDistance = viewDistance; - this.chatMode = chatMode; + this.chatMessageType = chatMessageType; this.chatColors = chatColors; this.displayedSkinParts = displayedSkinParts; this.mainHand = mainHand; diff --git a/src/main/java/net/minestom/server/listener/ChatMessageListener.java b/src/main/java/net/minestom/server/listener/ChatMessageListener.java index 1f5e56a49..d58525f69 100644 --- a/src/main/java/net/minestom/server/listener/ChatMessageListener.java +++ b/src/main/java/net/minestom/server/listener/ChatMessageListener.java @@ -6,10 +6,10 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerChatEvent; +import net.minestom.server.message.ChatPosition; +import net.minestom.server.message.Messenger; import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.packet.client.play.ClientChatMessagePacket; -import net.minestom.server.network.packet.server.play.ChatMessagePacket; -import net.minestom.server.utils.PacketUtils; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -26,21 +26,26 @@ public class ChatMessageListener { final String cmdPrefix = CommandManager.COMMAND_PREFIX; if (message.startsWith(cmdPrefix)) { // The message is a command - message = message.replaceFirst(cmdPrefix, ""); + final String command = message.replaceFirst(cmdPrefix, ""); - COMMAND_MANAGER.execute(player, message); + // check if we can receive commands + Messenger.receiveCommand(player, () -> COMMAND_MANAGER.execute(player, command)); // Do not call chat event return; } + // check if we can receive messages + if (!Messenger.canReceiveMessage(player)) { + Messenger.sendRejectionMessage(player); + return; + } + final Collection players = CONNECTION_MANAGER.getOnlinePlayers(); - String finalMessage = message; - PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, finalMessage), message); + PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, message), message); // Call the event player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> { - final Function formatFunction = playerChatEvent.getChatFormatFunction(); Component textObject; @@ -55,15 +60,10 @@ public class ChatMessageListener { final Collection recipients = playerChatEvent.getRecipients(); if (!recipients.isEmpty()) { - // Send the message with the correct player UUID - ChatMessagePacket chatMessagePacket = - new ChatMessagePacket(textObject, ChatMessagePacket.Position.CHAT, player.getUuid()); - - PacketUtils.sendGroupedPacket(recipients, chatMessagePacket); + // delegate to the messenger to avoid sending messages we shouldn't be + Messenger.sendMessage(recipients, textObject, ChatPosition.CHAT, player.getUuid()); } - }); - } private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) { diff --git a/src/main/java/net/minestom/server/listener/SettingsListener.java b/src/main/java/net/minestom/server/listener/SettingsListener.java index 7f23840be..3099a8226 100644 --- a/src/main/java/net/minestom/server/listener/SettingsListener.java +++ b/src/main/java/net/minestom/server/listener/SettingsListener.java @@ -8,7 +8,7 @@ public class SettingsListener { public static void listener(ClientSettingsPacket packet, Player player) { Player.PlayerSettings settings = player.getSettings(); - settings.refresh(packet.locale, packet.viewDistance, packet.chatMode, packet.chatColors, packet.displayedSkinParts, packet.mainHand); + settings.refresh(packet.locale, packet.viewDistance, packet.chatMessageType, packet.chatColors, packet.displayedSkinParts, packet.mainHand); PlayerSettingsChangeEvent playerSettingsChangeEvent = new PlayerSettingsChangeEvent(player); player.callEvent(PlayerSettingsChangeEvent.class, playerSettingsChangeEvent); diff --git a/src/main/java/net/minestom/server/message/ChatMessageType.java b/src/main/java/net/minestom/server/message/ChatMessageType.java new file mode 100644 index 000000000..73bf2fffd --- /dev/null +++ b/src/main/java/net/minestom/server/message/ChatMessageType.java @@ -0,0 +1,65 @@ +package net.minestom.server.message; + +import org.jetbrains.annotations.NotNull; + +import java.util.EnumSet; + +/** + * The messages that a player is willing to receive. + */ +public enum ChatMessageType { + /** + * The client wants all chat messages. + */ + FULL(EnumSet.allOf(ChatPosition.class)), + + /** + * The client only wants messages from commands, or system messages. + */ + SYSTEM(EnumSet.of(ChatPosition.SYSTEM_MESSAGE, ChatPosition.GAME_INFO)), + + /** + * The client doesn't want any messages. + */ + NONE(EnumSet.of(ChatPosition.GAME_INFO)); + + private final EnumSet acceptedPositions; + + ChatMessageType(@NotNull EnumSet acceptedPositions) { + this.acceptedPositions = acceptedPositions; + } + + /** + * Checks if this message type is accepting of messages from a given position. + * + * @param chatPosition the position + * @return if the message is accepted + */ + public boolean accepts(@NotNull ChatPosition chatPosition) { + return this.acceptedPositions.contains(chatPosition); + } + + /** + * Gets the packet ID for this chat message type. + * + * @return the packet ID + */ + public int getPacketID() { + return this.ordinal(); + } + + /** + * Gets a chat message type from a packet ID. + * + * @param id the packet ID + * @return the chat message type + */ + public static @NotNull ChatMessageType fromPacketID(int id) { + switch (id) { + case 0: return FULL; + case 1: return SYSTEM; + case 2: return NONE; + default: throw new IllegalArgumentException("id must be between 0-2 (inclusive)"); + } + } +} diff --git a/src/main/java/net/minestom/server/message/ChatPosition.java b/src/main/java/net/minestom/server/message/ChatPosition.java new file mode 100644 index 000000000..345fa0677 --- /dev/null +++ b/src/main/java/net/minestom/server/message/ChatPosition.java @@ -0,0 +1,79 @@ +package net.minestom.server.message; + +import net.kyori.adventure.audience.MessageType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The different positions for chat messages. + */ +public enum ChatPosition { + /** + * A player-initiated chat message. + */ + CHAT(MessageType.CHAT), + + /** + * Feedback from running a command or other system messages. + */ + SYSTEM_MESSAGE(MessageType.SYSTEM), + + /** + * Game state information displayed above the hot bar. + */ + GAME_INFO(null); + + private final MessageType messageType; + + ChatPosition(@NotNull MessageType messageType) { + this.messageType = messageType; + } + + /** + * Gets the Adventure message type from this position. Note that there is no + * message type for {@link #GAME_INFO}, as Adventure uses the title methods for this. + * + * @return the message type, if any + */ + public @Nullable MessageType getMessageType() { + return this.messageType; + } + + /** + * Gets the packet ID of this chat position. + * + * @return the ID + */ + public byte getID() { + return (byte) this.ordinal(); + } + + /** + * Gets a position from an Adventure message type. + * + * @param messageType the message type + * @return the position + */ + public static @NotNull ChatPosition fromMessageType(@NotNull MessageType messageType) { + switch (messageType) { + case CHAT: return CHAT; + case SYSTEM: return SYSTEM_MESSAGE; + } + throw new IllegalArgumentException("Cannot get position from message type!"); + } + + /** + * Gets a position from a packet ID. + * + * @param id the id + * @return the chat position + */ + public static @NotNull ChatPosition fromPacketID(byte id) { + switch (id) { + case 0: return CHAT; + case 1: return SYSTEM_MESSAGE; + case 2: return GAME_INFO; + default: throw new IllegalArgumentException("id must be between 0-2 (inclusive)"); + } + } +} diff --git a/src/main/java/net/minestom/server/message/Messenger.java b/src/main/java/net/minestom/server/message/Messenger.java new file mode 100644 index 000000000..0e5e69bfc --- /dev/null +++ b/src/main/java/net/minestom/server/message/Messenger.java @@ -0,0 +1,126 @@ +package net.minestom.server.message; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.minestom.server.entity.Player; +import net.minestom.server.network.packet.server.play.ChatMessagePacket; +import net.minestom.server.utils.Action; +import net.minestom.server.utils.PacketUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Utility class to handle client chat settings. + */ +public class Messenger { + /** + * The message sent to the client if they send a chat message but it is rejected by the server. + */ + public static final Component CANNOT_SEND_MESSAGE = Component.translatable("chat.cannotSend", NamedTextColor.RED); + + private static final ChatMessagePacket CANNOT_SEND_PACKET = new ChatMessagePacket(CANNOT_SEND_MESSAGE, ChatPosition.SYSTEM_MESSAGE, null); + + /** + * Sends a message to a player, respecting their chat settings. + * + * @param player the player + * @param message the message + * @param position the position + * @param uuid the UUID of the sender, if any + * @return if the message was sent + */ + public static boolean sendMessage(@NotNull Player player, @NotNull Component message, @NotNull ChatPosition position, @Nullable UUID uuid) { + if (player.getSettings().getChatMessageType().accepts(position)) { + player.getPlayerConnection().sendPacket(new ChatMessagePacket(message, position, uuid)); + return true; + } + + return false; + } + + /** + * Sends a message to some players, respecting their chat settings. + * + * @param players the players + * @param message the message + * @param position the position + * @param uuid the UUID of the sender, if any + * @return a set of players who received the message + */ + public static @NotNull Set sendMessage(@NotNull Iterable players, @NotNull Component message, + @NotNull ChatPosition position, @Nullable UUID uuid) { + final Set sentTo = new HashSet<>(); + + for (Player player : players) { + if (player.getSettings().getChatMessageType().accepts(position)) { + sentTo.add(player); + } + } + + PacketUtils.sendGroupedPacket(sentTo, new ChatMessagePacket(message, position, uuid)); + return sentTo; + } + + /** + * Checks if the server should receive messages from a player, given their chat settings. + * + * @param player the player + * @return if the server should receive messages from them + */ + public static boolean canReceiveMessage(@NotNull Player player) { + return player.getSettings().getChatMessageType() == ChatMessageType.FULL; + } + + /** + * Performs an action if the server can receive messages from a player. + * This method will send the rejection message automatically. + * + * @param player the player + * @param action the action + */ + public static void receiveMessage(@NotNull Player player, @NotNull Action action) { + if (canReceiveMessage(player)) { + action.act(); + } else { + sendRejectionMessage(player); + } + } + + /** + * Checks if the server should receive commands from a player, given their chat settings. + * + * @param player the player + * @return if the server should receive commands from them + */ + public static boolean canReceiveCommand(@NotNull Player player) { + return player.getSettings().getChatMessageType() != ChatMessageType.NONE; + } + + /** + * Performs an action if the server can receive commands from a player. + * This method will send the rejection message automatically. + * + * @param player the player + * @param action the action + */ + public static void receiveCommand(@NotNull Player player, @NotNull Action action) { + if (canReceiveCommand(player)) { + action.act(); + } else { + sendRejectionMessage(player); + } + } + + /** + * Sends a message to the player informing them we are rejecting their message or command. + * + * @param player the player + */ + public static void sendRejectionMessage(@NotNull Player player) { + player.getPlayerConnection().sendPacket(CANNOT_SEND_PACKET); + } +} diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientSettingsPacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientSettingsPacket.java index 968216cc9..8af3da577 100644 --- a/src/main/java/net/minestom/server/network/packet/client/play/ClientSettingsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientSettingsPacket.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.client.play; import net.minestom.server.entity.Player; +import net.minestom.server.message.ChatMessageType; import net.minestom.server.network.packet.client.ClientPlayPacket; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; @@ -10,7 +11,7 @@ public class ClientSettingsPacket extends ClientPlayPacket { public String locale = ""; public byte viewDistance; - public Player.ChatMode chatMode = Player.ChatMode.ENABLED; + public ChatMessageType chatMessageType = ChatMessageType.FULL; public boolean chatColors; public byte displayedSkinParts; public Player.MainHand mainHand = Player.MainHand.RIGHT; @@ -19,7 +20,7 @@ public class ClientSettingsPacket extends ClientPlayPacket { public void read(@NotNull BinaryReader reader) { this.locale = reader.readSizedString(128); this.viewDistance = reader.readByte(); - this.chatMode = Player.ChatMode.values()[reader.readVarInt()]; + this.chatMessageType = ChatMessageType.fromPacketID(reader.readVarInt()); this.chatColors = reader.readBoolean(); this.displayedSkinParts = reader.readByte(); this.mainHand = Player.MainHand.values()[reader.readVarInt()]; @@ -31,7 +32,7 @@ public class ClientSettingsPacket extends ClientPlayPacket { throw new IllegalArgumentException("Locale cannot be longer than 128 characters."); writer.writeSizedString(locale); writer.writeByte(viewDistance); - writer.writeVarInt(chatMode.ordinal()); + writer.writeVarInt(chatMessageType.getPacketID()); writer.writeBoolean(chatColors); writer.writeByte(displayedSkinParts); writer.writeVarInt(mainHand.ordinal()); 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 cc9f57195..246edb55d 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 @@ -1,7 +1,7 @@ package net.minestom.server.network.packet.server.play; -import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.text.Component; +import net.minestom.server.message.ChatPosition; import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; +import java.util.Objects; import java.util.UUID; import java.util.function.UnaryOperator; @@ -22,21 +23,19 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket { private static final UUID NULL_UUID = new UUID(0, 0); public Component message; - public Position position; + public ChatPosition position; public UUID uuid; public ChatMessagePacket() { - this(Component.empty(), Position.CHAT); + this.message = Component.empty(); + this.position = ChatPosition.SYSTEM_MESSAGE; + this.uuid = NULL_UUID; } - public ChatMessagePacket(Component message, Position position, UUID uuid) { + public ChatMessagePacket(@NotNull Component message, @NotNull ChatPosition position, @Nullable UUID uuid) { this.message = message; this.position = position; - this.uuid = uuid; - } - - public ChatMessagePacket(Component message, Position position) { - this(message, position, NULL_UUID); + this.uuid = Objects.requireNonNullElse(uuid, NULL_UUID); } @Override @@ -49,7 +48,7 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket { @Override public void read(@NotNull BinaryReader reader) { message = reader.readComponent(Integer.MAX_VALUE); - position = Position.values()[reader.readByte()]; + position = ChatPosition.fromPacketID(reader.readByte()); uuid = reader.readUuid(); } @@ -67,41 +66,4 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket { 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), - GAME_INFO(null); - - private final MessageType messageType; - - Position(MessageType messageType) { - this.messageType = messageType; - } - - /** - * Gets the Adventure message type from this position. Note that there is no - * message type for {@link #GAME_INFO}, as Adventure uses the title methods for this. - * - * @return the message type, if any - */ - public @Nullable MessageType getMessageType() { - return this.messageType; - } - - /** - * Gets a position from an Adventure message type. - * - * @param messageType the message type - * - * @return the position - */ - public static @NotNull Position fromMessageType(@NotNull MessageType messageType) { - switch (messageType) { - case CHAT: return CHAT; - case SYSTEM: return SYSTEM_MESSAGE; - } - throw new IllegalArgumentException("Cannot get position from message type!"); - } - } } diff --git a/src/main/java/net/minestom/server/utils/Action.java b/src/main/java/net/minestom/server/utils/Action.java new file mode 100644 index 000000000..e2fda399b --- /dev/null +++ b/src/main/java/net/minestom/server/utils/Action.java @@ -0,0 +1,17 @@ +package net.minestom.server.utils; + +/** + * A functional interface to perform an action. + */ +@FunctionalInterface +public interface Action { + /** + * An empty action. + */ + Action EMPTY = () -> {}; + + /** + * Performs the action. + */ + void act(); +} From 2404f19fe22c26f68819db27d783f5fe5ee8085a Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 6 May 2021 16:12:46 +0100 Subject: [PATCH 2/5] Avoid NPEs when obtaining chat message type from player --- .../net/minestom/server/entity/Player.java | 4 +++- .../minestom/server/message/Messenger.java | 21 ++++++++++++++----- .../network/player/PlayerConnection.java | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index dce6aab68..5cebbf447 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -2641,7 +2641,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable, * Gets the player chat mode. * * @return the player chat mode + * @deprecated Use {@link #getChatMessageType()} */ + @Deprecated public ChatMode getChatMode() { return ChatMode.values()[chatMessageType.ordinal()]; } @@ -2651,7 +2653,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, * * @return the messages */ - public @NotNull ChatMessageType getChatMessageType() { + public @Nullable ChatMessageType getChatMessageType() { return chatMessageType; } diff --git a/src/main/java/net/minestom/server/message/Messenger.java b/src/main/java/net/minestom/server/message/Messenger.java index 0e5e69bfc..f6c8f263c 100644 --- a/src/main/java/net/minestom/server/message/Messenger.java +++ b/src/main/java/net/minestom/server/message/Messenger.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -34,7 +35,7 @@ public class Messenger { * @return if the message was sent */ public static boolean sendMessage(@NotNull Player player, @NotNull Component message, @NotNull ChatPosition position, @Nullable UUID uuid) { - if (player.getSettings().getChatMessageType().accepts(position)) { + if (getChatMessageType(player).accepts(position)) { player.getPlayerConnection().sendPacket(new ChatMessagePacket(message, position, uuid)); return true; } @@ -56,7 +57,7 @@ public class Messenger { final Set sentTo = new HashSet<>(); for (Player player : players) { - if (player.getSettings().getChatMessageType().accepts(position)) { + if (getChatMessageType(player).accepts(position)) { sentTo.add(player); } } @@ -72,7 +73,7 @@ public class Messenger { * @return if the server should receive messages from them */ public static boolean canReceiveMessage(@NotNull Player player) { - return player.getSettings().getChatMessageType() == ChatMessageType.FULL; + return getChatMessageType(player) == ChatMessageType.FULL; } /** @@ -97,7 +98,7 @@ public class Messenger { * @return if the server should receive commands from them */ public static boolean canReceiveCommand(@NotNull Player player) { - return player.getSettings().getChatMessageType() != ChatMessageType.NONE; + return getChatMessageType(player) != ChatMessageType.NONE; } /** @@ -121,6 +122,16 @@ public class Messenger { * @param player the player */ public static void sendRejectionMessage(@NotNull Player player) { - player.getPlayerConnection().sendPacket(CANNOT_SEND_PACKET); + player.getPlayerConnection().sendPacket(CANNOT_SEND_PACKET, false); + } + + /** + * Gets the chat message type for a player, returning {@link ChatMessageType#FULL} if not set. + * + * @param player the player + * @return the chat message type + */ + private static @NotNull ChatMessageType getChatMessageType(@NotNull Player player) { + return Objects.requireNonNullElse(player.getSettings().getChatMessageType(), ChatMessageType.FULL); } } diff --git a/src/main/java/net/minestom/server/network/player/PlayerConnection.java b/src/main/java/net/minestom/server/network/player/PlayerConnection.java index 730836dbd..0d890c232 100644 --- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java @@ -95,7 +95,7 @@ public abstract class PlayerConnection { } /** - * Serializes the packet and send it to the client, skipping the translation phase. + * Serializes the packet and send it to the client, optionally skipping the translation phase. *

* Also responsible for executing {@link ConnectionManager#onPacketSend(ServerPacketConsumer)} consumers. * From 37f0f797fd9bcfac84e67d490b69efd9a8d36e9c Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 6 May 2021 16:40:31 +0100 Subject: [PATCH 3/5] Add ability to mimic messages from a player with the echo command --- src/test/java/demo/commands/EchoCommand.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/java/demo/commands/EchoCommand.java b/src/test/java/demo/commands/EchoCommand.java index e04800fc6..67932cbee 100644 --- a/src/test/java/demo/commands/EchoCommand.java +++ b/src/test/java/demo/commands/EchoCommand.java @@ -1,24 +1,32 @@ package demo.commands; +import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.arguments.ArgumentType; import net.minestom.server.command.builder.arguments.minecraft.ArgumentComponent; +import net.minestom.server.command.builder.arguments.minecraft.ArgumentUUID; public class EchoCommand extends Command { public EchoCommand() { super("echo"); this.setDefaultExecutor((sender, context) -> sender.sendMessage( - Component.text("Usage: /echo ") + Component.text("Usage: /echo [uuid]") .hoverEvent(Component.text("Click to get this command.") .clickEvent(ClickEvent.suggestCommand("/echo "))))); ArgumentComponent json = ArgumentType.Component("json"); + ArgumentUUID uuid = ArgumentType.UUID("uuid"); this.addSyntax((sender, context) -> { sender.sendMessage(context.get(json)); }, json); + + this.addSyntax((sender, context) -> { + sender.sendMessage(Identity.identity(context.get(uuid)), context.get(json), MessageType.CHAT); + }, uuid, json); } } From cdedcd89d6cb84cfb5edc3888b763e6e2b71a68a Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 17 May 2021 14:46:25 +0100 Subject: [PATCH 4/5] Simplify Messenger implementation --- .../server/listener/ChatMessageListener.java | 4 +- .../minestom/server/message/Messenger.java | 55 ++----------------- .../net/minestom/server/utils/Action.java | 17 ------ 3 files changed, 9 insertions(+), 67 deletions(-) delete mode 100644 src/main/java/net/minestom/server/utils/Action.java diff --git a/src/main/java/net/minestom/server/listener/ChatMessageListener.java b/src/main/java/net/minestom/server/listener/ChatMessageListener.java index d58525f69..0bd9cbe23 100644 --- a/src/main/java/net/minestom/server/listener/ChatMessageListener.java +++ b/src/main/java/net/minestom/server/listener/ChatMessageListener.java @@ -29,7 +29,9 @@ public class ChatMessageListener { final String command = message.replaceFirst(cmdPrefix, ""); // check if we can receive commands - Messenger.receiveCommand(player, () -> COMMAND_MANAGER.execute(player, command)); + if (Messenger.canReceiveCommand(player)) { + COMMAND_MANAGER.execute(player, command); + } // Do not call chat event return; diff --git a/src/main/java/net/minestom/server/message/Messenger.java b/src/main/java/net/minestom/server/message/Messenger.java index f6c8f263c..c4dc90ba1 100644 --- a/src/main/java/net/minestom/server/message/Messenger.java +++ b/src/main/java/net/minestom/server/message/Messenger.java @@ -1,19 +1,15 @@ package net.minestom.server.message; +import java.util.*; + import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.ChatMessagePacket; -import net.minestom.server.utils.Action; import net.minestom.server.utils.PacketUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; - /** * Utility class to handle client chat settings. */ @@ -50,20 +46,11 @@ public class Messenger { * @param message the message * @param position the position * @param uuid the UUID of the sender, if any - * @return a set of players who received the message */ - public static @NotNull Set sendMessage(@NotNull Iterable players, @NotNull Component message, - @NotNull ChatPosition position, @Nullable UUID uuid) { - final Set sentTo = new HashSet<>(); - - for (Player player : players) { - if (getChatMessageType(player).accepts(position)) { - sentTo.add(player); - } - } - - PacketUtils.sendGroupedPacket(sentTo, new ChatMessagePacket(message, position, uuid)); - return sentTo; + public static void sendMessage(@NotNull Collection players, @NotNull Component message, + @NotNull ChatPosition position, @Nullable UUID uuid) { + PacketUtils.sendGroupedPacket(players, new ChatMessagePacket(message, position, uuid), + player -> getChatMessageType(player).accepts(position)); } /** @@ -76,21 +63,6 @@ public class Messenger { return getChatMessageType(player) == ChatMessageType.FULL; } - /** - * Performs an action if the server can receive messages from a player. - * This method will send the rejection message automatically. - * - * @param player the player - * @param action the action - */ - public static void receiveMessage(@NotNull Player player, @NotNull Action action) { - if (canReceiveMessage(player)) { - action.act(); - } else { - sendRejectionMessage(player); - } - } - /** * Checks if the server should receive commands from a player, given their chat settings. * @@ -101,21 +73,6 @@ public class Messenger { return getChatMessageType(player) != ChatMessageType.NONE; } - /** - * Performs an action if the server can receive commands from a player. - * This method will send the rejection message automatically. - * - * @param player the player - * @param action the action - */ - public static void receiveCommand(@NotNull Player player, @NotNull Action action) { - if (canReceiveCommand(player)) { - action.act(); - } else { - sendRejectionMessage(player); - } - } - /** * Sends a message to the player informing them we are rejecting their message or command. * diff --git a/src/main/java/net/minestom/server/utils/Action.java b/src/main/java/net/minestom/server/utils/Action.java deleted file mode 100644 index e2fda399b..000000000 --- a/src/main/java/net/minestom/server/utils/Action.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.minestom.server.utils; - -/** - * A functional interface to perform an action. - */ -@FunctionalInterface -public interface Action { - /** - * An empty action. - */ - Action EMPTY = () -> {}; - - /** - * Performs the action. - */ - void act(); -} From 931806d40a255666578bb28230ff044f03e32b7d Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 17 May 2021 14:48:44 +0100 Subject: [PATCH 5/5] Send rejection message on incorrect command execution --- .../java/net/minestom/server/listener/ChatMessageListener.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/minestom/server/listener/ChatMessageListener.java b/src/main/java/net/minestom/server/listener/ChatMessageListener.java index 0bd9cbe23..cc1f491df 100644 --- a/src/main/java/net/minestom/server/listener/ChatMessageListener.java +++ b/src/main/java/net/minestom/server/listener/ChatMessageListener.java @@ -31,6 +31,8 @@ public class ChatMessageListener { // check if we can receive commands if (Messenger.canReceiveCommand(player)) { COMMAND_MANAGER.execute(player, command); + } else { + Messenger.sendRejectionMessage(player); } // Do not call chat event