Use interface for ChatType, and register using nbt

This commit is contained in:
Noel Németh 2022-06-19 16:40:43 +02:00
parent 418edc02e6
commit 7fe4334c61
13 changed files with 214 additions and 119 deletions

View File

@ -20,7 +20,7 @@ import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule;
import net.minestom.server.message.MessageSender;
import net.minestom.server.message.registry.ChatDecoration;
import net.minestom.server.message.registry.ChatRegistryManager;
import net.minestom.server.message.registry.ChatType;
import net.minestom.server.message.registry.ChatTypeBuilder;
import net.minestom.server.message.registry.CommonChatType;
import net.minestom.server.ping.ResponseData;
import net.minestom.server.utils.identity.NamedAndIdentified;
@ -111,10 +111,13 @@ public class Main {
});
final ChatRegistryManager chatRegistryManager = MinecraftServer.getChatRegistryManager();
chatRegistryManager.addChatType(ChatType.chat(CommonChatType.CHAT.getName(),
ChatDecoration.full("%s | %s> %s", Style.style(NamedTextColor.DARK_RED)).toTextDisplay()));
chatRegistryManager.addChatType(ChatType.chat(CommonChatType.SYSTEM.getName(),
ChatDecoration.content("SYSTEM: %s", Style.style(NamedTextColor.AQUA).font(Key.key("minecraft:uniform"))).toTextDisplay()));
chatRegistryManager.addChatType(ChatTypeBuilder.builder(CommonChatType.CHAT.key())
.chat(ChatDecoration.full("%s | %s> %s", Style.style(NamedTextColor.DARK_RED)))
.build());
chatRegistryManager.addChatType(ChatTypeBuilder.builder(CommonChatType.SYSTEM.key())
.chat(ChatDecoration.content("SYSTEM: %s", Style.style(NamedTextColor.AQUA)
.font(Key.key("minecraft:uniform"))))
.build());
PlayerInit.init();

View File

@ -61,7 +61,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
if (type == MessageType.SYSTEM)
Messenger.sendSystemMessage(getPlayers(), message, CommonChatType.SYSTEM.getId());
Messenger.sendSystemMessage(getPlayers(), message, CommonChatType.SYSTEM);
else
Messenger.sendUnsignedMessage(getPlayers(), new MessageSender(Component.text("UNKNOWN SENDER",
NamedTextColor.RED), null), message);

View File

@ -667,7 +667,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
@Override
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
if (type == MessageType.SYSTEM)
Messenger.sendSystemMessage(List.of(this), message, CommonChatType.SYSTEM.getId());
Messenger.sendSystemMessage(List.of(this), message, CommonChatType.SYSTEM);
else
Messenger.sendUnsignedMessage(List.of(this), new MessageSender(Component.text("UNKNOWN SENDER",
NamedTextColor.RED), null), message);

View File

@ -6,6 +6,7 @@ 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 net.minestom.server.message.registry.ChatType;
import net.minestom.server.message.registry.CommonChatType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -29,7 +30,7 @@ public class PlayerChatEvent implements PlayerInstanceEvent, CancellableEvent {
private MessageSignature signature;
private MessageSender sender;
private final Component message;
private int chatType;
private ChatType chatType;
public PlayerChatEvent(@NotNull Player player, @NotNull Collection<Player> recipients,
@NotNull String rawMessage, @NotNull MessageSignature signature,
@ -40,7 +41,7 @@ public class PlayerChatEvent implements PlayerInstanceEvent, CancellableEvent {
this.message = message;
this.signature = signature;
this.sender = sender;
this.chatType = CommonChatType.CHAT.getId();
this.chatType = CommonChatType.CHAT;
}
public MessageSender getSender() {
@ -118,11 +119,11 @@ public class PlayerChatEvent implements PlayerInstanceEvent, CancellableEvent {
this.signature = signature;
}
public int getChatType() {
public ChatType getChatType() {
return chatType;
}
public void setChatType(int chatType) {
public void setChatType(ChatType chatType) {
this.chatType = chatType;
}
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.message;
import net.kyori.adventure.audience.MessageType;
import net.minestom.server.message.registry.ChatTypeBuilder;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -8,7 +9,7 @@ import org.jetbrains.annotations.Nullable;
/**
* The different positions for chat messages.
*
* @deprecated Replaced by {@link net.minestom.server.message.registry.ChatType}, for more information refer to
* @deprecated Replaced by {@link ChatTypeBuilder}, for more information refer to
* {@link net.minestom.server.message.registry.ChatRegistryManager}. No methods should use this enum, only left
* here to help resolve issues when updating.
*/

View File

@ -4,6 +4,7 @@ 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.message.registry.ChatType;
import net.minestom.server.message.registry.CommonChatType;
import net.minestom.server.network.packet.server.play.PlayerChatMessagePacket;
import net.minestom.server.network.packet.server.play.SystemChatPacket;
@ -22,18 +23,13 @@ public final class Messenger {
private Messenger() {
//no instance
}
private static SystemChatPacket CANNOT_SEND_PACKET;
private static Component REJECTION_MESSAGE = Component.translatable("chat.cannotSend", NamedTextColor.RED);
static {
setRejectionMessage(Component.translatable("chat.cannotSend", NamedTextColor.RED));
}
// TODO Should we add overrides with CommonChatType and/or ChatType (and perform protocol id lookup behind the scenes)?
// TODO Javadoc
public static void sendSignedMessage(@NotNull Collection<Player> recipients, @NotNull MessageSender sender,
@NotNull Component message, @Nullable Component unsignedMessage,
@NotNull MessageSignature signature, int chatType) {
PacketUtils.sendGroupedPacket(recipients, new PlayerChatMessagePacket(message, unsignedMessage, chatType,
@NotNull MessageSignature signature, ChatType chatType) {
PacketUtils.sendGroupedPacket(recipients, new PlayerChatMessagePacket(message, unsignedMessage, chatType.id(),
sender.displayName(), sender.teamName(), signature),
player -> getChatPreference(player) == ChatPreference.FULL);
}
@ -41,39 +37,39 @@ public final class Messenger {
public static void sendSignedMessage(@NotNull Collection<Player> recipients, @NotNull MessageSender sender,
@NotNull Component message, @Nullable Component unsignedMessage,
@NotNull MessageSignature signature) {
sendSignedMessage(recipients, sender, message, unsignedMessage, signature, CommonChatType.CHAT.getId());
sendSignedMessage(recipients, sender, message, unsignedMessage, signature, CommonChatType.CHAT);
}
public static void sendSignedMessage(@NotNull Collection<Player> recipients, @NotNull MessageSender sender,
@NotNull Component message, @NotNull MessageSignature signature,
int chatType) {
ChatType chatType) {
sendSignedMessage(recipients, sender, message, null, signature, chatType);
}
public static void sendSignedMessage(@NotNull Collection<Player> recipients, @NotNull MessageSender sender,
@NotNull Component message, @NotNull MessageSignature signature) {
sendSignedMessage(recipients, sender, message, null, signature, CommonChatType.CHAT.getId());
sendSignedMessage(recipients, sender, message, null, signature, CommonChatType.CHAT);
}
public static void sendUnsignedMessage(@NotNull Collection<Player> recipients, @NotNull MessageSender sender,
@NotNull Component message, int chatType) {
@NotNull Component message, ChatType chatType) {
sendSignedMessage(recipients, sender, message, null, MessageSignature.UNSIGNED, chatType);
}
public static void sendUnsignedMessage(@NotNull Collection<Player> recipients, @NotNull MessageSender sender,
@NotNull Component message) {
sendUnsignedMessage(recipients, sender, message, CommonChatType.CHAT.getId());
sendUnsignedMessage(recipients, sender, message, CommonChatType.CHAT);
}
public static void sendSystemMessage(@NotNull Collection<Player> recipients, @NotNull Component message, int chatType) {
PacketUtils.sendGroupedPacket(recipients, new SystemChatPacket(message, chatType), player -> {
public static void sendSystemMessage(@NotNull Collection<Player> recipients, @NotNull Component message, ChatType chatType) {
PacketUtils.sendGroupedPacket(recipients, new SystemChatPacket(message, chatType.id()), player -> {
final ChatPreference preference = getChatPreference(player);
return preference == ChatPreference.FULL || preference == ChatPreference.SYSTEM;
});
}
public static void sendSystemMessage(@NotNull Collection<Player> recipients, @NotNull Component message) {
sendSystemMessage(recipients, message, CommonChatType.SYSTEM.getId());
sendSystemMessage(recipients, message, CommonChatType.SYSTEM);
}
/**
@ -97,7 +93,7 @@ public final class Messenger {
}
public static void setRejectionMessage(Component rejectionMessage) {
CANNOT_SEND_PACKET = new SystemChatPacket(rejectionMessage, CommonChatType.SYSTEM.getId());
REJECTION_MESSAGE = rejectionMessage;
}
/**
@ -106,7 +102,7 @@ public final class Messenger {
* @param player the player
*/
public static void sendRejectionMessage(@NotNull Player player) {
player.sendPacket(CANNOT_SEND_PACKET);
player.sendPacket(new SystemChatPacket(REJECTION_MESSAGE, CommonChatType.SYSTEM.id()));
}
/**

View File

@ -58,10 +58,6 @@ public record ChatDecoration(@NotNull String translationKey, @NotNull List<Param
return new ChatDecoration(template, PARAM_ALL, style);
}
public TextDisplay toTextDisplay() {
return new TextDisplay(this);
}
@Override
public void write(MutableNBTCompound compound) {
compound.setString("translation_key", translationKey);

View File

@ -7,7 +7,9 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer;
import net.minestom.server.utils.ObjectCache;
import org.intellij.lang.annotations.Subst;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTType;
@ -17,20 +19,18 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* Used to register {@link ChatType chat types} and retrieve their protocol id.
* Used to register {@link ChatTypeBuilder chat types} and retrieve their protocol id.
*/
public final class ChatRegistryManager {
private final AtomicInteger chatTypesId = new AtomicInteger();
private final Int2ObjectMap<ChatType> idToType = new Int2ObjectOpenHashMap<>();
private final Object2IntMap<ChatType> typeToId = new Object2IntOpenHashMap<>();
private final Int2ObjectMap<NBTCompound> idToCompound = new Int2ObjectOpenHashMap<>();
private final Object2IntMap<Key> nameToId = new Object2IntOpenHashMap<>();
private final ObjectCache<NBTCompound> nbtCompoundCache = new ObjectCache<>(() -> {
final MutableNBTCompound root = new MutableNBTCompound();
root.set("type", NBT.String("minecraft:chat_type"));
root.set("value", NBT.List(NBTType.TAG_Compound, idToType.int2ObjectEntrySet().stream().map(x -> {
final MutableNBTCompound compound = new MutableNBTCompound();
root.set("value", NBT.List(NBTType.TAG_Compound, idToCompound.int2ObjectEntrySet().stream().map(x -> {
final MutableNBTCompound compound = new MutableNBTCompound(x.getValue());
compound.setInt("id", x.getIntKey());
x.getValue().write(compound);
return compound.toCompound();
}).collect(Collectors.toList())));
return root.toCompound();
@ -40,25 +40,30 @@ public final class ChatRegistryManager {
* Registers a new ChatType
*
* @param type type to register
* @return the protocol id of this type
* @return the registered type id
*/
public int addChatType(ChatType type) {
@Contract("_ -> new")
public ChatType addChatType(NBTCompound type) {
final int id = chatTypesId.getAndIncrement();
idToType.put(id, type);
typeToId.put(type, id);
if (nameToId.containsKey(type.name())) {
MinecraftServer.LOGGER.warn("A ChatType has already been added with name: '{}'. This will overwrite name based lookups!", type.name());
final @Subst("chat") String name = type.getString("name");
if (name == null) {
throw new IllegalArgumentException("ChatType compound doesn't contain required name tag!");
}
nameToId.put(type.name(), id);
final ChatType chatType = ChatType.of(id, Key.key(name));
idToCompound.put(id, type);
if (nameToId.containsKey(chatType.key())) {
MinecraftServer.LOGGER.warn("A ChatType has already been added with name: '{}'. This will overwrite name based lookups!", chatType.key());
}
nameToId.put(chatType.key(), id);
nbtCompoundCache.invalidate();
return id;
return chatType;
}
/**
* @return the protocol id of the type, -1 if it isn't registered
*/
public int idOf(ChatType type) {
return typeToId.getOrDefault(type, -1);
return idOf(type.key());
}
/**
@ -68,11 +73,8 @@ public final class ChatRegistryManager {
return nameToId.getOrDefault(name, -1);
}
/**
* @return the protocol id of the type, -1 if it isn't registered
*/
public int idOf(CommonChatType type) {
return idOf(type.getName());
public boolean isRegistered(ChatType type) {
return idOf(type) != -1;
}
/**
@ -81,22 +83,25 @@ public final class ChatRegistryManager {
@ApiStatus.Internal
public void initDefaults() {
// Player chat
CommonChatType.CHAT.setId(idOf(CommonChatType.CHAT));
if (!CommonChatType.CHAT.registered()) {
CommonChatType.CHAT.setId(addChatType(ChatType.chat(CommonChatType.CHAT.getName(), ChatDecoration.contentWithSender("chat.type.text").toTextDisplay())));
logDefaultRegistering(CommonChatType.CHAT.getName());
if (!isRegistered(CommonChatType.CHAT)) {
CommonChatType.CHAT.setId(addChatType(ChatTypeBuilder.builder(CommonChatType.CHAT.key())
.chat(ChatDecoration.contentWithSender("chat.type.text")).build()).id());
logDefaultRegistering(CommonChatType.CHAT);
} else {
CommonChatType.CHAT.setId(idOf(CommonChatType.CHAT));
}
// System messages
CommonChatType.SYSTEM.setId(idOf(CommonChatType.SYSTEM));
if (!CommonChatType.SYSTEM.registered()) {
CommonChatType.SYSTEM.setId(addChatType(ChatType.chat(CommonChatType.SYSTEM.getName(), TextDisplay.undecorated())));
logDefaultRegistering(CommonChatType.SYSTEM.getName());
if (!isRegistered(CommonChatType.SYSTEM)) {
CommonChatType.SYSTEM.setId(addChatType(ChatTypeBuilder.builder(CommonChatType.SYSTEM.key()).chat().build()).id());
logDefaultRegistering(CommonChatType.SYSTEM);
} else {
CommonChatType.SYSTEM.setId(idOf(CommonChatType.SYSTEM));
}
// TODO Should we add all default types?
}
private void logDefaultRegistering(Key name) {
MinecraftServer.LOGGER.debug("Registering default ChatType for: {}", name);
private void logDefaultRegistering(ChatType type) {
MinecraftServer.LOGGER.debug("Registering default ChatType for: {}", type.key());
}
public NBTCompound toNBT() {

View File

@ -1,49 +1,13 @@
package net.minestom.server.message.registry;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import static net.minestom.server.message.registry.NBTCompoundWriteable.writeIfPresent;
/**
* Describes a chat type
*
* @param name name of this type
* @param chat if present the message sent with this type will show in chat
* @param overlay if present the message will show in the action bar
* @param narration id present the message can be narrated by the client if it's enabled clientside
*/
public record ChatType(Key name, @Nullable TextDisplay chat, @Nullable TextDisplay overlay, @Nullable Narration narration) implements NBTCompoundWriteable {
public static ChatType chat(Key name, TextDisplay display) {
return chat(name, display, null);
}
public static ChatType chat(Key name, TextDisplay display, Narration narration) {
return new ChatType(name, display, null, narration);
}
public static ChatType actionbar(Key name, TextDisplay display) {
return actionbar(name, display, null);
}
public static ChatType actionbar(Key name, TextDisplay display, Narration narration) {
return new ChatType(name, null, display, narration);
}
public static ChatType narration(Key name, Narration narration) {
return new ChatType(name, null, null, narration);
}
@Override
public void write(MutableNBTCompound compound) {
compound.setString("name", name.asString());
final MutableNBTCompound element = new MutableNBTCompound();
writeIfPresent("chat", chat, element);
writeIfPresent("overlay", overlay, element);
writeIfPresent("narration", narration, element);
compound.set("element", element.toCompound());
public interface ChatType {
static ChatType of(int id, Key key) {
return new ChatTypeImpl(id, key);
}
int id();
Key key();
record ChatTypeImpl(int id, Key key) implements ChatType {}
}

View File

@ -0,0 +1,88 @@
package net.minestom.server.message.registry;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import static net.minestom.server.message.registry.NBTCompoundWriteable.writeIfPresent;
public final class ChatTypeBuilder {
private final Key key;
@Nullable
private TextDisplay chat;
@Nullable
private TextDisplay overlay;
@Nullable
private Narration narration;
public ChatTypeBuilder(Key key) {
this.key = key;
}
@Contract("_ -> new")
public static ChatTypeBuilder builder(Key key) {
return new ChatTypeBuilder(key);
}
@Contract("_ -> this")
public ChatTypeBuilder chat(TextDisplay display) {
this.chat = display;
return this;
}
@Contract("_ -> this")
public ChatTypeBuilder chat(ChatDecoration decoration) {
return chat(new TextDisplay(decoration));
}
@Contract(" -> this")
public ChatTypeBuilder chat() {
return chat(TextDisplay.undecorated());
}
@Contract("_ -> this")
public ChatTypeBuilder overlay(TextDisplay display) {
this.overlay = display;
return this;
}
@Contract("_ -> this")
public ChatTypeBuilder overlay(ChatDecoration decoration) {
return overlay(new TextDisplay(decoration));
}
@Contract(" -> this")
public ChatTypeBuilder overlay() {
return overlay(TextDisplay.undecorated());
}
@Contract("_ -> this")
public ChatTypeBuilder narration(Narration narration) {
this.narration = narration;
return this;
}
@Contract("_ -> this")
public ChatTypeBuilder narration(Narration.Priority priority) {
return narration(new Narration(null, priority));
}
@Contract("_, _ -> this")
public ChatTypeBuilder narration(ChatDecoration decoration, Narration.Priority priority) {
return narration(new Narration(decoration, priority));
}
@Contract(" -> new")
public NBTCompound build() {
final MutableNBTCompound compound = new MutableNBTCompound();
compound.setString("name", key.asString());
final MutableNBTCompound element = new MutableNBTCompound();
writeIfPresent("chat", chat, element);
writeIfPresent("overlay", overlay, element);
writeIfPresent("narration", narration, element);
compound.set("element", element.toCompound());
return compound.toCompound();
}
}

View File

@ -1,33 +1,37 @@
package net.minestom.server.message.registry;
import net.kyori.adventure.key.Key;
import net.minestom.server.utils.FinalObject;
import org.jetbrains.annotations.ApiStatus;
public enum CommonChatType {
public enum CommonChatType implements ChatType {
CHAT(Key.key("minecraft:chat")),
SYSTEM(Key.key("minecraft:system"));
private final Key name;
private int id;
private final FinalObject<Integer> id = new FinalObject<>();
CommonChatType(Key name) {
this.name = name;
}
public Key getName() {
return name;
}
public int getId() {
return id;
}
public boolean registered() {
return getId() != -1;
return id.isSet();
}
@ApiStatus.Internal
public void setId(int id) {
this.id = id;
this.id.set(id);
}
@Override
public int id() {
return this.id.get();
}
@Override
public Key key() {
return name;
}
}

View File

@ -6,7 +6,7 @@ import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import static net.minestom.server.message.registry.NBTCompoundWriteable.writeIfPresent;
/**
* Used to display text either in chat or actionbar via {@link ChatType}
* Used to display text either in chat or actionbar via {@link ChatTypeBuilder}
*
* @param decoration defines how this text will appear, if null only the message will show
*/

View File

@ -0,0 +1,37 @@
package net.minestom.server.utils;
import org.jetbrains.annotations.ApiStatus;
/**
* Used to mimic the final keyword
*
* @param <T> type of the object
*/
@ApiStatus.Internal
public final class FinalObject<T> {
private T obj;
private boolean finalized;
/**
* Set the value
*
* @param object initial value
* @throws RuntimeException if this method called more than once on the same object
*/
public void set(T object) {
if (finalized) {
throw new RuntimeException("Object is already set!");
} else {
this.obj = object;
finalized = true;
}
}
public boolean isSet() {
return obj != null;
}
public T get() {
return obj;
}
}