Initial adventure implementation

- deprecate old text classes
- make CommandSender and Audience
- implement in ConsoleSender
- partially implement in Player
This commit is contained in:
Kieran Wallbanks 2021-03-01 15:25:23 +00:00
parent 7c63099963
commit 4f0944ba9f
17 changed files with 357 additions and 94 deletions

View File

@ -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 {

View File

@ -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;

View File

@ -18,6 +18,7 @@ import java.util.Map;
* <p>
* Immutable class.
*/
@Deprecated
public final class ChatColor {
// Special

View File

@ -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;

View File

@ -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 '§'

View File

@ -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 = '{';

View File

@ -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 <a href="https://wiki.vg/Chat">Chat Format</a>
*/
@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.
* <p>
@ -102,6 +113,7 @@ public abstract class JsonMessage {
return message;
}
@Deprecated
public static class RawJsonMessage extends JsonMessage {
private final JsonObject jsonObject;

View File

@ -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<RichComponent> components = new ArrayList<>();

View File

@ -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;

View File

@ -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;
* <p>
* 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());
}
/**

View File

@ -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<Permission> 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);

View File

@ -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.
*

View File

@ -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 <a href="https://bugs.mojang.com/browse/MC-119145">MC-119145</a>.
*/
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 <a href="https://bugs.mojang.com/browse/MC-119145">MC-119145</a>.
* @return the message type
*/
public @Nullable MessageType asMessageType() {
return this.messageType;
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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<TitlePacket> of(Title title) {
List<TitlePacket> 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;
}
}

View File

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