Merge pull request #275 from Minestom/respect-client-chat-settings

Respect client chat settings
This commit is contained in:
TheMode 2021-05-17 16:21:08 +02:00 committed by GitHub
commit 0a9fa31804
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 308 additions and 77 deletions

View File

@ -12,7 +12,8 @@ import net.kyori.adventure.title.Title;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.entity.Player; 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.PlayerListHeaderAndFooterPacket;
import net.minestom.server.network.packet.server.play.TitlePacket; import net.minestom.server.network.packet.server.play.TitlePacket;
import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.PacketUtils;
@ -46,7 +47,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override @Override
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { 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 @Override

View File

@ -38,12 +38,15 @@ import net.minestom.server.event.player.*;
import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.CustomBlock; 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.Inventory;
import net.minestom.server.inventory.PlayerInventory; import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import net.minestom.server.item.metadata.WrittenBookMeta; import net.minestom.server.item.metadata.WrittenBookMeta;
import net.minestom.server.listener.PlayerDiggingListener; import net.minestom.server.listener.PlayerDiggingListener;
import net.minestom.server.message.Messenger;
import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.ConnectionState; import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.PlayerProvider; import net.minestom.server.network.PlayerProvider;
@ -730,8 +733,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
@Override @Override
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
ChatMessagePacket chatMessagePacket = new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid()); Messenger.sendMessage(this, message, ChatPosition.fromMessageType(type), source.uuid());
playerConnection.sendPacket(chatMessagePacket);
} }
/** /**
@ -2584,6 +2586,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
RIGHT RIGHT
} }
/**
* @deprecated See {@link ChatMessageType}
*/
@Deprecated
public enum ChatMode { public enum ChatMode {
ENABLED, ENABLED,
COMMANDS_ONLY, COMMANDS_ONLY,
@ -2594,7 +2600,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
private String locale; private String locale;
private byte viewDistance; private byte viewDistance;
private ChatMode chatMode; private ChatMessageType chatMessageType;
private boolean chatColors; private boolean chatColors;
private byte displayedSkinParts; private byte displayedSkinParts;
private MainHand mainHand; private MainHand mainHand;
@ -2625,9 +2631,20 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
* Gets the player chat mode. * Gets the player chat mode.
* *
* @return the player chat mode * @return the player chat mode
* @deprecated Use {@link #getChatMessageType()}
*/ */
@Deprecated
public ChatMode getChatMode() { public ChatMode getChatMode() {
return chatMode; return ChatMode.values()[chatMessageType.ordinal()];
}
/**
* Gets the messages this player wants to receive.
*
* @return the messages
*/
public @Nullable ChatMessageType getChatMessageType() {
return chatMessageType;
} }
/** /**
@ -2659,19 +2676,19 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
* *
* @param locale the player locale * @param locale the player locale
* @param viewDistance the player view distance * @param viewDistance the player view distance
* @param chatMode the player chat mode * @param chatMessageType the chat messages the player wishes to receive
* @param chatColors the player chat colors * @param chatColors if chat colors should be displayed
* @param displayedSkinParts the player displayed skin parts * @param displayedSkinParts the player displayed skin parts
* @param mainHand the player main hand * @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) { byte displayedSkinParts, MainHand mainHand) {
final boolean viewDistanceChanged = this.viewDistance != viewDistance; final boolean viewDistanceChanged = this.viewDistance != viewDistance;
this.locale = locale; this.locale = locale;
this.viewDistance = viewDistance; this.viewDistance = viewDistance;
this.chatMode = chatMode; this.chatMessageType = chatMessageType;
this.chatColors = chatColors; this.chatColors = chatColors;
this.displayedSkinParts = displayedSkinParts; this.displayedSkinParts = displayedSkinParts;
this.mainHand = mainHand; this.mainHand = mainHand;

View File

@ -6,10 +6,10 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandManager; import net.minestom.server.command.CommandManager;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerChatEvent; 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.ConnectionManager;
import net.minestom.server.network.packet.client.play.ClientChatMessagePacket; 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 org.jetbrains.annotations.NotNull;
import java.util.Collection; import java.util.Collection;
@ -26,21 +26,30 @@ public class ChatMessageListener {
final String cmdPrefix = CommandManager.COMMAND_PREFIX; final String cmdPrefix = CommandManager.COMMAND_PREFIX;
if (message.startsWith(cmdPrefix)) { if (message.startsWith(cmdPrefix)) {
// The message is a command // 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
if (Messenger.canReceiveCommand(player)) {
COMMAND_MANAGER.execute(player, command);
} else {
Messenger.sendRejectionMessage(player);
}
// Do not call chat event // Do not call chat event
return; return;
} }
// check if we can receive messages
if (!Messenger.canReceiveMessage(player)) {
Messenger.sendRejectionMessage(player);
return;
}
final Collection<Player> players = CONNECTION_MANAGER.getOnlinePlayers(); final Collection<Player> players = CONNECTION_MANAGER.getOnlinePlayers();
String finalMessage = message; PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, message), message);
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, finalMessage), message);
// Call the event // Call the event
player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> { player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> {
final Function<PlayerChatEvent, Component> formatFunction = playerChatEvent.getChatFormatFunction(); final Function<PlayerChatEvent, Component> formatFunction = playerChatEvent.getChatFormatFunction();
Component textObject; Component textObject;
@ -55,15 +64,10 @@ public class ChatMessageListener {
final Collection<Player> recipients = playerChatEvent.getRecipients(); final Collection<Player> recipients = playerChatEvent.getRecipients();
if (!recipients.isEmpty()) { if (!recipients.isEmpty()) {
// Send the message with the correct player UUID // delegate to the messenger to avoid sending messages we shouldn't be
ChatMessagePacket chatMessagePacket = Messenger.sendMessage(recipients, textObject, ChatPosition.CHAT, player.getUuid());
new ChatMessagePacket(textObject, ChatMessagePacket.Position.CHAT, player.getUuid());
PacketUtils.sendGroupedPacket(recipients, chatMessagePacket);
} }
}); });
} }
private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) { private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) {

View File

@ -8,7 +8,7 @@ public class SettingsListener {
public static void listener(ClientSettingsPacket packet, Player player) { public static void listener(ClientSettingsPacket packet, Player player) {
Player.PlayerSettings settings = player.getSettings(); 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); PlayerSettingsChangeEvent playerSettingsChangeEvent = new PlayerSettingsChangeEvent(player);
player.callEvent(PlayerSettingsChangeEvent.class, playerSettingsChangeEvent); player.callEvent(PlayerSettingsChangeEvent.class, playerSettingsChangeEvent);

View File

@ -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<ChatPosition> acceptedPositions;
ChatMessageType(@NotNull EnumSet<ChatPosition> 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)");
}
}
}

View File

@ -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)");
}
}
}

View File

@ -0,0 +1,94 @@
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.PacketUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* 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 (getChatMessageType(player).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
*/
public static void sendMessage(@NotNull Collection<Player> players, @NotNull Component message,
@NotNull ChatPosition position, @Nullable UUID uuid) {
PacketUtils.sendGroupedPacket(players, new ChatMessagePacket(message, position, uuid),
player -> getChatMessageType(player).accepts(position));
}
/**
* 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 getChatMessageType(player) == ChatMessageType.FULL;
}
/**
* 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 getChatMessageType(player) != ChatMessageType.NONE;
}
/**
* 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, 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);
}
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.network.packet.client.play; package net.minestom.server.network.packet.client.play;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.message.ChatMessageType;
import net.minestom.server.network.packet.client.ClientPlayPacket; import net.minestom.server.network.packet.client.ClientPlayPacket;
import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.binary.BinaryWriter;
@ -10,7 +11,7 @@ public class ClientSettingsPacket extends ClientPlayPacket {
public String locale = ""; public String locale = "";
public byte viewDistance; public byte viewDistance;
public Player.ChatMode chatMode = Player.ChatMode.ENABLED; public ChatMessageType chatMessageType = ChatMessageType.FULL;
public boolean chatColors; public boolean chatColors;
public byte displayedSkinParts; public byte displayedSkinParts;
public Player.MainHand mainHand = Player.MainHand.RIGHT; public Player.MainHand mainHand = Player.MainHand.RIGHT;
@ -19,7 +20,7 @@ public class ClientSettingsPacket extends ClientPlayPacket {
public void read(@NotNull BinaryReader reader) { public void read(@NotNull BinaryReader reader) {
this.locale = reader.readSizedString(128); this.locale = reader.readSizedString(128);
this.viewDistance = reader.readByte(); this.viewDistance = reader.readByte();
this.chatMode = Player.ChatMode.values()[reader.readVarInt()]; this.chatMessageType = ChatMessageType.fromPacketID(reader.readVarInt());
this.chatColors = reader.readBoolean(); this.chatColors = reader.readBoolean();
this.displayedSkinParts = reader.readByte(); this.displayedSkinParts = reader.readByte();
this.mainHand = Player.MainHand.values()[reader.readVarInt()]; 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."); throw new IllegalArgumentException("Locale cannot be longer than 128 characters.");
writer.writeSizedString(locale); writer.writeSizedString(locale);
writer.writeByte(viewDistance); writer.writeByte(viewDistance);
writer.writeVarInt(chatMode.ordinal()); writer.writeVarInt(chatMessageType.getPacketID());
writer.writeBoolean(chatColors); writer.writeBoolean(chatColors);
writer.writeByte(displayedSkinParts); writer.writeByte(displayedSkinParts);
writer.writeVarInt(mainHand.ordinal()); writer.writeVarInt(mainHand.ordinal());

View File

@ -1,7 +1,7 @@
package net.minestom.server.network.packet.server.play; package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.text.Component; 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.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.network.packet.server.ServerPacketIdentifier;
@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
@ -22,21 +23,19 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
private static final UUID NULL_UUID = new UUID(0, 0); private static final UUID NULL_UUID = new UUID(0, 0);
public Component message; public Component message;
public Position position; public ChatPosition position;
public UUID uuid; public UUID uuid;
public ChatMessagePacket() { 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.message = message;
this.position = position; this.position = position;
this.uuid = uuid; this.uuid = Objects.requireNonNullElse(uuid, NULL_UUID);
}
public ChatMessagePacket(Component message, Position position) {
this(message, position, NULL_UUID);
} }
@Override @Override
@ -49,7 +48,7 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
@Override @Override
public void read(@NotNull BinaryReader reader) { public void read(@NotNull BinaryReader reader) {
message = reader.readComponent(Integer.MAX_VALUE); message = reader.readComponent(Integer.MAX_VALUE);
position = Position.values()[reader.readByte()]; position = ChatPosition.fromPacketID(reader.readByte());
uuid = reader.readUuid(); uuid = reader.readUuid();
} }
@ -67,41 +66,4 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) { public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new ChatMessagePacket(operator.apply(message), position, uuid); 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!");
}
}
} }

View File

@ -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.
* <p> * <p>
* Also responsible for executing {@link ConnectionManager#onPacketSend(ServerPacketConsumer)} consumers. * Also responsible for executing {@link ConnectionManager#onPacketSend(ServerPacketConsumer)} consumers.
* *

View File

@ -1,24 +1,32 @@
package demo.commands; 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.Component;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.arguments.ArgumentType; 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.ArgumentComponent;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentUUID;
public class EchoCommand extends Command { public class EchoCommand extends Command {
public EchoCommand() { public EchoCommand() {
super("echo"); super("echo");
this.setDefaultExecutor((sender, context) -> sender.sendMessage( this.setDefaultExecutor((sender, context) -> sender.sendMessage(
Component.text("Usage: /echo <json>") Component.text("Usage: /echo <json> [uuid]")
.hoverEvent(Component.text("Click to get this command.") .hoverEvent(Component.text("Click to get this command.")
.clickEvent(ClickEvent.suggestCommand("/echo "))))); .clickEvent(ClickEvent.suggestCommand("/echo ")))));
ArgumentComponent json = ArgumentType.Component("json"); ArgumentComponent json = ArgumentType.Component("json");
ArgumentUUID uuid = ArgumentType.UUID("uuid");
this.addSyntax((sender, context) -> { this.addSyntax((sender, context) -> {
sender.sendMessage(context.get(json)); sender.sendMessage(context.get(json));
}, json); }, json);
this.addSyntax((sender, context) -> {
sender.sendMessage(Identity.identity(context.get(uuid)), context.get(json), MessageType.CHAT);
}, uuid, json);
} }
} }