mirror of
https://github.com/Minestom/Minestom.git
synced 2024-11-10 21:00:36 +01:00
Chat handling done
This commit is contained in:
parent
d0a7f4544b
commit
533bb49eff
@ -47,6 +47,7 @@ import net.minestom.server.item.metadata.WrittenBookMeta;
|
||||
import net.minestom.server.listener.manager.PacketListenerManager;
|
||||
import net.minestom.server.message.ChatMessageType;
|
||||
import net.minestom.server.message.ChatPosition;
|
||||
import net.minestom.server.message.MessageSender;
|
||||
import net.minestom.server.message.Messenger;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.network.ConnectionState;
|
||||
@ -90,6 +91,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
@ -107,6 +110,7 @@ import java.util.function.UnaryOperator;
|
||||
* You can easily create your own implementation of this and use it with {@link ConnectionManager#setPlayerProvider(PlayerProvider)}.
|
||||
*/
|
||||
public class Player extends LivingEntity implements CommandSender, Localizable, HoverEventSource<ShowEntity>, Identified, NamedAndIdentified {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Player.class);
|
||||
private static final Component REMOVE_MESSAGE = Component.text("You have been removed from the server without reason.", NamedTextColor.RED);
|
||||
private static final int PACKET_PER_TICK = Integer.getInteger("minestom.packet-per-tick", 20);
|
||||
private static final int PACKET_QUEUE_SIZE = Integer.getInteger("minestom.packet-queue-size", 1000);
|
||||
@ -204,6 +208,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
// Adventure
|
||||
private Identity identity;
|
||||
private final Pointers pointers;
|
||||
private Component lastPreviewedMessage;
|
||||
|
||||
public Player(@NotNull UUID uuid, @NotNull String username, @NotNull PlayerConnection playerConnection) {
|
||||
super(EntityType.PLAYER, uuid);
|
||||
@ -664,7 +669,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
|
||||
Messenger.sendMessage(this, message, ChatPosition.fromMessageType(type), source.uuid());
|
||||
if (type == MessageType.SYSTEM)
|
||||
Messenger.sendSystemMessage(this, message, ChatPosition.SYSTEM_MESSAGE);
|
||||
else
|
||||
Messenger.sendMessage(Collections.singleton(this), PlayerChatMessagePacket.unsigned(message,
|
||||
ChatPosition.CHAT, MessageSender.forUnsigned(Component.text("SYSTEM"))));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2061,6 +2070,14 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
playerConnection.sendPacket(on ? ServerDataPacket.TOGGLE_PREVIEW_ON : ServerDataPacket.TOGGLE_PREVIEW_OFF);
|
||||
}
|
||||
|
||||
public @Nullable Component getLastPreviewedMessage() {
|
||||
return lastPreviewedMessage;
|
||||
}
|
||||
|
||||
public void setLastPreviewedMessage(@Nullable Component lastPreviewedMessage) {
|
||||
this.lastPreviewedMessage = lastPreviewedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the main or off hand of the player.
|
||||
*/
|
||||
|
@ -5,13 +5,13 @@ import net.minestom.server.crypto.MessageSignature;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.CancellableEvent;
|
||||
import net.minestom.server.event.trait.PlayerInstanceEvent;
|
||||
import net.minestom.server.message.MessageSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Called every time a {@link Player} write and send something in the chat.
|
||||
@ -21,21 +21,31 @@ public class PlayerChatEvent implements PlayerInstanceEvent, CancellableEvent {
|
||||
|
||||
private final Player player;
|
||||
private final Collection<Player> recipients;
|
||||
private final Supplier<Component> defaultChatFormat;
|
||||
private String message;
|
||||
private final String rawMessage;
|
||||
private Function<PlayerChatEvent, Component> chatFormat;
|
||||
|
||||
private boolean cancelled;
|
||||
private MessageSignature signature;
|
||||
private MessageSender sender;
|
||||
private final Component message;
|
||||
|
||||
public PlayerChatEvent(@NotNull Player player, @NotNull Collection<Player> recipients,
|
||||
@NotNull Supplier<Component> defaultChatFormat,
|
||||
@NotNull String message, @NotNull MessageSignature signature) {
|
||||
@NotNull String rawMessage, @NotNull MessageSignature signature,
|
||||
@NotNull MessageSender sender, @NotNull Component message) {
|
||||
this.player = player;
|
||||
this.recipients = new ArrayList<>(recipients);
|
||||
this.defaultChatFormat = defaultChatFormat;
|
||||
this.rawMessage = rawMessage;
|
||||
this.message = message;
|
||||
this.signature = signature;
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public MessageSender getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(MessageSender sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,17 +73,12 @@ public class PlayerChatEvent implements PlayerInstanceEvent, CancellableEvent {
|
||||
*
|
||||
* @return the sender's message
|
||||
*/
|
||||
public @NotNull String getMessage() {
|
||||
return message;
|
||||
public @NotNull String getRawMessage() {
|
||||
return rawMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to change the message.
|
||||
*
|
||||
* @param message the new message
|
||||
*/
|
||||
public void setMessage(@NotNull String message) {
|
||||
this.message = message;
|
||||
public @NotNull Component getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,10 +92,6 @@ public class PlayerChatEvent implements PlayerInstanceEvent, CancellableEvent {
|
||||
return chatFormat;
|
||||
}
|
||||
|
||||
public @NotNull Supplier<@NotNull Component> getDefaultChatFormat() {
|
||||
return defaultChatFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
|
@ -15,9 +15,10 @@ import net.minestom.server.network.packet.client.play.ClientChatMessagePacket;
|
||||
import net.minestom.server.network.packet.client.play.ClientChatPreviewPacket;
|
||||
import net.minestom.server.network.packet.client.play.ClientCommandChatPacket;
|
||||
import net.minestom.server.network.packet.server.play.ChatPreviewPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import net.minestom.server.network.packet.server.play.PlayerChatMessagePacket;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ChatMessageListener {
|
||||
@ -42,40 +43,47 @@ public class ChatMessageListener {
|
||||
}
|
||||
|
||||
final Collection<Player> players = CONNECTION_MANAGER.getOnlinePlayers();
|
||||
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, message), message, packet.signature());
|
||||
final Component expectedMessage = Objects.requireNonNullElse(player.getLastPreviewedMessage(), Component.text(message));
|
||||
PlayerChatEvent event = new PlayerChatEvent(player, players, message, packet.signature(), MessageSender.forSigned(player), expectedMessage);
|
||||
|
||||
// Call the event
|
||||
EventDispatcher.callCancellable(playerChatEvent, () -> {
|
||||
final Function<PlayerChatEvent, Component> formatFunction = playerChatEvent.getChatFormatFunction();
|
||||
EventDispatcher.callCancellable(event, () -> {
|
||||
player.setLastPreviewedMessage(null);
|
||||
final Collection<Player> recipients = event.getRecipients();
|
||||
if (recipients.isEmpty()) return;
|
||||
|
||||
Component textObject;
|
||||
// TODO Maybe change format to BiFunction<Player, String, Component>?
|
||||
final Function<PlayerChatEvent, Component> formatFunction = event.getChatFormatFunction();
|
||||
|
||||
if (formatFunction != null) {
|
||||
// Custom format
|
||||
textObject = formatFunction.apply(playerChatEvent);
|
||||
// Let the event modify the message
|
||||
if (event.getSender().unsigned()) {
|
||||
// Event handler set unsigned sender, send message as unsigned -> players with
|
||||
// "Only Show Secure Chat" option enabled won't see this message
|
||||
Messenger.sendMessage(event.getRecipients(), PlayerChatMessagePacket
|
||||
.unsigned(formatFunction.apply(event), ChatPosition.CHAT, event.getSender()));
|
||||
} else {
|
||||
// Default format
|
||||
textObject = playerChatEvent.getDefaultChatFormat().get();
|
||||
// Send both version of message -> players will see different versions based on
|
||||
// their "Only Show Secure Chat" option
|
||||
Messenger.sendMessage(event.getRecipients(), PlayerChatMessagePacket
|
||||
.signedWithUnsignedContent(event.getMessage(), formatFunction.apply(event),
|
||||
ChatPosition.CHAT, event.getSender(), event.getSignature()));
|
||||
}
|
||||
|
||||
final Collection<Player> recipients = playerChatEvent.getRecipients();
|
||||
if (!recipients.isEmpty()) {
|
||||
// delegate to the messenger to avoid sending messages we shouldn't be
|
||||
Messenger.sendSignedPlayerMessage(recipients, textObject, ChatPosition.CHAT,
|
||||
new MessageSender(player.getUuid(), player.getDisplayName() != null ? player.getDisplayName() :
|
||||
Component.text(player.getUsername()), player.getTeam() == null ?
|
||||
null : player.getTeam().getTeamDisplayName()), packet.signature());
|
||||
} else {
|
||||
// There is no way the message got modified, send it with the original signature
|
||||
// TODO Should we handle poor design where the signature or sender uuid got altered?
|
||||
Messenger.sendMessage(event.getRecipients(), PlayerChatMessagePacket.signed(event.getMessage(),
|
||||
ChatPosition.CHAT, event.getSender(), event.getSignature()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) {
|
||||
return Component.text(message);
|
||||
}
|
||||
|
||||
public static void previewListener(ClientChatPreviewPacket packet, Player player) {
|
||||
final PlayerChatPreviewEvent event = new PlayerChatPreviewEvent(player, packet.queryId(), packet.query());
|
||||
MinecraftServer.getGlobalEventHandler().callCancellable(event,
|
||||
() -> player.sendPacket(new ChatPreviewPacket(event.getId(), event.getResult())));
|
||||
() -> {
|
||||
player.sendPacket(new ChatPreviewPacket(event.getId(), event.getResult()));
|
||||
player.setLastPreviewedMessage(event.getResult());
|
||||
});
|
||||
}
|
||||
}
|
@ -2,9 +2,11 @@ package net.minestom.server.message;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.crypto.MessageSignature;
|
||||
import net.minestom.server.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public record MessageSender(UUID uuid, @NotNull Component displayName, @Nullable Component teamName) {
|
||||
@ -20,4 +22,22 @@ public record MessageSender(UUID uuid, @NotNull Component displayName, @Nullable
|
||||
public static MessageSender forSigned(@NotNull UUID uuid, @NotNull Component displayName) {
|
||||
return new MessageSender(uuid, displayName, null);
|
||||
}
|
||||
|
||||
public static MessageSender forSigned(Player player) {
|
||||
return player.getTeam() == null ? forSigned(player.getUuid(), Objects.requireNonNullElse(player.getDisplayName(),
|
||||
Component.text(player.getUsername()))) : new MessageSender(player.getUuid(),
|
||||
Objects.requireNonNullElse(player.getDisplayName(), Component.text(player.getUsername())),
|
||||
player.getTeam().getTeamDisplayName());
|
||||
}
|
||||
|
||||
public static MessageSender forUnsigned(Player player) {
|
||||
return player.getTeam() == null ? forUnsigned(Objects.requireNonNullElse(player.getDisplayName(),
|
||||
Component.text(player.getUsername()))) : forUnsigned(
|
||||
Objects.requireNonNullElse(player.getDisplayName(), Component.text(player.getUsername())),
|
||||
player.getTeam().getTeamDisplayName());
|
||||
}
|
||||
|
||||
public boolean unsigned() {
|
||||
return MessageSignature.UNSIGNED_SENDER == uuid;
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,11 @@ package net.minestom.server.message;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minestom.server.crypto.MessageSignature;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.server.play.PlayerChatMessagePacket;
|
||||
import net.minestom.server.network.packet.server.play.SystemChatPacket;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTException;
|
||||
import org.jglrxavpok.hephaistos.parser.SNBTParser;
|
||||
@ -16,7 +14,6 @@ import org.jglrxavpok.hephaistos.parser.SNBTParser;
|
||||
import java.io.StringReader;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -245,13 +242,11 @@ public final class Messenger {
|
||||
* @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
|
||||
*/
|
||||
//fixme
|
||||
public static boolean sendMessage(@NotNull Player player, @NotNull Component message, @NotNull ChatPosition position, @Nullable UUID uuid) {
|
||||
public static boolean sendSystemMessage(@NotNull Player player, @NotNull Component message, @NotNull ChatPosition position) {
|
||||
if (getChatMessageType(player).accepts(position)) {
|
||||
player.sendPacket(new SystemChatPacket(message, 1));
|
||||
player.sendPacket(new SystemChatPacket(message, position.getID()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -261,15 +256,11 @@ public final class Messenger {
|
||||
* Sends a message to some players, respecting their chat settings.
|
||||
*
|
||||
* @param recipients the players
|
||||
* @param message the message
|
||||
* @param position the position
|
||||
*/
|
||||
public static void sendSignedPlayerMessage(@NotNull Collection<Player> recipients, @NotNull Component message,
|
||||
@NotNull ChatPosition position, @NotNull MessageSender sender,
|
||||
@NotNull MessageSignature signature) {
|
||||
public static void sendMessage(@NotNull Collection<Player> recipients, @NotNull PlayerChatMessagePacket packet) {
|
||||
PacketUtils.sendGroupedPacket(recipients.stream().filter(x -> x.getSettings().getChatMessageType() == null ||
|
||||
x.getSettings().getChatMessageType().accepts(position)).collect(Collectors.toList()),
|
||||
PlayerChatMessagePacket.signed(message, position, sender, signature));
|
||||
x.getSettings().getChatMessageType().accepts(ChatPosition.fromPacketID(packet.type())))
|
||||
.collect(Collectors.toList()), packet);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,6 +40,14 @@ public record PlayerChatMessagePacket(@NotNull Component signedContent, @Nullabl
|
||||
sender.displayName(), sender.teamName(), signature);
|
||||
}
|
||||
|
||||
public static PlayerChatMessagePacket signedWithUnsignedContent(@NotNull Component message,
|
||||
@NotNull Component unsignedContent,
|
||||
ChatPosition type, @NotNull MessageSender sender,
|
||||
@NotNull MessageSignature signature) {
|
||||
return new PlayerChatMessagePacket(message, unsignedContent, type.getID(), sender.uuid(),
|
||||
sender.displayName(), sender.teamName(), signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
writer.writeComponent(signedContent);
|
||||
|
Loading…
Reference in New Issue
Block a user