diff --git a/build.gradle b/build.gradle
index 1ecf25be9..af869d0d8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -182,6 +182,13 @@ dependencies {
generatorsImplementation("com.squareup:javapoet:1.13.0")
+
+ // Adventure, for text messages
+ api platform("net.kyori:adventure-bom:4.5.1")
+ api "net.kyori:adventure-api"
+ implementation "net.kyori:adventure-text-serializer-gson"
+ implementation "net.kyori:adventure-text-serializer-plain"
+ implementation "net.kyori:adventure-text-serializer-legacy"
}
publishing {
diff --git a/src/main/java/net/minestom/server/chat/ChatClickEvent.java b/src/main/java/net/minestom/server/chat/ChatClickEvent.java
index 3f002a44e..331ed1d3c 100644
--- a/src/main/java/net/minestom/server/chat/ChatClickEvent.java
+++ b/src/main/java/net/minestom/server/chat/ChatClickEvent.java
@@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a click event for a specific portion of the message.
*/
+@Deprecated
public class ChatClickEvent {
private final String action;
diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java
index d2bf99a86..c53a4d0ee 100644
--- a/src/main/java/net/minestom/server/chat/ChatColor.java
+++ b/src/main/java/net/minestom/server/chat/ChatColor.java
@@ -18,6 +18,7 @@ import java.util.Map;
*
* Immutable class.
*/
+@Deprecated
public final class ChatColor {
// Special
diff --git a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java
index 191f043ca..a236ff9b4 100644
--- a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java
+++ b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java
@@ -10,6 +10,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound;
/**
* Represents a hover event for a specific portion of the message.
*/
+@Deprecated
public class ChatHoverEvent {
private final String action;
diff --git a/src/main/java/net/minestom/server/chat/ChatParser.java b/src/main/java/net/minestom/server/chat/ChatParser.java
index 6211fd1e0..7628ff9c0 100644
--- a/src/main/java/net/minestom/server/chat/ChatParser.java
+++ b/src/main/java/net/minestom/server/chat/ChatParser.java
@@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Class used to convert JSON string to proper chat message representation.
*/
+@Deprecated
public final class ChatParser {
public static final char COLOR_CHAR = (char) 0xA7; // Represent the character 'ยง'
diff --git a/src/main/java/net/minestom/server/chat/ColoredText.java b/src/main/java/net/minestom/server/chat/ColoredText.java
index 9da56f302..7739a8655 100644
--- a/src/main/java/net/minestom/server/chat/ColoredText.java
+++ b/src/main/java/net/minestom/server/chat/ColoredText.java
@@ -17,6 +17,7 @@ import java.util.regex.Pattern;
* To create one, you simply call one of the static methods like {@link #of(ChatColor, String)},
* you can then continue to append text with {@link #append(ChatColor, String)}.
*/
+@Deprecated
public class ColoredText extends JsonMessage {
private static final char SEPARATOR_START = '{';
diff --git a/src/main/java/net/minestom/server/chat/JsonMessage.java b/src/main/java/net/minestom/server/chat/JsonMessage.java
index 3842b738a..1cfa81a7a 100644
--- a/src/main/java/net/minestom/server/chat/JsonMessage.java
+++ b/src/main/java/net/minestom/server/chat/JsonMessage.java
@@ -3,6 +3,8 @@ package net.minestom.server.chat;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@@ -14,6 +16,7 @@ import java.util.Objects;
*
* @see Chat Format
*/
+@Deprecated
public abstract class JsonMessage {
// true if the compiled string is up-to-date, false otherwise
@@ -49,6 +52,14 @@ public abstract class JsonMessage {
return getTextMessage(getJsonObject()).toString();
}
+ /**
+ * Gets this JSON message as an Adventure Component.
+ * @return the component
+ */
+ public Component asComponent() {
+ return GsonComponentSerializer.gson().deserialize(this.toString());
+ }
+
/**
* Gets the Json representation.
*
@@ -102,6 +113,7 @@ public abstract class JsonMessage {
return message;
}
+ @Deprecated
public static class RawJsonMessage extends JsonMessage {
private final JsonObject jsonObject;
diff --git a/src/main/java/net/minestom/server/chat/RichMessage.java b/src/main/java/net/minestom/server/chat/RichMessage.java
index 97a3e4d0a..986e1727c 100644
--- a/src/main/java/net/minestom/server/chat/RichMessage.java
+++ b/src/main/java/net/minestom/server/chat/RichMessage.java
@@ -19,6 +19,7 @@ import java.util.List;
* events can be assigned with {@link #setClickEvent(ChatClickEvent)} and {@link #setHoverEvent(ChatHoverEvent)}
* and new text element can also be appended {@link #append(ColoredText)}.
*/
+@Deprecated
public class RichMessage extends JsonMessage {
private final List components = new ArrayList<>();
diff --git a/src/main/java/net/minestom/server/chat/TranslatableText.java b/src/main/java/net/minestom/server/chat/TranslatableText.java
index 9be8b7d37..62a88ad2d 100644
--- a/src/main/java/net/minestom/server/chat/TranslatableText.java
+++ b/src/main/java/net/minestom/server/chat/TranslatableText.java
@@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a translatable component which can be used in {@link ColoredText}.
*/
+@Deprecated
public class TranslatableText {
private final String code;
diff --git a/src/main/java/net/minestom/server/command/CommandSender.java b/src/main/java/net/minestom/server/command/CommandSender.java
index b32d49da9..a32561fde 100644
--- a/src/main/java/net/minestom/server/command/CommandSender.java
+++ b/src/main/java/net/minestom/server/command/CommandSender.java
@@ -1,5 +1,7 @@
package net.minestom.server.command;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.permission.PermissionHandler;
@@ -10,20 +12,26 @@ import org.jetbrains.annotations.NotNull;
*
* Main implementations are {@link Player} and {@link ConsoleSender}.
*/
-public interface CommandSender extends PermissionHandler {
+public interface CommandSender extends PermissionHandler, Audience {
/**
* Sends a raw string message.
*
* @param message the message to send
+ *
+ * @deprecated Use {@link #sendMessage(Component)}
*/
+ @Deprecated
void sendMessage(@NotNull String message);
/**
* Sends multiple raw string messages.
*
* @param messages the messages to send
+ *
+ * @deprecated Use {@link #sendMessage(Component)}
*/
+ @Deprecated
default void sendMessage(@NotNull String[] messages) {
for (String message : messages) {
sendMessage(message);
@@ -35,13 +43,12 @@ public interface CommandSender extends PermissionHandler {
* If this is not a {@link Player}, only the content of the message will be sent as a string.
*
* @param text The {@link JsonMessage} to send.
+ *
+ * @deprecated Use {@link #sendMessage(Component)}
* */
+ @Deprecated
default void sendMessage(@NotNull JsonMessage text) {
- if (this instanceof Player) {
- this.sendMessage(text);
- } else {
- sendMessage(text.getRawMessage());
- }
+ this.sendMessage(text.asComponent());
}
/**
diff --git a/src/main/java/net/minestom/server/command/ConsoleSender.java b/src/main/java/net/minestom/server/command/ConsoleSender.java
index 826b78be3..da3480225 100644
--- a/src/main/java/net/minestom/server/command/ConsoleSender.java
+++ b/src/main/java/net/minestom/server/command/ConsoleSender.java
@@ -1,6 +1,11 @@
package net.minestom.server.command;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.identity.Identity;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.minestom.server.permission.Permission;
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,6 +22,11 @@ public class ConsoleSender implements CommandSender {
private final Set permissions = new CopyOnWriteArraySet<>();
+ @Override
+ public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) {
+ LOGGER.info(PlainComponentSerializer.plain().serialize(message));
+ }
+
@Override
public void sendMessage(@NotNull String message) {
LOGGER.info(message);
diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java
index 006be9c9a..4ffd7e79a 100644
--- a/src/main/java/net/minestom/server/entity/Player.java
+++ b/src/main/java/net/minestom/server/entity/Player.java
@@ -1,6 +1,14 @@
package net.minestom.server.entity;
import com.google.common.collect.Queues;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.identity.Identity;
+import net.kyori.adventure.inventory.Book;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.ComponentLike;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.adventure.title.Title;
import net.minestom.server.MinecraftServer;
import net.minestom.server.advancements.AdvancementTab;
import net.minestom.server.attribute.Attribute;
@@ -59,6 +67,7 @@ import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.utils.time.UpdateOption;
import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.DimensionType;
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -766,6 +775,7 @@ public class Player extends LivingEntity implements CommandSender {
* @param message the message to send,
* you can use {@link ColoredText} and/or {@link RichMessage} to create it easily
*/
+ @Override
public void sendMessage(@NotNull JsonMessage message) {
sendJsonMessage(message.toString());
}
@@ -775,7 +785,10 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param text the text with the legacy color formatting
* @param colorChar the color character
+ *
+ * @deprecated Use {@link #sendMessage(Component)}
*/
+ @Deprecated
public void sendLegacyMessage(@NotNull String text, char colorChar) {
ColoredText coloredText = ColoredText.ofLegacy(text, colorChar);
sendJsonMessage(coloredText.toString());
@@ -785,15 +798,26 @@ public class Player extends LivingEntity implements CommandSender {
* Sends a legacy message with the default color char {@link ChatParser#COLOR_CHAR}.
*
* @param text the text with the legacy color formatting
+ *
+ * @deprecated Use {@link #sendMessage(Component)}
*/
+ @Deprecated
public void sendLegacyMessage(@NotNull String text) {
ColoredText coloredText = ColoredText.ofLegacy(text, ChatParser.COLOR_CHAR);
sendJsonMessage(coloredText.toString());
}
+ /**
+ * @deprecated Use {@link #sendMessage(Component)}
+ */
+ @Deprecated
public void sendJsonMessage(@NotNull String json) {
- ChatMessagePacket chatMessagePacket =
- new ChatMessagePacket(json, ChatMessagePacket.Position.CHAT);
+ this.sendMessage(json);
+ }
+
+ @Override
+ public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) {
+ ChatMessagePacket chatMessagePacket = new ChatMessagePacket(GsonComponentSerializer.gson().serialize(message), type, source.uuid());
playerConnection.sendPacket(chatMessagePacket);
}
@@ -926,13 +950,21 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param header the header text, null to set empty
* @param footer the footer text, null to set empty
+ *
+ * @deprecated Use {@link #sendPlayerListHeaderAndFooter(Component, Component)}
*/
+ @Deprecated
public void sendHeaderFooter(@Nullable JsonMessage header, @Nullable JsonMessage footer) {
- PlayerListHeaderAndFooterPacket playerListHeaderAndFooterPacket = new PlayerListHeaderAndFooterPacket();
- playerListHeaderAndFooterPacket.header = header;
- playerListHeaderAndFooterPacket.footer = footer;
+ this.sendPlayerListHeaderAndFooter(header == null ? Component.empty() : header.asComponent(),
+ footer == null ? Component.empty() : footer.asComponent());
+ }
- playerConnection.sendPacket(playerListHeaderAndFooterPacket);
+ @Override
+ public void sendPlayerListHeaderAndFooter(@NonNull Component header, @NonNull Component footer) {
+ playerConnection.sendPacket(new PlayerListHeaderAndFooterPacket(
+ GsonComponentSerializer.gson().serialize(header),
+ GsonComponentSerializer.gson().serialize(footer)
+ ));
}
/**
@@ -941,25 +973,12 @@ public class Player extends LivingEntity implements CommandSender {
* @param text the text of the title
* @param action the action of the title (where to show it)
* @see #sendTitleTime(int, int, int) to specify the display time
+ *
+ * @deprecated Use {@link #showTitle(Title)} and {@link #sendActionBar(Component)}
*/
+ @Deprecated
private void sendTitle(@NotNull JsonMessage text, @NotNull TitlePacket.Action action) {
- TitlePacket titlePacket = new TitlePacket();
- titlePacket.action = action;
-
- switch (action) {
- case SET_TITLE:
- titlePacket.titleText = text;
- break;
- case SET_SUBTITLE:
- titlePacket.subtitleText = text;
- break;
- case SET_ACTION_BAR:
- titlePacket.actionBarText = text;
- break;
- default:
- throw new UnsupportedOperationException("Invalid TitlePacket.Action type!");
- }
-
+ TitlePacket titlePacket = new TitlePacket(action, text.toString());
playerConnection.sendPacket(titlePacket);
}
@@ -969,10 +988,12 @@ public class Player extends LivingEntity implements CommandSender {
* @param title the title message
* @param subtitle the subtitle message
* @see #sendTitleTime(int, int, int) to specify the display time
+ *
+ * @deprecated Use {@link #showTitle(Title)}
*/
+ @Deprecated
public void sendTitleSubtitleMessage(@NotNull JsonMessage title, @NotNull JsonMessage subtitle) {
- sendTitle(title, TitlePacket.Action.SET_TITLE);
- sendTitle(subtitle, TitlePacket.Action.SET_SUBTITLE);
+ this.showTitle(Title.title(title.asComponent(), subtitle.asComponent()));
}
/**
@@ -980,9 +1001,12 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param title the title message
* @see #sendTitleTime(int, int, int) to specify the display time
+ *
+ * @deprecated Use {@link #showTitle(Title)}
*/
+ @Deprecated
public void sendTitleMessage(@NotNull JsonMessage title) {
- sendTitle(title, TitlePacket.Action.SET_TITLE);
+ this.showTitle(Title.title(title.asComponent(), Component.empty()));
}
/**
@@ -990,9 +1014,12 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param subtitle the subtitle message
* @see #sendTitleTime(int, int, int) to specify the display time
+ *
+ * @deprecated Use {@link #showTitle(Title)}
*/
+ @Deprecated
public void sendSubtitleMessage(@NotNull JsonMessage subtitle) {
- sendTitle(subtitle, TitlePacket.Action.SET_SUBTITLE);
+ this.showTitle(Title.title(Component.empty(), subtitle.asComponent()));
}
/**
@@ -1000,9 +1027,25 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param actionBar the action bar message
* @see #sendTitleTime(int, int, int) to specify the display time
+ *
+ * @deprecated Use {@link #sendActionBar(Component)}
*/
+ @Deprecated
public void sendActionBarMessage(@NotNull JsonMessage actionBar) {
- sendTitle(actionBar, TitlePacket.Action.SET_ACTION_BAR);
+ this.sendActionBar(actionBar.asComponent());
+ }
+
+ @Override
+ public void showTitle(@NonNull Title title) {
+ for (TitlePacket titlePacket : TitlePacket.of(title)) {
+ playerConnection.sendPacket(titlePacket);
+ }
+ }
+
+ @Override
+ public void sendActionBar(@NonNull Component message) {
+ TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, GsonComponentSerializer.gson().serialize(message));
+ playerConnection.sendPacket(titlePacket);
}
/**
@@ -1011,31 +1054,40 @@ public class Player extends LivingEntity implements CommandSender {
* @param fadeIn ticks to spend fading in
* @param stay ticks to keep the title displayed
* @param fadeOut ticks to spend out, not when to start fading out
+ *
+ * @deprecated Use {@link #showTitle(Title)}. Note that this will overwrite the
+ * existing title. This is expected behavior and will be the case in 1.17.
*/
+ @Deprecated
public void sendTitleTime(int fadeIn, int stay, int fadeOut) {
- TitlePacket titlePacket = new TitlePacket();
- titlePacket.action = TitlePacket.Action.SET_TIMES_AND_DISPLAY;
- titlePacket.fadeIn = fadeIn;
- titlePacket.stay = stay;
- titlePacket.fadeOut = fadeOut;
+ TitlePacket titlePacket = new TitlePacket(fadeIn, stay, fadeOut);
playerConnection.sendPacket(titlePacket);
}
/**
* Hides the previous title.
+ * @deprecated Use {@link #clearTitle()}. Note this title cannot be shown again. This
+ * is expected behavior and will be the case in 1.17.
*/
+ @Deprecated
public void hideTitle() {
- TitlePacket titlePacket = new TitlePacket();
- titlePacket.action = TitlePacket.Action.HIDE;
+ TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.HIDE);
playerConnection.sendPacket(titlePacket);
}
/**
* Resets the previous title.
+ * @deprecated Use {@link #clearTitle()}. Note this title cannot be shown again. This
+ * is expected behavior and will be the case in 1.17.
*/
public void resetTitle() {
- TitlePacket titlePacket = new TitlePacket();
- titlePacket.action = TitlePacket.Action.RESET;
+ TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.RESET);
+ playerConnection.sendPacket(titlePacket);
+ }
+
+ @Override
+ public void clearTitle() {
+ TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.RESET);
playerConnection.sendPacket(titlePacket);
}
@@ -1043,7 +1095,10 @@ public class Player extends LivingEntity implements CommandSender {
* Opens a book ui for the player with the given book metadata.
*
* @param bookMeta The metadata of the book to open
+ *
+ * @deprecated Use {@link #openBook(Book)}
*/
+ @Deprecated
public void openBook(@NotNull WrittenBookMeta bookMeta) {
// Set book in offhand
final ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1);
@@ -1063,6 +1118,11 @@ public class Player extends LivingEntity implements CommandSender {
this.inventory.update();
}
+ @Override
+ public void openBook(@NonNull Book book) {
+ // TODO write the book
+ }
+
@Override
public boolean isImmune(@NotNull DamageType type) {
if (!getGameMode().canTakeDamage()) {
@@ -1730,16 +1790,40 @@ public class Player extends LivingEntity implements CommandSender {
* Kicks the player with a reason.
*
* @param text the kick reason
+ *
+ * @deprecated Use {@link #kick(Component)}
*/
+ @Deprecated
public void kick(@NotNull JsonMessage text) {
+ this.kick(text.asComponent());
+ }
+
+ /**
+ * Kicks the player with a reason.
+ *
+ * @param message the kick reason
+ *
+ * @deprecated Use {@link #kick(Component)}
+ */
+ @Deprecated
+ public void kick(@NotNull String message) {
+ this.kick(Component.text(message));
+ }
+
+ /**
+ * Kicks the player with a reason.
+ *
+ * @param component the reason
+ */
+ public void kick(@NotNull Component component) {
final ConnectionState connectionState = playerConnection.getConnectionState();
// Packet type depends on the current player connection state
final ServerPacket disconnectPacket;
if (connectionState == ConnectionState.LOGIN) {
- disconnectPacket = new LoginDisconnectPacket(text);
+ disconnectPacket = new LoginDisconnectPacket(GsonComponentSerializer.gson().serialize(component));
} else {
- disconnectPacket = new DisconnectPacket(text);
+ disconnectPacket = new DisconnectPacket(GsonComponentSerializer.gson().serialize(component));
}
if (playerConnection instanceof NettyPlayerConnection) {
@@ -1751,15 +1835,6 @@ public class Player extends LivingEntity implements CommandSender {
}
}
- /**
- * Kicks the player with a reason.
- *
- * @param message the kick reason
- */
- public void kick(@NotNull String message) {
- kick(ColoredText.of(message));
- }
-
/**
* Changes the current held slot for the player.
*
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 02d096e76..a498275d8 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,32 +1,68 @@
package net.minestom.server.network.packet.server.play;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.text.Component;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.UUID;
+/**
+ * Represents an outgoing chat message packet. Do not use this to send messages above the
+ * hotbar (the game info position) as it is preferred to use
+ * {@link TitlePacket} due to MC-119145.
+ */
public class ChatMessagePacket implements ServerPacket {
+ private static final UUID NULL_UUID = new UUID(0, 0);
public String jsonMessage;
- public Position position;
+ public MessageType messageType;
public UUID uuid;
+ @Deprecated
public ChatMessagePacket(String jsonMessage, Position position, UUID uuid) {
- this.jsonMessage = jsonMessage;
- this.position = position;
- this.uuid = uuid;
+ this(jsonMessage, position.asMessageType(), uuid);
}
+ @Deprecated
public ChatMessagePacket(String jsonMessage, Position position) {
- this(jsonMessage, position, new UUID(0, 0));
+ this(jsonMessage, position, NULL_UUID);
+ }
+
+ /**
+ * Constructs a new chat message packet with a zeroed UUID. To send formatted
+ * messages please use the respective {@link Audience#sendMessage(Component)}
+ * functions.
+ *
+ * @param jsonMessage the raw message payload
+ * @param messageType the message type
+ */
+ public ChatMessagePacket(String jsonMessage, MessageType messageType) {
+ this(jsonMessage, messageType, NULL_UUID);
+ }
+
+ /**
+ * Constructs a new chat message packet. To send formatted messages please use the
+ * respective {@link Audience#sendMessage(Component)} functions.
+ *
+ * @param jsonMessage the raw message payload
+ * @param messageType the message type
+ * @param uuid the sender of the chat message
+ */
+ public ChatMessagePacket(String jsonMessage, MessageType messageType, UUID uuid) {
+ this.jsonMessage = jsonMessage;
+ this.messageType = messageType;
+ this.uuid = uuid;
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeSizedString(jsonMessage);
- writer.writeByte((byte) position.ordinal());
+ writer.writeByte((byte) (messageType == null ? 3 : messageType.ordinal()));
writer.writeUuid(uuid);
}
@@ -35,9 +71,29 @@ public class ChatMessagePacket implements ServerPacket {
return ServerPacketIdentifier.CHAT_MESSAGE;
}
+ /**
+ * @deprecated Use {@link MessageType}
+ */
+ @Deprecated
public enum Position {
- CHAT,
- SYSTEM_MESSAGE,
- GAME_INFO
+ CHAT(MessageType.CHAT),
+ SYSTEM_MESSAGE(MessageType.SYSTEM),
+ GAME_INFO(null);
+
+ private final MessageType messageType;
+
+ Position(MessageType messageType) {
+ this.messageType = messageType;
+ }
+
+ /**
+ * Gets this position as an Adventure message type. Note this will return
+ * {@code null} for {@link #GAME_INFO} as it is preferred to use
+ * {@link TitlePacket} due to MC-119145.
+ * @return the message type
+ */
+ public @Nullable MessageType asMessageType() {
+ return this.messageType;
+ }
}
}
diff --git a/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java
index 3ef3a6512..c3ef7bd56 100644
--- a/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java
+++ b/src/main/java/net/minestom/server/network/packet/server/play/DisconnectPacket.java
@@ -1,5 +1,6 @@
package net.minestom.server.network.packet.server.play;
+import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
@@ -7,16 +8,27 @@ import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class DisconnectPacket implements ServerPacket {
+ private String payload;
- public JsonMessage message; // Only text
+ /**
+ * Creates a new disconnect packet with a given string.
+ * @param payload the message
+ */
+ public DisconnectPacket(@NotNull String payload) {
+ this.payload = payload;
+ }
+ /**
+ * @deprecated Use {@link #DisconnectPacket(String)}
+ */
+ @Deprecated
public DisconnectPacket(@NotNull JsonMessage message){
- this.message = message;
+ this(message.toString());
}
@Override
public void write(@NotNull BinaryWriter writer) {
- writer.writeSizedString(message.toString());
+ writer.writeSizedString(payload);
}
@Override
diff --git a/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java
index 245437adf..021f98727 100644
--- a/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java
+++ b/src/main/java/net/minestom/server/network/packet/server/play/PlayerListHeaderAndFooterPacket.java
@@ -1,31 +1,26 @@
package net.minestom.server.network.packet.server.play;
-import net.minestom.server.chat.JsonMessage;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
+import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
public class PlayerListHeaderAndFooterPacket implements ServerPacket {
+ public String header;
+ public String footer;
- private static final String EMPTY_COMPONENT = "{\"translate\":\"\"}";
-
- public JsonMessage header; // Only text
- public JsonMessage footer; // Only text
+ public PlayerListHeaderAndFooterPacket(@NotNull String header, @NotNull String footer) {
+ Validate.notNull(header, "Header cannot be null.");
+ Validate.notNull(footer, "Footer cannot be null.");
+ this.header = header;
+ this.footer = footer;
+ }
@Override
public void write(@NotNull BinaryWriter writer) {
- if (header == null) {
- writer.writeSizedString(EMPTY_COMPONENT);
- } else {
- writer.writeSizedString(header.toString());
- }
-
- if (footer == null) {
- writer.writeSizedString(EMPTY_COMPONENT);
- } else {
- writer.writeSizedString(footer.toString());
- }
+ writer.writeSizedString(header);
+ writer.writeSizedString(footer);
}
@Override
diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java
index 2a54e9770..25f8717bc 100644
--- a/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java
+++ b/src/main/java/net/minestom/server/network/packet/server/play/TitlePacket.java
@@ -1,24 +1,64 @@
package net.minestom.server.network.packet.server.play;
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.adventure.title.Title;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
+import net.minestom.server.utils.TickUtils;
import net.minestom.server.utils.binary.BinaryWriter;
+import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static net.minestom.server.network.packet.server.play.TitlePacket.Action.*;
+
public class TitlePacket implements ServerPacket {
- public Action action;
+ private Action action;
- public JsonMessage titleText; // Only text
+ private String payload;
- public JsonMessage subtitleText; // Only text
+ private int fadeIn, stay, fadeOut;
- public JsonMessage actionBarText; // Only text
+ /**
+ * Constructs a new title packet from an action that can take a string argument.
+ * @param action the action
+ * @param payload the payload
+ * @throws IllegalArgumentException if the action is not {@link Action#SET_TITLE},
+ * {@link Action#SET_SUBTITLE} or {@link Action#SET_ACTION_BAR}
+ */
+ public TitlePacket(@NotNull Action action, @NotNull String payload) {
+ Validate.isTrue(action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR, "Invalid action type");
+ this.action = action;
+ this.payload = payload;
+ }
- public int fadeIn;
- public int stay;
- public int fadeOut;
+ /**
+ * Constructs a new title packet from a clear or reset action.
+ * @param action the action
+ * @throws IllegalArgumentException if the action is not {@link Action#RESET},
+ * or {@link Action#HIDE}
+ */
+ public TitlePacket(@NotNull Action action) {
+ this.action = action;
+ }
+
+ /**
+ * Constructs a new title packet for {@link Action#SET_TIMES_AND_DISPLAY}.
+ * @param fadeIn the fade in time
+ * @param stay the stay time
+ * @param fadeOut the fade out time
+ */
+ public TitlePacket(int fadeIn, int stay, int fadeOut) {
+ this.action = SET_TIMES_AND_DISPLAY;
+ this.fadeIn = fadeIn;
+ this.stay = stay;
+ this.fadeOut = fadeOut;
+ }
@Override
public void write(@NotNull BinaryWriter writer) {
@@ -26,13 +66,9 @@ public class TitlePacket implements ServerPacket {
switch (action) {
case SET_TITLE:
- writer.writeSizedString(titleText.toString());
- break;
case SET_SUBTITLE:
- writer.writeSizedString(subtitleText.toString());
- break;
case SET_ACTION_BAR:
- writer.writeSizedString(actionBarText.toString());
+ writer.writeSizedString(payload);
break;
case SET_TIMES_AND_DISPLAY:
writer.writeInt(fadeIn);
@@ -59,4 +95,25 @@ public class TitlePacket implements ServerPacket {
RESET
}
+ /**
+ * Creates a collection of title packets from an Adventure title.
+ * @param title the title
+ * @return the packets
+ */
+ public static Collection of(Title title) {
+ List packets = new ArrayList<>(4);
+
+ // base packets
+ packets.add(new TitlePacket(SET_TITLE, GsonComponentSerializer.gson().serialize(title.title())));
+ packets.add(new TitlePacket(SET_SUBTITLE, GsonComponentSerializer.gson().serialize(title.subtitle())));
+
+ // times packet
+ Title.Times times = title.times();
+ if (times != null) {
+ packets.add(new TitlePacket(TickUtils.fromDuration(times.fadeIn()), TickUtils.fromDuration(times.stay()),
+ TickUtils.fromDuration(times.fadeOut())));
+ }
+
+ return packets;
+ }
}
diff --git a/src/main/java/net/minestom/server/utils/TickUtils.java b/src/main/java/net/minestom/server/utils/TickUtils.java
new file mode 100644
index 000000000..6b6bfcc89
--- /dev/null
+++ b/src/main/java/net/minestom/server/utils/TickUtils.java
@@ -0,0 +1,25 @@
+package net.minestom.server.utils;
+
+import net.minestom.server.MinecraftServer;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.NotNull;
+
+import java.time.Duration;
+
+/**
+ * Tick related utilities.
+ */
+public class TickUtils {
+
+ /**
+ * Creates a number of ticks from a given duration, based on {@link MinecraftServer#TICK_MS}.
+ * @param duration the duration
+ * @return the number of ticks
+ * @throws IllegalArgumentException if duration is negative
+ */
+ public static int fromDuration(@NotNull Duration duration) {
+ Validate.isTrue(!duration.isNegative(), "Duration cannot be negative");
+
+ return (int) (duration.toMillis() / MinecraftServer.TICK_MS);
+ }
+}